diff --git a/DEPS b/DEPS
index 2b58522..10b5b7f 100644
--- a/DEPS
+++ b/DEPS
@@ -199,11 +199,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'b03f4a1dc831202cc7a68945c752ec56c4415607',
+  'skia_revision': '0bad6cf1458da91d48233930d28a869f0d3104bf',
   # 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': 'f569a18017889537012ab0775ec21101f1b2f7c6',
+  'v8_revision': '609f77888b667dbb70edc38715437b69f15a4dc4',
   # 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.
@@ -211,7 +211,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '4ce2e2135123407e09e37eb2f3400fd263febecc',
+  'angle_revision': 'a408ce8349289c8fe457a5ebccb8c4cbac7b9c3f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -278,7 +278,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '4afb63517e64e10da3a3a98c01576e2733b62246',
+  'devtools_frontend_revision': '8ab2d7e59263642069235798b5a1e357f21fa479',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -910,7 +910,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ea89b0d66a4c18363296667a9f37afe75f1f53f2',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9bdbb8f944a7aa679381d87b64b3042daae9fc7b',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1114,7 +1114,7 @@
     Var('chromium_git') + '/external/libaddressinput.git' + '@' + '390dfc08e3806a2125d08d4d8d034a24d587d77a',
 
   'src/third_party/libaom/source/libaom':
-    Var('aomedia_git') + '/aom.git' + '@' +  '43927e4611e7c3062a67ebaca38a625faa9a39d6',
+    Var('aomedia_git') + '/aom.git' + '@' +  '61c6fda0fdc927830559597bbb3410a0000bdc9c',
 
   'src/third_party/libavif/src':
     Var('chromium_git') + '/external/github.com/AOMediaCodec/libavif.git' + '@' + Var('libavif_revision'),
@@ -1275,7 +1275,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '91cc5be54402afc43605c15f07548e53741e0430',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd8241a7a3e4d6e6a8dc6ec6e00d90ad7a93166eb',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1511,7 +1511,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '66460536ee975a3e98931b7b40a661a63fd9cd57',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b70c9531ee0c00d5d8aa104a3e5b76e6bc1412b1',
+    Var('webrtc_git') + '/src.git' + '@' + '54b925cfc2cd7c2f8f83e8e484f281aa87b92dfc',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1549,7 +1549,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/linux-amd64',
-          'version': 'opL5N0hiBtWxiOFaEhRqXpJWh70uurNTInvkY8NHr8oC',
+          'version': 'AyPgwgutb8buiGKwMyMGU2SGR5H8h1Y2XDfiIN2BtzwC',
         },
       ],
       'dep_type': 'cipd',
@@ -1559,7 +1559,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'GRI6-L-7YmnYGw8gs2jXZDqvI5KIFIgsj_GvzZ4krYQC',
+          'version': 'kET1R1m5NBszkTdWAJUnKHE6R3DkKz59FxiaENTn_QgC',
         },
       ],
       'dep_type': 'cipd',
@@ -1569,7 +1569,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'pmlVeVFuSc7Re0D7Efu2pAh2Oo5rEAnlg1rFIfp1RncC',
+          'version': 'tIfvKvCHGv4G8z3DzRxNQ7W7G95EP9N0R1wbJ_ezmwkC',
         },
       ],
       'dep_type': 'cipd',
@@ -1583,7 +1583,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@99666637f799ac21e532defe1d21f464f464bce0',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@de2f82a60468d9e05188fc465eb945cf144c8aed',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 49f74cf..d9478d54 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -658,21 +658,6 @@
                   'chrome/browser/ui/views/bubble/|'\
                   'components/bubble/',
     },
-    'cast': {
-      'filepath': 'media/cast/' \
-                  '|chrome/browser/extensions/api/cast_streaming/' \
-                  '|chrome/browser/media/cast/' \
-                  '|chrome/browser/media/router/providers/cast/' \
-                  '|chrome/browser/resources/cast/' \
-                  '|chrome/browser/ui/webui/cast/' \
-                  '|chrome/common/media_router/providers/cast/' \
-                  '|chrome/renderer/media/cast/' \
-                  '|chrome/test/data/extensions/api_test/cast/' \
-                  '|chrome/test/data/extensions/api_test/cast_streaming/' \
-                  '|components/mirroring/' \
-                  '|content/public/renderer/media_stream_' \
-                  '|content/renderer/media/(media_stream|(.+audio_source))',
-    },
     'cast_certificate': {
       'filepath': 'components/cast_certificate/'\
                   '|components/test/data/cast_certificate/',
@@ -682,6 +667,11 @@
                    '|components/cast_channel/' \
                    '|extensions/browser/api/cast_channel'
     },
+    'cast_streaming': {
+      'filepath': 'media/cast/' \
+                  '|chrome/browser/media/cast_mirroring' \
+                  '|components/mirroring/' \
+    },
     'cc': {
       'filepath': 'cc/'\
                   '|components/viz/'
@@ -1289,16 +1279,13 @@
                   '|media/remoting/',
     },
     'media_router': {
-      'filepath': 'chrome/android/features/media_router/' \
-                  '|chrome/app/media_router_strings.grdp' \
+      'filepath': 'chrome/app/media_router_strings.grdp' \
                   '|chrome/browser/media/router/' \
                   '|chrome/browser/resources/media_router/' \
                   '|chrome/browser/ui/media_router/' \
                   '|chrome/browser/ui/views/media_router/' \
-                  '|chrome/browser/ui/webui/media_router/' \
-                  '|chrome/common/media_router/' \
-                  '|chrome/test/data/webui/media_router/' \
-                  '|chrome/test/media_router/'
+                  '|chrome/test/media_router/' \
+                  '|components/media_router/'
     },
     'message_loop': {
       'filepath': 'base/message_'
@@ -1809,22 +1796,20 @@
         '|media/audio/audio_(output_controller|power_monitor)',
     },
     'tab_capture': {
+      # TODO: Watch code in media/audio & audio service for tab audio capture
       'filepath': 'chrome/browser/extensions/api/tab_capture/'\
         '|chrome/renderer/resources/extensions/tab_capture_custom_bindings.js'\
         '|chrome/test/data/extensions/api_test/tab_capture/'\
-        '|components/viz/common/(frame_sinks/copy_output_|gl_helper|yuv_)'\
-        '|components/viz/service/display/'\
+        '|components/viz/common/(frame_sinks/|gl_helper|gl_i420|yuv_)'\
+        '|components/viz/host/client_frame_sink'\
         '|components/viz/service/frame_sinks/(frame_sink_manager'\
             '|video_capture/)'\
         '|content/browser/media/capture/'\
-        '|content/browser/renderer_host/media/(audio_'\
-            '|media_stream_manager|video_)'\
-        '|content/public/renderer/media_stream_'\
-        '|content/renderer/media/(media_stream|(.+audio_source))'\
-        '|media/audio/(audio_output_controller|fake_audio_|virtual_audio_)'\
+        '|content/browser/renderer_host/media/'\
         '|media/base/video_frame\.h'\
         '|media/capture/'\
-        '|services/viz/privileged/mojom/compositing/frame_sink',
+        '|services/viz/privileged/mojom/compositing/frame_sink'\
+        '|third_party/blink/public/mojom/mediastream/'
     },
     'tab_contents': {
       'filepath': 'chrome/browser/tab_contents/|'\
@@ -2359,15 +2344,14 @@
     'browsing_data': ['dullweber+watch@chromium.org',
                       'msramek+watch@chromium.org'],
     'bubble': ['hcarmona+bubble@chromium.org'],
-    'cast': ['jasonroberts+watch@google.com',
-             'miu+watch@chromium.org',
-             'mfoltz+watch@chromium.org',
-             'pthatcher+watch@chromium.org'],
     'cast_certificate': ['dougsteed+watch@chromium.org',
                          'mfoltz+watch@chromium.org',
                          'ryanchung+watch@chromium.org'],
     'cast_channel': ['mfoltz+watch@chromium.org',
                      'ryanchung+watch@chromium.org'],
+    'cast_streaming': ['jasonroberts+watch@google.com',
+                       'jophba+watch@chromium.org',
+                       'mfoltz+watch@chromium.org'],
     'cc': ['cc-bugs@chromium.org'],
     'cc-animation': ['gerchiko@microsoft.com',
                      'majidvp@chromium.org'],
@@ -2620,10 +2604,9 @@
     'media_recorder': ['emircan+watch+mediarecorder@chromium.org',
                        'mcasas+mediarecorder@chromium.org'],
     'media_remoting': ['erickung+watch@chromium.org',
-                       'miu+watch@chromium.org',
-                       'pthatcher+watch@chromium.org'],
+                       'jophba+watch@chromium.org',
+                       'mfoltz+watch@chromium.org'],
     'media_router': ['mfoltz+watch@chromium.org',
-                     'pthatcher+watch@chromium.org',
                      'takumif+watch@chromium.org'],
     'message_loop': ['sadrul@chromium.org'],
     'metrics': ['asvitkine+watch@chromium.org',
@@ -2814,9 +2797,9 @@
     'syncfs': ['kinuko+fileapi@chromium.org'],
     'system_web_apps': [ 'ortuno@chromium.org',
                          'dominicschulz@google.com'],
-    'tab_alert_indicators': ['miu+watch@chromium.org'],
-    'tab_capture': ['miu+watch@chromium.org',
-                    'pthatcher+watch@chromium.org'],
+    'tab_alert_indicators': ['jophba+watch@chromium.org'],
+    'tab_capture': ['jophba+watch@chromium.org',
+                    'mfoltz+watch@chromium.org'],
     'tab_contents': ['ajwong+watch@chromium.org',
                      'avi@chromium.org',
                      'creis+watch@chromium.org'],
diff --git a/ash/accessibility/accessibility_controller_impl.h b/ash/accessibility/accessibility_controller_impl.h
index 2e74936a..c0815d0 100644
--- a/ash/accessibility/accessibility_controller_impl.h
+++ b/ash/accessibility/accessibility_controller_impl.h
@@ -492,7 +492,7 @@
   std::unique_ptr<ScopedBacklightsForcedOff> scoped_backlights_forced_off_;
 
   // True if ChromeVox should enable its volume slide gesture.
-  bool enable_chromevox_volume_slide_gesture_ = true;
+  bool enable_chromevox_volume_slide_gesture_ = false;
 
   base::ObserverList<AccessibilityObserver> observers_;
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index a463901..0357ee2e7 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -3164,6 +3164,18 @@
       <message name="IDS_ASH_ALT_TAB_CURRENT_DESK_MODE" desc="Alt-tab shows only windows from the current desk.">
         Current desk
       </message>
+      <message name="IDS_ASH_ALT_TAB_ALL_DESKS_MODE_SELECTED_TITLE" desc="Accessibility text read by ChromeVox to indicate that alt-tab is in the all-desks mode.">
+        Show windows from all desks, radio button selected
+      </message>
+      <message name="IDS_ASH_ALT_TAB_CURRENT_DESK_MODE_SELECTED_TITLE" desc="Accessibility text read by ChromeVox to indicate that alt-tab is in the current-desk mode.">
+        Show windows from the current desk, radio button selected
+      </message>
+      <message name="IDS_ASH_ALT_TAB_FOCUS_WINDOW_LIST_TITLE" desc="Accessibility text read by ChromeVox to suggest that the user can continue cycling the window list by pressing Down arrow key.">
+        Press the Down arrow key to switch windows
+      </message>
+      <message name="IDS_ASH_ALT_TAB_WINDOW_SELECTED_TITLE" desc="Accessibility text read by ChromeVox to inform that the window is selected during alt-tab mode switching.">
+        <ph name="WINDOW_TITLE">$1<ex>Chrome New tab</ex></ph> window selected
+      </message>
 
       <!-- Back gesture -->
       <message name="IDS_ASH_BACK_GESTURE_CONTEXTUAL_NUDGE" desc="Information shown in the suggestion label for the contextual nudge of the back gesture in tablet mode">
diff --git a/ash/ash_strings_grd/IDS_ASH_ALT_TAB_ALL_DESKS_MODE_SELECTED_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_ALL_DESKS_MODE_SELECTED_TITLE.png.sha1
new file mode 100644
index 0000000..36fb6e8
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_ALL_DESKS_MODE_SELECTED_TITLE.png.sha1
@@ -0,0 +1 @@
+0082ed1cec82b24b3407a9e03b6e77b700e85b16
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ALT_TAB_CURRENT_DESK_MODE_SELECTED_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_CURRENT_DESK_MODE_SELECTED_TITLE.png.sha1
new file mode 100644
index 0000000..98851b4
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_CURRENT_DESK_MODE_SELECTED_TITLE.png.sha1
@@ -0,0 +1 @@
+07abc27ff5e08e255931929d4e6b66372d3fa35a
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ALT_TAB_FOCUS_WINDOW_LIST_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_FOCUS_WINDOW_LIST_TITLE.png.sha1
new file mode 100644
index 0000000..c02b2c0
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_FOCUS_WINDOW_LIST_TITLE.png.sha1
@@ -0,0 +1 @@
+93bd96bbc448169b18043345ee9e4b321672fc1f
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ALT_TAB_WINDOW_SELECTED_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_WINDOW_SELECTED_TITLE.png.sha1
new file mode 100644
index 0000000..bd42554
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_WINDOW_SELECTED_TITLE.png.sha1
@@ -0,0 +1 @@
+1237699bbd415b25f2b77e3ed4e1581d42bee562
\ No newline at end of file
diff --git a/ash/clipboard/clipboard_history_controller_impl.cc b/ash/clipboard/clipboard_history_controller_impl.cc
index 76cd559..09d936a6 100644
--- a/ash/clipboard/clipboard_history_controller_impl.cc
+++ b/ash/clipboard/clipboard_history_controller_impl.cc
@@ -375,6 +375,9 @@
 
 bool ClipboardHistoryControllerImpl::DeleteClipboardItemByClipboardData(
     ui::ClipboardData* data) {
+  if (!history() || !data)
+    return false;
+
   for (const auto& item : history()->GetItems()) {
     if (item.data() == *data) {
       DeleteClipboardHistoryItem(item);
diff --git a/ash/public/cpp/holding_space/holding_space_item.cc b/ash/public/cpp/holding_space/holding_space_item.cc
index 763664a..5691a3b 100644
--- a/ash/public/cpp/holding_space/holding_space_item.cc
+++ b/ash/public/cpp/holding_space/holding_space_item.cc
@@ -30,7 +30,9 @@
 
 }  // namespace
 
-HoldingSpaceItem::~HoldingSpaceItem() = default;
+HoldingSpaceItem::~HoldingSpaceItem() {
+  deletion_callback_list_.Notify();
+}
 
 bool HoldingSpaceItem::operator==(const HoldingSpaceItem& rhs) const {
   return type_ == rhs.type_ && id_ == rhs.id_ && file_path_ == rhs.file_path_ &&
@@ -113,6 +115,11 @@
   return dict;
 }
 
+base::CallbackListSubscription HoldingSpaceItem::AddDeletionCallback(
+    base::RepeatingClosureList::CallbackType callback) const {
+  return deletion_callback_list_.Add(std::move(callback));
+}
+
 bool HoldingSpaceItem::IsFinalized() const {
   return !file_system_url_.is_empty();
 }
diff --git a/ash/public/cpp/holding_space/holding_space_item.h b/ash/public/cpp/holding_space/holding_space_item.h
index 656a1d9..99f3772 100644
--- a/ash/public/cpp/holding_space/holding_space_item.h
+++ b/ash/public/cpp/holding_space/holding_space_item.h
@@ -10,6 +10,7 @@
 
 #include "ash/public/cpp/ash_public_export.h"
 #include "base/callback_forward.h"
+#include "base/callback_list.h"
 #include "base/files/file_path.h"
 #include "base/strings/string16.h"
 #include "url/gurl.h"
@@ -73,6 +74,10 @@
   // Serializes from `HoldingSpaceItem` to `base::DictionaryValue`.
   base::DictionaryValue Serialize() const;
 
+  // Adds `callback` to be notified when `this` gets deleted.
+  base::CallbackListSubscription AddDeletionCallback(
+      base::RepeatingClosureList::CallbackType callback) const;
+
   // Indicates whether the item has been finalized. This will be false for items
   // created using `Deserialize()` for which `Finalize()` has not yet been
   // called.
@@ -129,6 +134,9 @@
 
   // The image representation of the item.
   std::unique_ptr<HoldingSpaceImage> image_;
+
+  // Mutable to allow const access from `AddDeletionCallback()`.
+  mutable base::RepeatingClosureList deletion_callback_list_;
 };
 
 }  // namespace ash
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc
index 1e2e0b9..663f039 100644
--- a/ash/system/holding_space/holding_space_tray.cc
+++ b/ash/system/holding_space/holding_space_tray.cc
@@ -104,6 +104,21 @@
     previews_tray_icon_ = tray_container()->AddChildView(
         std::make_unique<HoldingSpaceTrayIcon>(shelf));
     previews_tray_icon_->SetVisible(false);
+
+    // Enable context menu, which supports an action to toggle item previews.
+    set_context_menu_controller(this);
+  }
+
+  set_use_bounce_in_animation(true);
+}
+
+HoldingSpaceTray::~HoldingSpaceTray() = default;
+
+void HoldingSpaceTray::Initialize() {
+  TrayBackgroundView::Initialize();
+
+  if (features::IsTemporaryHoldingSpacePreviewsEnabled()) {
+    DCHECK(previews_tray_icon_);
     UpdatePreviewsVisibility();
 
     // If previews feature is enabled, the preview icon is displayed
@@ -111,9 +126,6 @@
     auto* prefs = Shell::Get()->session_controller()->GetActivePrefService();
     if (prefs)
       ObservePrefService(prefs);
-
-    // Enable context menu, which supports an action to toggle item previews.
-    set_context_menu_controller(this);
   }
 
   // It's possible that this holding space tray was created after login, such as
@@ -121,12 +133,8 @@
   // the holding space model will already have been attached.
   if (HoldingSpaceController::Get()->model())
     OnHoldingSpaceModelAttached(HoldingSpaceController::Get()->model());
-
-  set_use_bounce_in_animation(true);
 }
 
-HoldingSpaceTray::~HoldingSpaceTray() = default;
-
 void HoldingSpaceTray::ClickedOutsideBubble() {
   CloseBubble();
 }
diff --git a/ash/system/holding_space/holding_space_tray.h b/ash/system/holding_space/holding_space_tray.h
index b9b8e39..34aba0c 100644
--- a/ash/system/holding_space/holding_space_tray.h
+++ b/ash/system/holding_space/holding_space_tray.h
@@ -56,6 +56,7 @@
   ~HoldingSpaceTray() override;
 
   // TrayBackgroundView:
+  void Initialize() override;
   void ClickedOutsideBubble() override;
   base::string16 GetAccessibleNameForTray() override;
   views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
diff --git a/ash/system/holding_space/holding_space_tray_icon_preview.cc b/ash/system/holding_space/holding_space_tray_icon_preview.cc
index 1b3a6c4..b3b6471 100644
--- a/ash/system/holding_space/holding_space_tray_icon_preview.cc
+++ b/ash/system/holding_space/holding_space_tray_icon_preview.cc
@@ -141,6 +141,9 @@
   contents_image_ = gfx::ImageSkia(
       std::make_unique<ContentsImageSource>(item->image().GetImageSkia(size)),
       size);
+  item_deletion_subscription_ = item->AddDeletionCallback(base::BindRepeating(
+      &HoldingSpaceTrayIconPreview::OnHoldingSpaceItemDeleted,
+      base::Unretained(this)));
   image_subscription_ =
       item->image().AddImageSkiaChangedCallback(base::BindRepeating(
           &HoldingSpaceTrayIconPreview::OnHoldingSpaceItemImageChanged,
@@ -439,12 +442,21 @@
 
 void HoldingSpaceTrayIconPreview::OnHoldingSpaceItemImageChanged() {
   const gfx::Size size(GetPreviewSize());
-  contents_image_ = gfx::ImageSkia(
-      std::make_unique<ContentsImageSource>(item_->image().GetImageSkia(size)),
-      size);
+  if (item_) {
+    contents_image_ = gfx::ImageSkia(std::make_unique<ContentsImageSource>(
+                                         item_->image().GetImageSkia(size)),
+                                     size);
+  } else {
+    contents_image_ = gfx::ImageSkia();
+  }
+
   InvalidateLayer();
 }
 
+void HoldingSpaceTrayIconPreview::OnHoldingSpaceItemDeleted() {
+  item_ = nullptr;
+}
+
 void HoldingSpaceTrayIconPreview::CreateLayer(
     const gfx::Transform& initial_transform) {
   DCHECK(!layer());
diff --git a/ash/system/holding_space/holding_space_tray_icon_preview.h b/ash/system/holding_space/holding_space_tray_icon_preview.h
index f6669008..88806248 100644
--- a/ash/system/holding_space/holding_space_tray_icon_preview.h
+++ b/ash/system/holding_space/holding_space_tray_icon_preview.h
@@ -99,6 +99,9 @@
   // representation gets updated.
   void OnHoldingSpaceItemImageChanged();
 
+  // Subscription callback for `item_` deletion.
+  void OnHoldingSpaceItemDeleted();
+
   // Creates a layer for this preview. The layer will be owned by
   // `layer_owner_`. Note that a layer may be created multiple times throughout
   // this preview's lifetime as the preview will only have a layer while in the
@@ -133,7 +136,8 @@
   // icon.
   views::View* const container_;
 
-  // The holding space item this preview represents.
+  // The holding space item this preview represents - may be null if the item
+  // gets deleted before the preview.
   const HoldingSpaceItem* item_;
 
   // Whether or not this preview is currently using small dimensions. This is
@@ -173,6 +177,9 @@
   // `contents_image_`.
   base::CallbackListSubscription image_subscription_;
 
+  // Subscription for the associated holding space item deletion.
+  base::CallbackListSubscription item_deletion_subscription_;
+
   // The `layer()` for this preview is parented by `container_`'s layer. It is
   // necessary to observe and react to bounds changes in `container_` to keep
   // `layer()`'s bounds in sync.
diff --git a/ash/system/holding_space/holding_space_tray_unittest.cc b/ash/system/holding_space/holding_space_tray_unittest.cc
index 399f638..48dbab96 100644
--- a/ash/system/holding_space/holding_space_tray_unittest.cc
+++ b/ash/system/holding_space/holding_space_tray_unittest.cc
@@ -22,7 +22,9 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/system/holding_space/holding_space_item_view.h"
+#include "ash/system/tray/tray_constants.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
 #include "base/files/file_path.h"
 #include "base/strings/strcat.h"
 #include "base/test/bind.h"
@@ -513,6 +515,95 @@
   EXPECT_FALSE(test_api()->IsShowingInShelf());
 }
 
+// Tests that the tray icon size changes on in-app shelf.
+TEST_P(HoldingSpaceTrayTest, UpdateTrayIconSizeForInAppShelf) {
+  MarkTimeOfFirstPin();
+  StartSession();
+
+  // The tray button should be hidden if the user has previously pinned an item,
+  // and the holding space is empty.
+  EXPECT_FALSE(test_api()->IsShowingInShelf());
+
+  // Add a download item - the button should be shown.
+  AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_1"));
+  GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
+
+  EXPECT_TRUE(test_api()->IsShowingInShelf());
+  if (IsPreviewsFeatureEnabled()) {
+    ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
+    EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
+              test_api()->GetPreviewsTrayIcon()->size());
+  } else {
+    ASSERT_TRUE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
+    EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
+              test_api()->GetDefaultTrayIcon()->size());
+  }
+
+  TabletModeControllerTestApi().EnterTabletMode();
+
+  // Create a test widget to force in-app shelf.
+  std::unique_ptr<views::Widget> widget = CreateTestWidget();
+  ASSERT_TRUE(widget);
+
+  EXPECT_TRUE(test_api()->IsShowingInShelf());
+  if (IsPreviewsFeatureEnabled()) {
+    ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
+    EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconSmallPreviewSize, kTrayItemSize),
+              test_api()->GetPreviewsTrayIcon()->size());
+  } else {
+    ASSERT_TRUE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
+    EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
+              test_api()->GetDefaultTrayIcon()->size());
+  }
+
+  // Transition to home screen.
+  widget->Minimize();
+
+  if (IsPreviewsFeatureEnabled()) {
+    ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
+    EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
+              test_api()->GetPreviewsTrayIcon()->size());
+  } else {
+    ASSERT_TRUE(IsViewVisible(test_api()->GetDefaultTrayIcon()));
+    EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
+              test_api()->GetDefaultTrayIcon()->size());
+  }
+}
+
+// Tests that a shelf config change just after an item has been removed does
+// not cause a crash.
+TEST_P(HoldingSpaceTrayTest, ShelfConfigChangeWithDelayedItemRemoval) {
+  MarkTimeOfFirstPin();
+  StartSession();
+
+  // Create a test widget to force in-app shelf in tablet mode.
+  std::unique_ptr<views::Widget> widget = CreateTestWidget();
+  ASSERT_TRUE(widget);
+
+  // The tray button should be hidden if the user has previously pinned an item,
+  // and the holding space is empty.
+  EXPECT_FALSE(test_api()->IsShowingInShelf());
+
+  HoldingSpaceItem* item_1 =
+      AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_1"));
+  HoldingSpaceItem* item_2 =
+      AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/fake_2"));
+  GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
+
+  EXPECT_TRUE(test_api()->IsShowingInShelf());
+
+  model()->RemoveItem(item_1->id());
+  TabletModeControllerTestApi().EnterTabletMode();
+  GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
+
+  EXPECT_TRUE(test_api()->IsShowingInShelf());
+
+  model()->RemoveItem(item_2->id());
+  TabletModeControllerTestApi().LeaveTabletMode();
+  GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
+  EXPECT_FALSE(test_api()->IsShowingInShelf());
+}
+
 // Tests how download chips are updated during item addition, removal and
 // finalization.
 TEST_P(HoldingSpaceTrayTest, DownloadsSection) {
diff --git a/ash/system/network/network_icon.cc b/ash/system/network/network_icon.cc
index 00b6b2a..a07c9a4b 100644
--- a/ash/system/network/network_icon.cc
+++ b/ash/system/network/network_icon.cc
@@ -567,10 +567,8 @@
     }
     if (activation_state == ActivationStateType::kNotActivated ||
         activation_state == ActivationStateType::kPartiallyActivated) {
-      if (base::FeatureList::IsEnabled(
-              chromeos::features::kUpdatedCellularActivationUi)) {
+      if (chromeos::features::IsCellularActivationUiEnabled())
         return base::UTF8ToUTF16(network->name);
-      }
 
       return l10n_util::GetStringFUTF16(
           IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATE,
diff --git a/ash/system/network/network_list_view.cc b/ash/system/network/network_list_view.cc
index a53ef02..497e20c 100644
--- a/ash/system/network/network_list_view.cc
+++ b/ash/system/network/network_list_view.cc
@@ -83,8 +83,7 @@
 bool ShouldShowActivateCellularNetwork(const NetworkInfo& info) {
   return NetworkTypeMatchesType(info.type, NetworkType::kCellular) &&
          info.activation_state == ActivationStateType::kNotActivated &&
-         base::FeatureList::IsEnabled(
-             chromeos::features::kUpdatedCellularActivationUi);
+         chromeos::features::IsCellularActivationUiEnabled();
 }
 
 }  // namespace
@@ -487,8 +486,7 @@
     }
     case NetworkType::kCellular:
       if (info.activation_state == ActivationStateType::kNotActivated &&
-          base::FeatureList::IsEnabled(
-              chromeos::features::kUpdatedCellularActivationUi)) {
+          chromeos::features::IsCellularActivationUiEnabled()) {
         return l10n_util::GetStringUTF16(
             IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CLICK_TO_ACTIVATE);
       }
diff --git a/ash/system/network/network_section_header_view.cc b/ash/system/network/network_section_header_view.cc
index 1d38b8e..b0c77f1 100644
--- a/ash/system/network/network_section_header_view.cc
+++ b/ash/system/network/network_section_header_view.cc
@@ -276,10 +276,8 @@
 }
 
 void MobileSectionHeaderView::AddExtraButtons(bool enabled) {
-  if (!base::FeatureList::IsEnabled(
-          chromeos::features::kUpdatedCellularActivationUi)) {
+  if (!chromeos::features::IsCellularActivationUiEnabled())
     return;
-  }
 
   TopShortcutButton* add_cellular_button = new TopShortcutButton(
       base::BindRepeating(&MobileSectionHeaderView::AddCellularButtonPressed,
diff --git a/ash/system/system_notification_controller.cc b/ash/system/system_notification_controller.cc
index 535cd0a..243fefc7 100644
--- a/ash/system/system_notification_controller.cc
+++ b/ash/system/system_notification_controller.cc
@@ -15,7 +15,6 @@
 #include "ash/system/session/session_limit_notification_controller.h"
 #include "ash/system/tracing_notification_controller.h"
 #include "ash/system/update/update_notification_controller.h"
-#include "base/feature_list.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "ui/message_center/message_center.h"
 
@@ -23,8 +22,7 @@
 namespace {
 
 std::unique_ptr<ash::CellularSetupNotifier> CreateCellularSetupNotifier() {
-  return base::FeatureList::IsEnabled(
-             chromeos::features::kUpdatedCellularActivationUi)
+  return chromeos::features::IsCellularActivationUiEnabled()
              ? std::make_unique<ash::CellularSetupNotifier>()
              : nullptr;
 }
diff --git a/base/allocator/allocator.gni b/base/allocator/allocator.gni
index d15ff97..79baecb 100644
--- a/base/allocator/allocator.gni
+++ b/base/allocator/allocator.gni
@@ -11,8 +11,10 @@
 # - Windows: shims don't work for component builds and debug CRT is not
 # compatible, see below.
 # - Android: symbol wrapping is not universal for component builds.
-_disable_partition_alloc = (is_win && (is_component_build || is_debug)) ||
-                           (is_android && is_component_build)
+# - Chromecast on Android: causes issues with crash reporting, see b/178423326.
+_disable_partition_alloc =
+    (is_win && (is_component_build || is_debug)) ||
+    (is_android && is_component_build) || (is_android && is_chromecast)
 
 # The debug CRT on Windows has some debug features that are incompatible with
 # the shim. NaCl in particular does seem to link some binaries statically
diff --git a/base/bind.h b/base/bind.h
index ff1a6d4..7b2361ce 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -380,7 +380,7 @@
 // Without the Unretained() wrapper on |&foo|, the above call would fail
 // to compile because Foo does not support the AddRef() and Release() methods.
 template <typename T>
-static inline internal::UnretainedWrapper<T> Unretained(T* o) {
+inline internal::UnretainedWrapper<T> Unretained(T* o) {
   return internal::UnretainedWrapper<T>(o);
 }
 
@@ -400,11 +400,11 @@
 //
 //    OnceClosure callback = BindOnce(&foo, bytes); // ERROR!
 template <typename T>
-static inline internal::RetainedRefWrapper<T> RetainedRef(T* o) {
+inline internal::RetainedRefWrapper<T> RetainedRef(T* o) {
   return internal::RetainedRefWrapper<T>(o);
 }
 template <typename T>
-static inline internal::RetainedRefWrapper<T> RetainedRef(scoped_refptr<T> o) {
+inline internal::RetainedRefWrapper<T> RetainedRef(scoped_refptr<T> o) {
   return internal::RetainedRefWrapper<T>(std::move(o));
 }
 
@@ -429,12 +429,12 @@
 // Without Owned(), someone would have to know to delete |pn| when the last
 // reference to the callback is deleted.
 template <typename T>
-static inline internal::OwnedWrapper<T> Owned(T* o) {
+inline internal::OwnedWrapper<T> Owned(T* o) {
   return internal::OwnedWrapper<T>(o);
 }
 
 template <typename T, typename Deleter>
-static inline internal::OwnedWrapper<T, Deleter> Owned(
+inline internal::OwnedWrapper<T, Deleter> Owned(
     std::unique_ptr<T, Deleter>&& ptr) {
   return internal::OwnedWrapper<T, Deleter>(std::move(ptr));
 }
@@ -482,11 +482,11 @@
 // via use of enable_if, and the second takes a T* which will not bind to T&.
 template <typename T,
           std::enable_if_t<!std::is_lvalue_reference<T>::value>* = nullptr>
-static inline internal::PassedWrapper<T> Passed(T&& scoper) {
+inline internal::PassedWrapper<T> Passed(T&& scoper) {
   return internal::PassedWrapper<T>(std::move(scoper));
 }
 template <typename T>
-static inline internal::PassedWrapper<T> Passed(T* scoper) {
+inline internal::PassedWrapper<T> Passed(T* scoper) {
   return internal::PassedWrapper<T>(std::move(*scoper));
 }
 
@@ -506,7 +506,7 @@
 //   // Prints "2" on |ml|.
 //   ml->PostTask(FROM_HERE, BindOnce(IgnoreResult(&DoSomething), 2);
 template <typename T>
-static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
+inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
   return internal::IgnoreResultHelper<T>(std::move(data));
 }
 
diff --git a/base/containers/util.h b/base/containers/util.h
index 435db0d1..555d79b 100644
--- a/base/containers/util.h
+++ b/base/containers/util.h
@@ -12,7 +12,7 @@
 // TODO(crbug.com/817982): What we really need is for checked_math.h to be
 // able to do checked arithmetic on pointers.
 template <typename T>
-static inline uintptr_t get_uintptr(const T* t) {
+inline uintptr_t get_uintptr(const T* t) {
   return reinterpret_cast<uintptr_t>(t);
 }
 
diff --git a/base/posix/file_descriptor_shuffle.h b/base/posix/file_descriptor_shuffle.h
index 2b47047..0457c72 100644
--- a/base/posix/file_descriptor_shuffle.h
+++ b/base/posix/file_descriptor_shuffle.h
@@ -77,7 +77,7 @@
     InjectionDelegate* delegate);
 
 // This function will not call malloc but will mutate |map|
-static inline bool ShuffleFileDescriptors(InjectiveMultimap* map) {
+inline bool ShuffleFileDescriptors(InjectiveMultimap* map) {
   FileDescriptorTableInjection delegate;
   return PerformInjectiveMultimapDestructive(map, &delegate);
 }
diff --git a/base/strings/string_util_internal.h b/base/strings/string_util_internal.h
index 7229524c..4d2e25a 100644
--- a/base/strings/string_util_internal.h
+++ b/base/strings/string_util_internal.h
@@ -228,7 +228,7 @@
 }
 
 template <bool (*Validator)(uint32_t)>
-inline static bool DoIsStringUTF8(StringPiece str) {
+inline bool DoIsStringUTF8(StringPiece str) {
   const char* src = str.data();
   int32_t src_len = static_cast<int32_t>(str.length());
   int32_t char_index = 0;
@@ -258,8 +258,8 @@
 // string piece gives additional flexibility for the caller (doesn't have to be
 // null terminated) so we choose the StringPiece route.
 template <typename Str>
-static inline bool DoLowerCaseEqualsASCII(BasicStringPiece<Str> str,
-                                          StringPiece lowercase_ascii) {
+inline bool DoLowerCaseEqualsASCII(BasicStringPiece<Str> str,
+                                   StringPiece lowercase_ascii) {
   return std::equal(
       str.begin(), str.end(), lowercase_ascii.begin(), lowercase_ascii.end(),
       [](auto lhs, auto rhs) { return ToLowerASCII(lhs) == rhs; });
diff --git a/base/task/thread_pool/worker_thread.cc b/base/task/thread_pool/worker_thread.cc
index 105c89f..dd526c86 100644
--- a/base/task/thread_pool/worker_thread.cc
+++ b/base/task/thread_pool/worker_thread.cc
@@ -221,73 +221,53 @@
 
 NOINLINE void WorkerThread::RunPooledWorker() {
   RunWorker();
-  // Inhibit tail calls of RunWorker and inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 
 NOINLINE void WorkerThread::RunBackgroundPooledWorker() {
   RunWorker();
-  // Inhibit tail calls of RunWorker and inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 
 NOINLINE void WorkerThread::RunSharedWorker() {
   RunWorker();
-  // Inhibit tail calls of RunWorker and inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 
 NOINLINE void WorkerThread::RunBackgroundSharedWorker() {
   RunWorker();
-  // Inhibit tail calls of RunWorker and inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 
 NOINLINE void WorkerThread::RunDedicatedWorker() {
   RunWorker();
-  // Inhibit tail calls of RunWorker and inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 
 NOINLINE void WorkerThread::RunBackgroundDedicatedWorker() {
   RunWorker();
-  // Inhibit tail calls of RunWorker and inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 
 #if defined(OS_WIN)
 NOINLINE void WorkerThread::RunSharedCOMWorker() {
   RunWorker();
-  // Inhibit tail calls of RunWorker and inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 
 NOINLINE void WorkerThread::RunBackgroundSharedCOMWorker() {
   RunWorker();
-  // Inhibit tail calls of RunWorker and inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 
 NOINLINE void WorkerThread::RunDedicatedCOMWorker() {
   RunWorker();
-  // Inhibit tail calls of RunWorker and inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 
 NOINLINE void WorkerThread::RunBackgroundDedicatedCOMWorker() {
   RunWorker();
-  // Inhibit tail calls of RunWorker and inhibit code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
 }
 #endif  // defined(OS_WIN)
 
diff --git a/base/task/thread_pool/worker_thread.h b/base/task/thread_pool/worker_thread.h
index 819718d..09ba022 100644
--- a/base/task/thread_pool/worker_thread.h
+++ b/base/task/thread_pool/worker_thread.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/base_export.h"
+#include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/atomic_flag.h"
 #include "base/synchronization/waitable_event.h"
@@ -194,7 +195,7 @@
   //     ThreadMain() -> RunLabeledWorker() -> RunWorker().
   // "RunLabeledWorker()" is a dummy frame based on ThreadLabel+ThreadPriority
   // and used to easily identify threads in stack traces.
-  void RunWorker();
+  void NOT_TAIL_CALLED RunWorker();
 
   // Self-reference to prevent destruction of |this| while the thread is alive.
   // Set in Start() before creating the thread. Reset in ThreadMain() before the
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java b/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java
index 17425613..ce2ab448 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/Batch.java
@@ -29,6 +29,14 @@
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Batch {
+    /**
+     * This annotation can be added in addition to @Batch to split batches based on @Features
+     * annotation. This will ensure that native features are configured correctly.
+     */
+    @Target(ElementType.TYPE)
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface SplitByFeature {}
+
     public String value();
 
     /**
diff --git a/base/test/trace_event_analyzer.h b/base/test/trace_event_analyzer.h
index 25a5a53..fbc89df 100644
--- a/base/test/trace_event_analyzer.h
+++ b/base/test/trace_event_analyzer.h
@@ -832,8 +832,7 @@
                     size_t end_position);
 
 // Count all matches.
-static inline size_t CountMatches(const TraceEventVector& events,
-                                  const Query& query) {
+inline size_t CountMatches(const TraceEventVector& events, const Query& query) {
   return CountMatches(events, query, 0u, events.size());
 }
 
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h
index a0cd28cf..49ea036 100644
--- a/base/trace_event/trace_event.h
+++ b/base/trace_event/trace_event.h
@@ -583,7 +583,7 @@
 // the arg_values must live throughout these procedures.
 
 template <class ARG1_TYPE>
-static inline base::trace_event::TraceEventHandle
+inline base::trace_event::TraceEventHandle
 AddTraceEventWithThreadIdAndTimestamp(
     char phase,
     const unsigned char* category_group_enabled,
@@ -604,7 +604,7 @@
 }
 
 template <class ARG1_TYPE, class ARG2_TYPE>
-static inline base::trace_event::TraceEventHandle
+inline base::trace_event::TraceEventHandle
 AddTraceEventWithThreadIdAndTimestamp(
     char phase,
     const unsigned char* category_group_enabled,
@@ -627,7 +627,7 @@
       timestamp, &args, flags);
 }
 
-static inline base::trace_event::TraceEventHandle
+inline base::trace_event::TraceEventHandle
 AddTraceEventWithThreadIdAndTimestamp(
     char phase,
     const unsigned char* category_group_enabled,
@@ -643,7 +643,7 @@
       timestamp, nullptr, flags);
 }
 
-static inline base::trace_event::TraceEventHandle AddTraceEvent(
+inline base::trace_event::TraceEventHandle AddTraceEvent(
     char phase,
     const unsigned char* category_group_enabled,
     const char* name,
@@ -659,7 +659,7 @@
 }
 
 template <class ARG1_TYPE>
-static inline base::trace_event::TraceEventHandle AddTraceEvent(
+inline base::trace_event::TraceEventHandle AddTraceEvent(
     char phase,
     const unsigned char* category_group_enabled,
     const char* name,
@@ -677,7 +677,7 @@
 }
 
 template <class ARG1_TYPE, class ARG2_TYPE>
-static inline base::trace_event::TraceEventHandle AddTraceEvent(
+inline base::trace_event::TraceEventHandle AddTraceEvent(
     char phase,
     const unsigned char* category_group_enabled,
     const char* name,
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 69bce58..8bd5a00 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -480,11 +480,23 @@
     batched_tests = dict()
     other_tests = []
     for test in tests:
-      if 'Batch' in test['annotations'] and 'RequiresRestart' not in test[
-          'annotations']:
-        batch_name = test['annotations']['Batch']['value']
+      annotations = test['annotations']
+      if 'Batch' in annotations and 'RequiresRestart' not in annotations:
+        batch_name = annotations['Batch']['value']
         if not batch_name:
           batch_name = test['class']
+
+        # Feature flags won't work in instrumentation tests unless the activity
+        # is restarted.
+        # Tests with identical features are grouped to minimize restarts.
+        if 'Batch$SplitByFeature' in annotations:
+          if 'Features$EnableFeatures' in annotations:
+            batch_name += '|enabled:' + ','.join(
+                sorted(annotations['Features$EnableFeatures']['value']))
+          if 'Features$DisableFeatures' in annotations:
+            batch_name += '|disabled:' + ','.join(
+                sorted(annotations['Features$DisableFeatures']['value']))
+
         if not batch_name in batched_tests:
           batched_tests[batch_name] = []
         batched_tests[batch_name].append(test)
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 8c8f34b..9c44e43e 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -192,6 +192,7 @@
     "metrics/latency_ukm_reporter.h",
     "metrics/lcd_text_metrics_reporter.cc",
     "metrics/lcd_text_metrics_reporter.h",
+    "metrics/shared_metrics_buffer.h",
     "metrics/throughput_ukm_reporter.cc",
     "metrics/throughput_ukm_reporter.h",
     "metrics/total_frame_counter.cc",
diff --git a/cc/metrics/OWNERS b/cc/metrics/OWNERS
index 0cb4f83e..39db757 100644
--- a/cc/metrics/OWNERS
+++ b/cc/metrics/OWNERS
@@ -1,2 +1,4 @@
 per-file ukm_smoothness_data.h=set noparent
 per-file ukm_smoothness_data.h=file://ipc/SECURITY_OWNERS
+per-file shared_metrics_buffer.h=set noparent
+per-file shared_metrics_buffer.h=file://ipc/SECURITY_OWNERS
diff --git a/cc/metrics/dropped_frame_counter.cc b/cc/metrics/dropped_frame_counter.cc
index 818de70..18e2925c 100644
--- a/cc/metrics/dropped_frame_counter.cc
+++ b/cc/metrics/dropped_frame_counter.cc
@@ -163,12 +163,7 @@
         static_cast<double>(total_smoothness_dropped_) * 100 / total_frames;
     smoothness_data.worst_smoothness = sliding_window_max_percent_dropped_;
     smoothness_data.percentile_95 = sliding_window_95pct_percent_dropped;
-
-    ukm_smoothness_data_->seq_lock.WriteBegin();
-    device::OneWriterSeqLock::AtomicWriterMemcpy(&ukm_smoothness_data_->data,
-                                                 &smoothness_data,
-                                                 sizeof(UkmSmoothnessData));
-    ukm_smoothness_data_->seq_lock.WriteEnd();
+    ukm_smoothness_data_->Write(smoothness_data);
   }
 }
 
diff --git a/cc/metrics/dropped_frame_counter.h b/cc/metrics/dropped_frame_counter.h
index 153d0ff..72bc46d 100644
--- a/cc/metrics/dropped_frame_counter.h
+++ b/cc/metrics/dropped_frame_counter.h
@@ -12,10 +12,10 @@
 #include "base/containers/ring_buffer.h"
 #include "cc/cc_export.h"
 #include "cc/metrics/frame_sorter.h"
+#include "cc/metrics/ukm_smoothness_data.h"
 
 namespace cc {
 class TotalFrameCounter;
-struct UkmSmoothnessDataShared;
 
 // This class maintains a counter for produced/dropped frames, and can be used
 // to estimate the recent throughput.
diff --git a/cc/metrics/frame_sequence_metrics.cc b/cc/metrics/frame_sequence_metrics.cc
index d5c2bea..1d4e00a 100644
--- a/cc/metrics/frame_sequence_metrics.cc
+++ b/cc/metrics/frame_sequence_metrics.cc
@@ -228,6 +228,7 @@
 
     main_throughput_ = {};
     impl_throughput_ = {};
+    jank_reporter_->Reset();
     frames_checkerboarded_ = 0;
     return;
   }
diff --git a/cc/metrics/frame_sequence_tracker_unittest.cc b/cc/metrics/frame_sequence_tracker_unittest.cc
index 3fccb65..908b0ed 100644
--- a/cc/metrics/frame_sequence_tracker_unittest.cc
+++ b/cc/metrics/frame_sequence_tracker_unittest.cc
@@ -390,11 +390,6 @@
   collection_.NotifyFramePresented(
       frame_token,
       /*feedback=*/{args_timestamp, zero_interval, 0});
-  ImplThroughput().frames_expected = 100u;
-  ReportMetrics();
-  histogram_tester.ExpectTotalCount(histogram_name, 1u);
-  EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name),
-              testing::ElementsAre(base::Bucket(0, 1)));
 
   // Frame 3: There is one jank (frame interval incremented from 16.67ms
   // to 30.0ms)
@@ -410,13 +405,6 @@
       frame_token,
       /*feedback=*/{args_timestamp, zero_interval, 0});
 
-  ImplThroughput().frames_expected = 100u;
-  ReportMetrics();
-  histogram_tester.ExpectTotalCount(
-      "Graphics.Smoothness.Jank.Compositor.TouchScroll", 2u);
-  EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name),
-              testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1)));
-
   // Frame 4: There is no jank since the increment from 30ms to  31ms is too
   // small. This tests if |NotifyFramePresented| can correctly handle the
   // situation when the frame interval reported in presentation feedback is 0.
@@ -434,9 +422,11 @@
   ImplThroughput().frames_expected = 100u;
   ReportMetrics();
   histogram_tester.ExpectTotalCount(
-      "Graphics.Smoothness.Jank.Compositor.TouchScroll", 3u);
+      "Graphics.Smoothness.Jank.Compositor.TouchScroll", 1u);
+
+  // There should be only one jank for frame 3.
   EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name),
-              testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 2)));
+              testing::ElementsAre(base::Bucket(1, 1)));
 }
 
 // Base case for checkerboarding: present a single frame with checkerboarding,
diff --git a/cc/metrics/jank_metrics.cc b/cc/metrics/jank_metrics.cc
index b6237766..8922e14 100644
--- a/cc/metrics/jank_metrics.cc
+++ b/cc/metrics/jank_metrics.cc
@@ -246,6 +246,14 @@
           GetMaxStaleHistogramName(tracker_type_), kStaleHistogramMin,
           kStaleHistogramMax, kStaleHistogramBucketCount,
           base::HistogramBase::kUmaTargetedHistogramFlag));
+
+  // Reset counts to avoid duplicated reporting.
+  Reset();
+}
+
+void JankMetrics::Reset() {
+  jank_count_ = 0;
+  max_staleness_ = {};
 }
 
 void JankMetrics::Merge(std::unique_ptr<JankMetrics> jank_metrics) {
diff --git a/cc/metrics/jank_metrics.h b/cc/metrics/jank_metrics.h
index 6fea8f00..91ca7fa3d 100644
--- a/cc/metrics/jank_metrics.h
+++ b/cc/metrics/jank_metrics.h
@@ -31,6 +31,9 @@
   JankMetrics(const JankMetrics&) = delete;
   JankMetrics& operator=(const JankMetrics&) = delete;
 
+  void AddFrameWithNoUpdate(uint32_t sequence_number,
+                            base::TimeDelta frame_interval);
+
   // Check if a jank occurs based on the timestamps of recent presentations.
   // If there is a jank, increment |jank_count_| and log a trace event.
   // Graphics.Smoothness.Stale.* metrics are reported in this function.
@@ -38,22 +41,24 @@
                          base::TimeTicks current_presentation_timestamp,
                          base::TimeDelta frame_interval);
 
-  // Report Graphics.Smoothness.(Jank|MaxStale).* metrics.
-  void ReportJankMetrics(int frames_expected);
+  void AddSubmitFrame(uint32_t frame_token, uint32_t sequence_number);
 
   // Merge the current jank count with a previously unreported jank metrics.
   void Merge(std::unique_ptr<JankMetrics> jank_metrics);
 
-  void AddSubmitFrame(uint32_t frame_token, uint32_t sequence_number);
+  // Report Graphics.Smoothness.(Jank|MaxStale).* metrics.
+  void ReportJankMetrics(int frames_expected);
 
-  void AddFrameWithNoUpdate(uint32_t sequence_number,
-                            base::TimeDelta frame_interval);
+  // Reset the internal jank count
+  void Reset();
+
+  int jank_count() const { return jank_count_; }
+
+  base::TimeDelta max_staleness() const { return max_staleness_; }
   FrameSequenceMetrics::ThreadType thread_type() const {
     return effective_thread_;
   }
 
-  int jank_count() const { return jank_count_; }
-
  private:
   // The type of the tracker this JankMetrics object is attached to.
   const FrameSequenceTrackerType tracker_type_;
diff --git a/cc/metrics/jank_metrics_unittest.cc b/cc/metrics/jank_metrics_unittest.cc
index b4c43be..dbc624e 100644
--- a/cc/metrics/jank_metrics_unittest.cc
+++ b/cc/metrics/jank_metrics_unittest.cc
@@ -415,8 +415,17 @@
   SimulateFrameSequence(other_reporter.get(), seqs);
 
   jank_reporter.Merge(std::move(other_reporter));
+  EXPECT_EQ(jank_reporter.jank_count(), 6);
+  EXPECT_TRUE(
+      jank_reporter.max_staleness() > base::TimeDelta::FromMilliseconds(33) &&
+      jank_reporter.max_staleness() < base::TimeDelta::FromMilliseconds(34));
   jank_reporter.ReportJankMetrics(100u);
 
+  // Jank / staleness values should be reset after reporting
+  EXPECT_EQ(jank_reporter.jank_count(), 0);
+  EXPECT_EQ(jank_reporter.max_staleness(),
+            base::TimeDelta::FromMilliseconds(0));
+
   // Expect 6 janks for "Main" (3 from each reporter)
   const char* metric = "Graphics.Smoothness.Jank.Main.RAF";
   const char* invalid_metric = "Graphics.Smoothness.Jank.Compositor.RAF";
diff --git a/cc/metrics/shared_metrics_buffer.h b/cc/metrics/shared_metrics_buffer.h
new file mode 100644
index 0000000..5167a77
--- /dev/null
+++ b/cc/metrics/shared_metrics_buffer.h
@@ -0,0 +1,46 @@
+// Copyright 2021 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 CC_METRICS_SHARED_METRICS_BUFFER_H_
+#define CC_METRICS_SHARED_METRICS_BUFFER_H_
+
+#include "device/base/synchronization/one_writer_seqlock.h"
+
+namespace cc {
+// The struct written in shared memory to transport metrics across
+// processes. |data| is protected by the sequence-lock |seq_lock|.
+// Note: This template copies data between processes. Any class that uses this
+// template would need security review.
+template <class T>
+struct SharedMetricsBuffer {
+  device::OneWriterSeqLock seq_lock;
+  T data;
+  static_assert(std::is_trivially_copyable<T>::value,
+                "Metrics shared across processes need to be trivially "
+                "copyable, otherwise it is dangerous to copy it.");
+
+  bool Read(T& out) const {
+    const uint32_t kMaxRetries = 5;
+    uint32_t retries = 0;
+    base::subtle::Atomic32 version;
+    do {
+      const uint32_t kMaxReadAttempts = 32;
+      version = seq_lock.ReadBegin(kMaxReadAttempts);
+      device::OneWriterSeqLock::AtomicReaderMemcpy(&out, &data, sizeof(T));
+    } while (seq_lock.ReadRetry(version) && ++retries < kMaxRetries);
+
+    // Consider the number of retries less than kMaxRetries as success.
+    return retries < kMaxRetries;
+  }
+
+  void Write(const T& in) {
+    seq_lock.WriteBegin();
+    device::OneWriterSeqLock::AtomicWriterMemcpy(&data, &in, sizeof(T));
+    seq_lock.WriteEnd();
+  }
+};
+
+}  // namespace cc
+
+#endif  // CC_METRICS_SHARED_METRICS_BUFFER_H_
diff --git a/cc/metrics/ukm_smoothness_data.h b/cc/metrics/ukm_smoothness_data.h
index 2bb5118..d4b95533 100644
--- a/cc/metrics/ukm_smoothness_data.h
+++ b/cc/metrics/ukm_smoothness_data.h
@@ -5,7 +5,7 @@
 #ifndef CC_METRICS_UKM_SMOOTHNESS_DATA_H_
 #define CC_METRICS_UKM_SMOOTHNESS_DATA_H_
 
-#include "device/base/synchronization/one_writer_seqlock.h"
+#include "cc/metrics/shared_metrics_buffer.h"
 
 namespace cc {
 
@@ -19,12 +19,7 @@
   double percentile_95 = 0.0;
 };
 
-// The struct written in shared memory to transport UkmSmoothnessData across
-// processes. |data| is protected by the sequence-lock |seq_lock|.
-struct UkmSmoothnessDataShared {
-  device::OneWriterSeqLock seq_lock;
-  struct UkmSmoothnessData data;
-};
+using UkmSmoothnessDataShared = SharedMetricsBuffer<UkmSmoothnessData>;
 
 }  // namespace cc
 
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 4d5286e..6834f8f 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -526,7 +526,6 @@
     "//third_party/android_deps:material_design_java",
     "//third_party/android_deps:protobuf_lite_runtime_java",
     "//third_party/android_media:android_media_java",
-    "//third_party/android_sdk:android_gcm_java",
     "//third_party/android_sdk/androidx_browser:androidx_browser_java",
     "//third_party/android_swipe_refresh:android_swipe_refresh_java",
     "//third_party/blink/public:blink_headers_java",
diff --git a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
index f6b4fe6..5d7057e2 100644
--- a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
@@ -894,16 +894,6 @@
         android:exported="false"
         android:name="com.google.android.gms.cast.framework.media.MediaIntentReceiver">
     </receiver>  # DIFF-ANCHOR: 1091f66b
-    <receiver  # DIFF-ANCHOR: 0ea504ef
-        android:exported="true"
-        android:name="com.google.android.gms.gcm.GcmReceiver"
-        android:permission="com.google.android.c2dm.permission.SEND">
-      <intent-filter>  # DIFF-ANCHOR: aae22013
-        <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
-        <action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
-        <category android:name="$PACKAGE"/>
-      </intent-filter>  # DIFF-ANCHOR: aae22013
-    </receiver>  # DIFF-ANCHOR: 0ea504ef
     <receiver  # DIFF-ANCHOR: f1c9c29d
         android:exported="true"
         android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
@@ -1214,9 +1204,9 @@
     <service  # DIFF-ANCHOR: d930289b
         android:exported="false"
         android:name="org.chromium.chrome.browser.services.gcm.ChromeGcmListenerService">
-      <intent-filter>  # DIFF-ANCHOR: aae22013
-        <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
-      </intent-filter>  # DIFF-ANCHOR: aae22013
+      <intent-filter>  # DIFF-ANCHOR: 69e70070
+        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
+      </intent-filter>  # DIFF-ANCHOR: 69e70070
     </service>  # DIFF-ANCHOR: d930289b
     <service  # DIFF-ANCHOR: 682abdc1
         android:exported="false"
diff --git a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
index 9468992..0e102676 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
@@ -832,16 +832,6 @@
         android:exported="false"
         android:name="com.google.android.gms.cast.framework.media.MediaIntentReceiver">
     </receiver>  # DIFF-ANCHOR: 1091f66b
-    <receiver  # DIFF-ANCHOR: 0ea504ef
-        android:exported="true"
-        android:name="com.google.android.gms.gcm.GcmReceiver"
-        android:permission="com.google.android.c2dm.permission.SEND">
-      <intent-filter>  # DIFF-ANCHOR: aae22013
-        <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
-        <action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
-        <category android:name="$PACKAGE"/>
-      </intent-filter>  # DIFF-ANCHOR: aae22013
-    </receiver>  # DIFF-ANCHOR: 0ea504ef
     <receiver  # DIFF-ANCHOR: f1c9c29d
         android:exported="true"
         android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
@@ -1116,9 +1106,9 @@
     <service  # DIFF-ANCHOR: d930289b
         android:exported="false"
         android:name="org.chromium.chrome.browser.services.gcm.ChromeGcmListenerService">
-      <intent-filter>  # DIFF-ANCHOR: aae22013
-        <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
-      </intent-filter>  # DIFF-ANCHOR: aae22013
+      <intent-filter>  # DIFF-ANCHOR: 69e70070
+        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
+      </intent-filter>  # DIFF-ANCHOR: 69e70070
     </service>  # DIFF-ANCHOR: d930289b
     <service  # DIFF-ANCHOR: 682abdc1
         android:exported="false"
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 0407e32..2f79a93c 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -943,16 +943,6 @@
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
             android:hardwareAccelerated="false" />
 
-        <!-- Receiver for GCM messages. -->
-        <receiver android:name="com.google.android.gms.gcm.GcmReceiver"
-            android:exported="true"
-            android:permission="com.google.android.c2dm.permission.SEND">
-            <intent-filter>
-                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
-                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
-               <category android:name="{{ manifest_package }}"/>
-            </intent-filter>
-        </receiver>
         <!-- GcmTaskService for registration for Invalidations. Not actually implemented anymore. -->
         <service android:name="com.google.ipc.invalidation.ticl.android2.channel.GcmRegistrationTaskService"
             android:exported="true"
@@ -965,7 +955,7 @@
         <service android:name="org.chromium.chrome.browser.services.gcm.ChromeGcmListenerService"
             android:exported="false" >
             <intent-filter>
-              <action android:name="com.google.android.c2dm.intent.RECEIVE" />
+                <action android:name="com.google.firebase.MESSAGING_EVENT" />
             </intent-filter>
         </service>
         <service android:name="org.chromium.chrome.browser.services.gcm.GCMBackgroundService"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 67c23fe..567d6db3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -2051,6 +2051,7 @@
             SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
             settingsLauncher.launchSettingsActivity(this);
             RecordUserAction.record("MobileMenuSettings");
+            return true;
         }
 
         if (id == R.id.update_menu_id) {
@@ -2078,36 +2079,56 @@
             }
             RecordUserAction.record("MobileMenuHistory");
             HistoryManagerUtils.showHistoryManager(this, currentTab);
+            return true;
         }
 
         // All the code below assumes currentTab is not null, so return early if it is null.
         if (currentTab == null) {
             return false;
-        } else if (id == R.id.backward_menu_id) {
+        }
+
+        if (id == R.id.backward_menu_id) {
             if (currentTab.canGoBack()) {
                 currentTab.goBack();
                 RecordUserAction.record("MobileMenuBackward");
+                return true;
             }
-        } else if (id == R.id.forward_menu_id) {
+            return false;
+        }
+
+        if (id == R.id.forward_menu_id) {
             if (currentTab.canGoForward()) {
                 currentTab.goForward();
                 RecordUserAction.record("MobileMenuForward");
+                return true;
             }
-        } else if (id == R.id.bookmark_this_page_id || id == R.id.bookmark_this_page_chip_id
+            return false;
+        }
+
+        if (id == R.id.bookmark_this_page_id || id == R.id.bookmark_this_page_chip_id
                 || id == R.id.add_to_bookmarks_menu_id) {
             addOrEditBookmark(currentTab);
             RecordUserAction.record("MobileMenuAddToBookmarks");
-        } else if (id == R.id.add_to_reading_list_menu_id) {
+            return true;
+        }
+
+        if (id == R.id.add_to_reading_list_menu_id) {
             mBookmarkBridgeSupplier.get().finishLoadingBookmarkModel(() -> {
                 BookmarkUtils.addToReadingList(currentTab.getOriginalUrl(), currentTab.getTitle(),
                         this.getSnackbarManager(), mBookmarkBridgeSupplier.get(), this);
             });
             RecordUserAction.record("MobileMenuAddToReadingList");
-        } else if (id == R.id.offline_page_id || id == R.id.offline_page_chip_id
+            return true;
+        }
+
+        if (id == R.id.offline_page_id || id == R.id.offline_page_chip_id
                 || id == R.id.add_to_downloads_menu_id) {
             DownloadUtils.downloadOfflinePage(this, currentTab);
             RecordUserAction.record("MobileMenuDownloadPage");
-        } else if (id == R.id.reload_menu_id) {
+            return true;
+        }
+
+        if (id == R.id.reload_menu_id) {
             if (currentTab.isLoading()) {
                 currentTab.stopLoading();
                 RecordUserAction.record("MobileMenuStop");
@@ -2115,7 +2136,10 @@
                 currentTab.reload();
                 RecordUserAction.record("MobileMenuReload");
             }
-        } else if (id == R.id.info_menu_id || id == R.id.info_id) {
+            return true;
+        }
+
+        if (id == R.id.info_menu_id || id == R.id.info_id) {
             WebContents webContents = currentTab.getWebContents();
             PageInfoController.show(this, webContents, null,
                     PageInfoController.OpenedFromSource.MENU,
@@ -2124,13 +2148,19 @@
                             /*offlinePageLoadUrlDelegate=*/
                             new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(currentTab)),
                     new ChromePermissionParamsListBuilderDelegate());
-        } else if (id == R.id.translate_id) {
+            return true;
+        }
+
+        if (id == R.id.translate_id) {
             RecordUserAction.record("MobileMenuTranslate");
             Tracker tracker = TrackerFactory.getTrackerForProfile(
-                    Profile.fromWebContents(getActivityTab().getWebContents()));
+                    Profile.fromWebContents(currentTab.getWebContents()));
             tracker.notifyEvent(EventConstants.TRANSLATE_MENU_BUTTON_CLICKED);
-            TranslateBridge.translateTabWhenReady(getActivityTab());
-        } else if (id == R.id.print_id) {
+            TranslateBridge.translateTabWhenReady(currentTab);
+            return true;
+        }
+
+        if (id == R.id.print_id) {
             PrintingController printingController = PrintingControllerImpl.getInstance();
             if (printingController != null && !printingController.isBusy()
                     && UserPrefs.get(Profile.getLastUsedRegularProfile())
@@ -2138,24 +2168,28 @@
                 printingController.startPrint(
                         new TabPrinter(currentTab), new PrintManagerDelegateImpl(this));
                 RecordUserAction.record("MobileMenuPrint");
+                return true;
             }
-        } else if (id == R.id.add_to_homescreen_id || id == R.id.add_to_homescreen_menu_id
+            return false;
+        }
+
+        if (id == R.id.add_to_homescreen_id || id == R.id.add_to_homescreen_menu_id
                 || id == R.id.install_app_id) {
-            boolean handled = false;
+            RecordUserAction.record("MobileMenuAddToHomescreen");
             if (ChromeFeatureList.isEnabled(ChromeFeatureList.PWA_INSTALL_USE_BOTTOMSHEET)) {
                 PwaBottomSheetController controller =
                         PwaBottomSheetControllerProvider.from(getWindowAndroid());
                 if (controller != null) {
                     controller.requestOrExpandBottomSheetInstaller(currentTab.getWebContents());
-                    handled = true;
+                    return true;
                 }
             }
-            if (!handled) {
-                AddToHomescreenCoordinator.showForAppMenu(this, getWindowAndroid(),
-                        getModalDialogManager(), currentTab.getWebContents(), mMenuItemData);
-            }
-            RecordUserAction.record("MobileMenuAddToHomescreen");
-        } else if (id == R.id.open_webapk_id || id == R.id.menu_open_webapk_id) {
+            AddToHomescreenCoordinator.showForAppMenu(this, getWindowAndroid(),
+                    getModalDialogManager(), currentTab.getWebContents(), mMenuItemData);
+            return true;
+        }
+
+        if (id == R.id.open_webapk_id || id == R.id.menu_open_webapk_id) {
             Context context = ContextUtils.getApplicationContext();
             String packageName =
                     WebApkValidator.queryFirstWebApkPackage(context, currentTab.getUrlString());
@@ -2167,19 +2201,25 @@
             } catch (ActivityNotFoundException e) {
                 Toast.makeText(context, R.string.open_webapk_failed, Toast.LENGTH_SHORT).show();
             }
-        } else if (id == R.id.request_desktop_site_id || id == R.id.request_desktop_site_check_id) {
+            return true;
+        }
+
+        if (id == R.id.request_desktop_site_id || id == R.id.request_desktop_site_check_id) {
             final boolean reloadOnChange = !currentTab.isNativePage();
             final boolean usingDesktopUserAgent =
                     currentTab.getWebContents().getNavigationController().getUseDesktopUserAgent();
             currentTab.getWebContents().getNavigationController().setUseDesktopUserAgent(
                     !usingDesktopUserAgent, reloadOnChange);
             RecordUserAction.record("MobileMenuRequestDesktopSite");
-        } else if (id == R.id.reader_mode_prefs_id) {
-            DomDistillerUIUtils.openSettings(currentTab.getWebContents());
-        } else {
-            return false;
+            return true;
         }
-        return true;
+
+        if (id == R.id.reader_mode_prefs_id) {
+            DomDistillerUIUtils.openSettings(currentTab.getWebContents());
+            return true;
+        }
+
+        return false;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatGcmListenerService.java b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatGcmListenerService.java
index 16104c78..a112b99 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatGcmListenerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitCompatGcmListenerService.java
@@ -7,13 +7,14 @@
 import android.content.Context;
 import android.os.Bundle;
 
-import com.google.android.gms.gcm.GcmListenerService;
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
 
 /**
  * GcmListenerService base class which will call through to the given {@link Impl}. This class must
  * be present in the base module, while the Impl can be in the chrome module.
  */
-public class SplitCompatGcmListenerService extends GcmListenerService {
+public class SplitCompatGcmListenerService extends FirebaseMessagingService {
     private String mServiceClassName;
     private Impl mImpl;
 
@@ -36,7 +37,9 @@
     }
 
     @Override
-    public void onMessageReceived(String from, Bundle data) {
+    public void onMessageReceived(RemoteMessage message) {
+        String from = message.getFrom();
+        Bundle data = message.toIntent().getExtras();
         mImpl.onMessageReceived(from, data);
     }
 
@@ -46,7 +49,7 @@
     }
 
     @Override
-    public void onSendError(String msgId, String error) {
+    public void onSendError(String msgId, Exception error) {
         mImpl.onSendError(msgId, error);
     }
 
@@ -55,6 +58,11 @@
         mImpl.onDeletedMessages();
     }
 
+    @Override
+    public void onNewToken(String token) {
+        mImpl.onNewToken(token);
+    }
+
     /**
      * Holds the implementation of service logic. Will be called by {@link
      * SplitCompatGcmListenerService}.
@@ -76,8 +84,10 @@
 
         public void onMessageSent(String msgId) {}
 
-        public void onSendError(String msgId, String error) {}
+        public void onSendError(String msgId, Exception error) {}
 
         public void onDeletedMessages() {}
+
+        public void onNewToken(String token) {}
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeader.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeader.java
index d979935..4a7f805 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeader.java
@@ -84,12 +84,11 @@
     private static BookmarkListEntry createReadingListSectionHeader(boolean read, Context context) {
         String title =
                 context.getString(read ? R.string.reading_list_read : R.string.reading_list_unread);
-        String description =
-                read ? null : context.getString(R.string.reading_list_ready_for_offline);
         int paddingTop = read ? context.getResources().getDimensionPixelSize(
                                  R.dimen.bookmark_reading_list_section_header_padding_top)
                               : 0;
-        return BookmarkListEntry.createSectionHeader(title, description, paddingTop, context);
+        return BookmarkListEntry.createSectionHeader(
+                title, /*description=*/null, paddingTop, context);
     }
 
     private static void recordMetrics(List<BookmarkListEntry> listItems) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
index 073bc0f2..ba777e0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.omnibox;
 
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
 import android.content.res.Configuration;
 import android.view.ActionMode;
 import android.view.View;
@@ -41,6 +43,8 @@
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 
+import java.util.List;
+
 /**
  * The public API of the location bar component. Location bar responsibilities are:
  * <ul>
@@ -66,6 +70,7 @@
     private AutocompleteCoordinator mAutocompleteCoordinator;
     private StatusCoordinator mStatusCoordinator;
     private WindowDelegate mWindowDelegate;
+    private WindowAndroid mWindowAndroid;
     private View mAutocompleteAnchorView;
     private LocationBarMediator mLocationBarMediator;
     private View mUrlBar;
@@ -117,6 +122,7 @@
             BackKeyBehaviorDelegate backKeyBehavior, SearchEngineLogoUtils searchEngineLogoUtils) {
         mLocationBarLayout = (LocationBarLayout) locationBarLayout;
         mWindowDelegate = windowDelegate;
+        mWindowAndroid = windowAndroid;
         mActivityLifecycleDispatcher = activityLifecycleDispatcher;
         mActivityLifecycleDispatcher.register(this);
         mAutocompleteAnchorView = autocompleteAnchorView;
@@ -289,7 +295,7 @@
     // OmniboxSuggestionsDropdownEmbedder implementation
     @Override
     public boolean isTablet() {
-        return DeviceFormFactor.isNonMultiDisplayContextOnTablet(mLocationBarLayout.getContext());
+        return DeviceFormFactor.isWindowOnTablet(mWindowAndroid);
     }
 
     @Override
@@ -368,7 +374,7 @@
     public void setOmniboxEditingText(String text) {
         mUrlCoordinator.setUrlBarData(UrlBarData.forNonUrlText(text), UrlBar.ScrollType.NO_SCROLL,
                 UrlBarCoordinator.SelectionState.SELECT_END);
-        mLocationBarMediator.updateButtonVisibility();
+        updateButtonVisibility();
     }
 
     /**
@@ -465,6 +471,84 @@
         }
     }
 
+    /**
+     * Toggles the mic button being shown when the location bar is not focused. By default the mic
+     * button is not shown.
+     */
+    public void setShouldShowMicButtonWhenUnfocused(boolean shouldShowMicButtonWhenUnfocused) {
+        mLocationBarMediator.setShouldShowMicButtonWhenUnfocusedForPhone(
+                shouldShowMicButtonWhenUnfocused);
+    }
+
+    /** Updates the visibility of the buttons inside the location bar. */
+    public void updateButtonVisibility() {
+        mLocationBarMediator.updateButtonVisibility();
+    }
+
+    // Tablet-specific methods.
+                                                       
+    /**
+     * Returns an animator to run for the given view when hiding buttons in the unfocused location 
+     * bar. This should also be used to create animators for hiding toolbar buttons.
+     *
+     * @param button The {@link View} of the button to hide.
+     */
+    public ObjectAnimator createHideButtonAnimatorForTablet(View button) {
+        assert isTablet();
+        return mLocationBarMediator.createHideButtonAnimatorForTablet(button);
+    }
+
+    /**
+     * Returns an animator to run for the given view when showing buttons in the unfocused location 
+     * bar. This should also be used to create animators for showing toolbar buttons.
+     *
+     * @param button The {@link View} of the button to show.
+     */
+    public ObjectAnimator createShowButtonAnimatorForTablet(View button) {
+        assert isTablet();
+        return mLocationBarMediator.createShowButtonAnimatorForTablet(button);
+    }
+
+    /**
+     * Creates animators for hiding buttons in the unfocused location bar. The buttons fade out
+     * while width of the location bar gets larger. There are toolbar buttons that also hide at
+     * the same time, causing the width of the location bar to change.
+     *
+     * @param toolbarStartPaddingDifference The difference in the toolbar's start padding
+     *         between the beginning and end of the animation.
+     * @return A list of animators to run.
+     */
+    public List<Animator> getHideButtonsWhenUnfocusedAnimatorsForTablet(
+            int toolbarStartPaddingDifference) {
+        assert isTablet();
+        return mLocationBarMediator.getHideButtonsWhenUnfocusedAnimatorsForTablet(
+                toolbarStartPaddingDifference);
+    }
+
+    /**
+     * Creates animators for showing buttons in the unfocused location bar. The buttons fade in
+     * while width of the location bar gets smaller. There are toolbar buttons that also show at
+     * the same time, causing the width of the location bar to change.
+     *
+     * @param toolbarStartPaddingDifference The difference in the toolbar's start padding
+     *         between the beginning and end of the animation.
+     * @return A list of animators to run.
+     */
+    public List<Animator> getShowButtonsWhenUnfocusedAnimatorsForTablet(
+            int toolbarStartPaddingDifference) {
+        assert isTablet();
+        return mLocationBarMediator.getShowButtonsWhenUnfocusedAnimatorsForTablet(
+                toolbarStartPaddingDifference);
+    }
+
+    /** Toggles whether buttons should be displayed in the URL bar when it's not focused. */
+    public void setShouldShowButtonsWhenUnfocusedForTablet(boolean shouldShowButtons) {
+        assert isTablet();
+        mLocationBarMediator.setShouldShowButtonsWhenUnfocusedForTablet(shouldShowButtons);
+    }
+
+    // End tablet-specific methods.
+
     public void setVoiceRecognitionHandlerForTesting(
             VoiceRecognitionHandler voiceRecognitionHandler) {
         mLocationBarMediator.setVoiceRecognitionHandlerForTesting(voiceRecognitionHandler);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorTablet.java
index 3f669a2..14f468a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorTablet.java
@@ -4,13 +4,9 @@
 
 package org.chromium.chrome.browser.omnibox;
 
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
 import android.graphics.drawable.Drawable;
 import android.view.View;
 
-import java.util.List;
-
 /**
  * A supplement to {@link LocationBarCoordinator} with methods specific to larger devices.
  */
@@ -27,64 +23,6 @@
     }
 
     /**
-     * @param button The {@link View} of the button to hide.
-     * @return An animator to run for the given view when hiding buttons in the unfocused
-     *         location bar. This should also be used to create animators for hiding toolbar
-     *         buttons.
-     */
-    public ObjectAnimator createHideButtonAnimator(View button) {
-        return mLocationBarTablet.createHideButtonAnimator(button);
-    }
-
-    /**
-     * @param button The {@link View} of the button to show.
-     * @return An animator to run for the given view when showing buttons in the unfocused
-     *         location bar. This should also be used to create animators for showing toolbar
-     *         buttons.
-     */
-    public ObjectAnimator createShowButtonAnimator(View button) {
-        return mLocationBarTablet.createShowButtonAnimator(button);
-    }
-
-    /**
-     * Creates animators for hiding buttons in the unfocused location bar. The buttons fade out
-     * while width of the location bar gets larger. There are toolbar buttons that also hide at
-     * the same time, causing the width of the location bar to change.
-     *
-     * @param toolbarStartPaddingDifference The difference in the toolbar's start padding
-     *         between the beginning and end of the animation.
-     * @return A list of animators to run.
-     */
-    public List<Animator> getHideButtonsWhenUnfocusedAnimators(int toolbarStartPaddingDifference) {
-        return mLocationBarTablet.getHideButtonsWhenUnfocusedAnimators(
-                toolbarStartPaddingDifference);
-    }
-
-    /**
-     * Creates animators for showing buttons in the unfocused location bar. The buttons fade in
-     * while width of the location bar gets smaller. There are toolbar buttons that also show at
-     * the same time, causing the width of the location bar to change.
-     *
-     * @param toolbarStartPaddingDifference The difference in the toolbar's start padding
-     *         between the beginning and end of the animation.
-     * @return A list of animators to run.
-     */
-    public List<Animator> getShowButtonsWhenUnfocusedAnimators(int toolbarStartPaddingDifference) {
-        return mLocationBarTablet.getShowButtonsWhenUnfocusedAnimators(
-                toolbarStartPaddingDifference);
-    }
-
-    /** Sets, whether buttons should be displayed in the URL bar when it's not focused. */
-    public void setShouldShowButtonsWhenUnfocused(boolean shouldShowButtons) {
-        mLocationBarTablet.setShouldShowButtonsWhenUnfocused(shouldShowButtons);
-    }
-
-    /** Updates the visibility of the buttons inside the location bar. */
-    public void updateButtonVisibility() {
-        mLocationBarTablet.updateButtonVisibility();
-    }
-
-    /**
      * Gets the background drawable.
      *
      * <p>TODO(1133482): Hide this View interaction if possible.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 4712088..a424558 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -8,7 +8,6 @@
 import android.content.res.ColorStateList;
 import android.graphics.drawable.Drawable;
 import android.os.Parcelable;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
@@ -40,7 +39,6 @@
 public class LocationBarLayout extends FrameLayout {
     protected ImageButton mDeleteButton;
     protected ImageButton mMicButton;
-    private boolean mShouldShowMicButtonWhenUnfocused;
     protected UrlBar mUrlBar;
 
     protected UrlBarCoordinator mUrlCoordinator;
@@ -50,12 +48,8 @@
 
     protected StatusCoordinator mStatusCoordinator;
 
-    private boolean mUrlFocusChangeInProgress;
     protected boolean mNativeInitialized;
-    private boolean mUrlHasFocus;
-    protected boolean mVoiceSearchEnabled;
 
-    protected float mUrlFocusChangeFraction;
     protected LinearLayout mUrlActionContainer;
 
     protected CompositeTouchDelegate mCompositeTouchDelegate;
@@ -128,8 +122,6 @@
         mStatusCoordinator = statusCoordinator;
         mLocationBarDataProvider = locationBarDataProvider;
         mSearchEngineLogoUtils = searchEngineLogoUtils;
-
-        updateButtonVisibility();
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
@@ -142,8 +134,6 @@
      */
     public void onFinishNativeInitialization() {
         mNativeInitialized = true;
-
-        updateMicButtonVisibility();
     }
 
     /* package */ void setMicButtonDrawable(Drawable drawable) {
@@ -178,33 +168,12 @@
         // When we restore tabs, we focus the selected tab so the URL of the page shows.
     }
 
-    /* package */ boolean isUrlBarFocused() {
-        return mUrlHasFocus;
-    }
-
     protected void onNtpStartedLoading() {}
 
     public View getSecurityIconView() {
         return mStatusCoordinator.getSecurityIconView();
     }
 
-    @CallSuper
-    protected void setUrlFocusChangeFraction(float fraction) {
-        mUrlFocusChangeFraction = fraction;
-    }
-
-    /* package */ float getUrlFocusChangeFraction() {
-        return mUrlFocusChangeFraction;
-    }
-
-    /**
-     * @return Whether the URL focus change is taking place, e.g. a focus animation is running on
-     *         a phone device.
-     */
-    public boolean isUrlFocusChangeInProgress() {
-        return mUrlFocusChangeInProgress;
-    }
-
     /**
      * Specify whether location bar should present icons when focused.
      * @param showIcon True if we should show the icons when the url is focused.
@@ -212,21 +181,6 @@
     protected void setShowIconsWhenUrlFocused(boolean showIcon) {}
 
     /**
-     * @param inProgress Whether a URL focus change is taking place.
-     */
-    protected void setUrlFocusChangeInProgress(boolean inProgress) {
-        mUrlFocusChangeInProgress = inProgress;
-    }
-
-    /**
-     * Triggered when the URL input field has gained or lost focus.
-     * @param hasFocus Whether the URL field has gained focus.
-     */
-    protected void setUrlHasFocus(boolean hasFocus) {
-        mUrlHasFocus = hasFocus;
-    }
-
-    /**
      * @return The margin to be applied to the URL bar based on the buttons currently visible next
      *         to it, used to avoid text overlapping the buttons and vice versa.
      */
@@ -323,21 +277,14 @@
         return outList;
     }
 
-    /**
-     * @return Whether the delete button should be shown.
-     */
-    protected boolean shouldShowDeleteButton() {
-        // Show the delete button at the end when the bar has focus and has some text.
-        boolean hasText = mUrlCoordinator != null
-                && !TextUtils.isEmpty(mUrlCoordinator.getTextWithAutocomplete());
-        return hasText && (mUrlBar.hasFocus() || mUrlFocusChangeInProgress);
+    /** Sets the visibility of the delete URL content button. */
+    /* package */ void setDeleteButtonVisibility(boolean shouldShow) {
+        mDeleteButton.setVisibility(shouldShow ? VISIBLE : GONE);
     }
 
-    /**
-     * Updates the display of the delete URL content button.
-     */
-    protected void updateDeleteButtonVisibility() {
-        mDeleteButton.setVisibility(shouldShowDeleteButton() ? VISIBLE : GONE);
+    /** Sets the visibility of the mic button. */
+    /* package */ void setMicButtonVisibility(boolean shouldShow) {
+        mMicButton.setVisibility(shouldShow ? VISIBLE : GONE);
     }
 
     protected void setUnfocusedWidth(int unfocusedWidth) {
@@ -350,41 +297,11 @@
                 shouldShowSearchEngineLogo, isSearchEngineGoogle, searchEngineUrl);
     }
 
-    /**
-     * Call to update the visibility of the buttons inside the location bar.
-     */
-    protected void updateButtonVisibility() {
-        updateDeleteButtonVisibility();
-    }
-
-    /**
-     * Updates the display of the mic button.
-     */
-    protected void updateMicButtonVisibility() {
-        boolean visible = !shouldShowDeleteButton();
-        boolean showMicButton = mVoiceSearchEnabled && visible
-                && (mUrlBar.hasFocus() || mUrlFocusChangeInProgress || mUrlFocusChangeFraction > 0f
-                        || mShouldShowMicButtonWhenUnfocused);
-        mMicButton.setVisibility(showMicButton ? VISIBLE : GONE);
-    }
-
-    /**
-     * Value determines if mic button should be shown when location bar is not focused. By default
-     * mic button is not shown. It is only shown for SearchActivityLocationBarLayout.
-     */
-    protected void setShouldShowMicButtonWhenUnfocused(boolean shouldShowMicButtonWhenUnfocused) {
-        mShouldShowMicButtonWhenUnfocused = shouldShowMicButtonWhenUnfocused;
-    }
-
     @VisibleForTesting
     public StatusCoordinator getStatusCoordinatorForTesting() {
         return mStatusCoordinator;
     }
 
-    /* package */ void setVoiceSearchEnabled(boolean isEnabled) {
-        mVoiceSearchEnabled = isEnabled;
-    }
-
     /** Update the status visibility according to the current state held in LocationBar. */
     /* package */ void updateStatusVisibility() {}
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index c4eb3829..013feed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.omnibox;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.ComponentCallbacks;
 import android.content.Context;
@@ -29,6 +30,7 @@
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.OneshotSupplierImpl;
+import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.gsa.GSAState;
 import org.chromium.chrome.browser.locale.LocaleManager;
@@ -59,6 +61,7 @@
 import org.chromium.content_public.common.ResourceRequestBody;
 import org.chromium.ui.base.PageTransition;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.interpolators.BakedBezierInterpolator;
 import org.chromium.ui.util.ColorUtils;
 
 import java.util.ArrayList;
@@ -74,13 +77,17 @@
                                      VoiceRecognitionHandler.Observer,
                                      AssistantVoiceSearchService.Observer, UrlBarDelegate,
                                      OnKeyListener, ComponentCallbacks, TemplateUrlServiceObserver {
-    private static final long MAX_NTP_KEYBOARD_FOCUS_DURATION_MS = 200;
+    private static final int ICON_FADE_ANIMATION_DURATION_MS = 150;
+    private static final int ICON_FADE_ANIMATION_DELAY_MS = 75;
+    private static final long NTP_KEYBOARD_FOCUS_DURATION_MS = 200;
+    private static final int WIDTH_CHANGE_ANIMATION_DURATION_MS = 225;
+    private static final int WIDTH_CHANGE_ANIMATION_DELAY_MS = 75;
 
     private final Property<LocationBarMediator, Float> mUrlFocusChangeFractionProperty =
             new Property<LocationBarMediator, Float>(Float.class, "") {
                 @Override
                 public Float get(LocationBarMediator object) {
-                    return mLocationBarLayout.getUrlFocusChangeFraction();
+                    return mUrlFocusChangeFraction;
                 }
 
                 @Override
@@ -89,6 +96,19 @@
                 }
             };
 
+    private final Property<LocationBarMediator, Float> mWidthChangeFractionPropertyTablet =
+            new Property<LocationBarMediator, Float>(Float.class, "") {
+                @Override
+                public Float get(LocationBarMediator object) {
+                    return ((LocationBarTablet) mLocationBarLayout).getWidthChangeFraction();
+                }
+
+                @Override
+                public void set(LocationBarMediator object, Float value) {
+                    ((LocationBarTablet) mLocationBarLayout).setWidthChangeAnimationFraction(value);
+                }
+            };
+
     private final LocationBarLayout mLocationBarLayout;
     private VoiceRecognitionHandler mVoiceRecognitionHandler;
     private final LocationBarDataProvider mLocationBarDataProvider;
@@ -120,7 +140,14 @@
     private boolean mUrlFocusedFromFakebox;
     private boolean mUrlFocusedFromQueryTiles;
     private boolean mUrlFocusedWithoutAnimations;
+    private boolean mIsUrlFocusChangeInProgress;
     private final boolean mIsTablet;
+    private boolean mShouldShowMicButtonWhenUnfocused;
+    // Whether the microphone and bookmark buttons should be shown in the tablet location bar. These
+    // buttons are hidden if the window size is < 600dp.
+    private boolean mShouldShowButtonsWhenUnfocused;
+    private float mUrlFocusChangeFraction;
+    private boolean mUrlHasFocus;
 
     /*package */ LocationBarMediator(@NonNull Context context,
             @NonNull LocationBarLayout locationBarLayout,
@@ -149,6 +176,7 @@
         mWindowAndroid = windowAndroid;
         mIsTablet = isTablet;
         mSearchEngineLogoUtils = searchEngineLogoUtils;
+        mShouldShowButtonsWhenUnfocused = isTablet;
     }
 
     /**
@@ -165,6 +193,15 @@
         mAutocompleteCoordinator = autocompleteCoordinator;
         mStatusCoordinator = statusCoordinator;
         updateShouldAnimateIconChanges();
+        updateButtonVisibility();
+
+        if (mIsTablet) {
+            mStatusCoordinator.setShowIconsWhenUrlFocused(true);
+            if (mSearchEngineLogoUtils.shouldShowSearchEngineLogo(
+                        mLocationBarDataProvider.isIncognito())) {
+                mStatusCoordinator.setStatusIconShown(true);
+            }
+        }
     }
 
     /*package */ void destroy() {
@@ -189,7 +226,7 @@
 
     /*package */ void onUrlFocusChange(boolean hasFocus) {
         setUrlFocusChangeInProgress(true);
-        mLocationBarLayout.setUrlHasFocus(hasFocus);
+        mUrlHasFocus = hasFocus;
         updateButtonVisibility();
         updateShouldAnimateIconChanges();
         onPrimaryColorChanged();
@@ -250,7 +287,23 @@
     }
 
     /*package */ void setUrlFocusChangeFraction(float fraction) {
-        mLocationBarLayout.setUrlFocusChangeFraction(fraction);
+        mUrlFocusChangeFraction = fraction;
+        if (mIsTablet) {
+            mLocationBarDataProvider.getNewTabPageDelegate().setUrlFocusChangeAnimationPercent(
+                    fraction);
+        } else {
+            if (fraction > 0f) {
+                mLocationBarLayout.setUrlActionContainerVisibility(View.VISIBLE);
+            } else if (fraction == 0f && !mIsUrlFocusChangeInProgress) {
+                // If a URL focus change is in progress, then it will handle setting the visibility
+                // correctly after it completes.  If done here, it would cause the URL to jump due
+                // to a badly timed layout call.
+                mLocationBarLayout.setUrlActionContainerVisibility(View.GONE);
+            }
+
+            mStatusCoordinator.setUrlFocusChangePercent(fraction);
+            updateButtonVisibility();
+        }
     }
 
     /*package */ void setUnfocusedWidth(int unfocusedWidth) {
@@ -287,7 +340,7 @@
     }
 
     /*package */ void showUrlBarCursorWithoutFocusAnimations() {
-        if (mLocationBarLayout.isUrlBarFocused() || mUrlFocusedFromFakebox) {
+        if (mUrlHasFocus || mUrlFocusedFromFakebox) {
             return;
         }
 
@@ -299,7 +352,7 @@
     }
 
     /*package */ void revertChanges() {
-        if (mLocationBarLayout.isUrlBarFocused()) {
+        if (mUrlHasFocus) {
             String currentUrl = mLocationBarDataProvider.getCurrentUrl();
             if (NativePageFactory.isNativePageUrl(
                         currentUrl, mLocationBarDataProvider.isIncognito())) {
@@ -330,7 +383,7 @@
 
         // Handle the case where suggestions (in particular zero suggest) are received without the
         // URL focusing happening.
-        if (mUrlFocusedWithoutAnimations && mLocationBarLayout.isUrlBarFocused()) {
+        if (mUrlFocusedWithoutAnimations && mUrlHasFocus) {
             handleUrlFocusAnimation(/*hasFocus=*/true);
         }
 
@@ -417,9 +470,13 @@
         return mUrlFocusedFromQueryTiles;
     }
 
-    /** Updates the visibility of the buttons inside the location bar. */
+    /** Recalculates the visibility of the buttons inside the location bar. */
     /* package */ void updateButtonVisibility() {
-        mLocationBarLayout.updateButtonVisibility();
+        updateDeleteButtonVisibility();
+        updateMicButtonVisibility();
+        if (mIsTablet) {
+            updateTabletButtonsVisibility();
+        }
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@@ -472,14 +529,14 @@
 
     /* package */ void setUrlFocusChangeInProgress(boolean inProgress) {
         if (mUrlCoordinator == null) return;
-        mLocationBarLayout.setUrlFocusChangeInProgress(inProgress);
+        mIsUrlFocusChangeInProgress = inProgress;
         if (!inProgress) {
             updateButtonVisibility();
 
             // The accessibility bounding box is not properly updated when focusing the Omnibox
             // from the NTP fakebox.  Clearing/re-requesting focus triggers the bounding box to
             // be recalculated.
-            if (didFocusUrlFromFakebox() && mLocationBarLayout.isUrlBarFocused()
+            if (didFocusUrlFromFakebox() && mUrlHasFocus
                     && ChromeAccessibilityUtil.get().isAccessibilityEnabled()) {
                 String existingText = mUrlCoordinator.getTextWithoutAutocomplete();
                 mUrlCoordinator.clearFocus();
@@ -495,7 +552,7 @@
             }
 
             for (UrlFocusChangeListener listener : mUrlFocusChangeListeners) {
-                listener.onUrlAnimationFinished(mLocationBarLayout.isUrlBarFocused());
+                listener.onUrlAnimationFinished(mUrlHasFocus);
             }
         }
     }
@@ -552,7 +609,7 @@
             mUrlFocusChangeAnimator = ObjectAnimator.ofFloat(
                     this, mUrlFocusChangeFractionProperty, hasFocus ? 1f : 0f);
             mUrlFocusChangeAnimator.setDuration(
-                    (long) (MAX_NTP_KEYBOARD_FOCUS_DURATION_MS * screenSizeRatio));
+                    (long) (NTP_KEYBOARD_FOCUS_DURATION_MS * screenSizeRatio));
             mUrlFocusChangeAnimator.addListener(new CancelAwareAnimatorListener() {
                 @Override
                 public void onEnd(Animator animator) {
@@ -568,6 +625,174 @@
         }
     }
 
+    /* package */ void setShouldShowMicButtonWhenUnfocusedForPhone(boolean shouldShow) {
+        assert !mIsTablet;
+        mShouldShowMicButtonWhenUnfocused = shouldShow;
+    }
+
+    /**
+     * @param shouldShow Whether buttons should be displayed in the URL bar when it's not
+     *                          focused.
+     */
+    /* package */ void setShouldShowButtonsWhenUnfocusedForTablet(boolean shouldShow) {
+        assert mIsTablet;
+        mShouldShowButtonsWhenUnfocused = shouldShow;
+        updateButtonVisibility();
+    }
+
+    /**
+     * @param button The {@link View} of the button to show.
+     * Returns An animator to run for the given view when showing buttons in the unfocused location
+     *         bar. This should also be used to create animators for showing toolbar buttons.
+     */
+    /* package */ ObjectAnimator createShowButtonAnimatorForTablet(View button) {
+        assert mIsTablet;
+        if (button.getVisibility() != View.VISIBLE) {
+            button.setAlpha(0.f);
+        }
+        ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(button, View.ALPHA, 1.f);
+        buttonAnimator.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
+        buttonAnimator.setStartDelay(ICON_FADE_ANIMATION_DELAY_MS);
+        buttonAnimator.setDuration(ICON_FADE_ANIMATION_DURATION_MS);
+        return buttonAnimator;
+    }
+
+    /**
+     * @param button The {@link View} of the button to hide.
+     * Returns An animator to run for the given view when hiding buttons in the unfocused location
+     *         bar. This should also be used to create animators for hiding toolbar buttons.
+     */
+    /* package */ ObjectAnimator createHideButtonAnimatorForTablet(View button) {
+        assert mIsTablet;
+        ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(button, View.ALPHA, 0.f);
+        buttonAnimator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
+        buttonAnimator.setDuration(ICON_FADE_ANIMATION_DURATION_MS);
+        return buttonAnimator;
+    }
+
+    /**
+     * Creates animators for showing buttons in the unfocused tablet location bar. The buttons fade
+     * in while the width of the location bar decreases. There are toolbar buttons that show at
+     * the same time, causing the width of the location bar to change.
+     *
+     * @param toolbarStartPaddingDifference The difference in the toolbar's start padding between
+     *                                      the beginning and end of the animation.
+     * @return A List of animators to run.
+     */
+    /* package */ List<Animator> getShowButtonsWhenUnfocusedAnimatorsForTablet(
+            int toolbarStartPaddingDifference) {
+        assert mIsTablet;
+        LocationBarTablet locationBarTablet = ((LocationBarTablet) mLocationBarLayout);
+
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        Animator widthChangeAnimator =
+                ObjectAnimator.ofFloat(this, mWidthChangeFractionPropertyTablet, 0f);
+        widthChangeAnimator.setDuration(WIDTH_CHANGE_ANIMATION_DURATION_MS);
+        widthChangeAnimator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
+        widthChangeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                locationBarTablet.startAnimatingWidthChange(toolbarStartPaddingDifference);
+                setShouldShowButtonsWhenUnfocusedForTablet(true);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                // Only reset values if the animation is ending because it's completely finished
+                // and not because it was canceled.
+                if (locationBarTablet.getWidthChangeFraction() == 0.f) {
+                    locationBarTablet.finishAnimatingWidthChange();
+                    locationBarTablet.resetValuesAfterAnimation();
+                }
+            }
+        });
+        animators.add(widthChangeAnimator);
+
+        // When buttons show in the unfocused location bar, either the delete button or bookmark
+        // button will be showing. If the delete button is currently showing, the bookmark button
+        // should not fade in.
+        if (!locationBarTablet.isDeleteButtonVisible()) {
+            animators.add(createShowButtonAnimatorForTablet(
+                    locationBarTablet.getBookmarkButtonForAnimation()));
+        }
+
+        if (shouldShowSaveOfflineButton()) {
+            animators.add(createShowButtonAnimatorForTablet(
+                    locationBarTablet.getSaveOfflineButtonForAnimation()));
+        } else if (!locationBarTablet.isMicButtonVisible()
+                || locationBarTablet.getMicButtonAlpha() != 1.f) {
+            // If the microphone button is already fully visible, don't animate its appearance.
+            animators.add(createShowButtonAnimatorForTablet(
+                    locationBarTablet.getMicButtonForAnimation()));
+        }
+
+        return animators;
+    }
+
+    /**
+     * Creates animators for hiding buttons in the unfocused tablet location bar. The buttons fade
+     * out while the width of the location bar increases. There are toolbar buttons that hide at
+     * the same time, causing the width of the location bar to change.
+     *
+     * @param toolbarStartPaddingDifference The difference in the toolbar's start padding between
+     *                                      the beginning and end of the animation.
+     * @return A List of animators to run.
+     */
+    /* package */ List<Animator> getHideButtonsWhenUnfocusedAnimatorsForTablet(
+            int toolbarStartPaddingDifference) {
+        LocationBarTablet locationBarTablet = ((LocationBarTablet) mLocationBarLayout);
+
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        Animator widthChangeAnimator =
+                ObjectAnimator.ofFloat(this, mWidthChangeFractionPropertyTablet, 1f);
+        widthChangeAnimator.setStartDelay(WIDTH_CHANGE_ANIMATION_DELAY_MS);
+        widthChangeAnimator.setDuration(WIDTH_CHANGE_ANIMATION_DURATION_MS);
+        widthChangeAnimator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
+        widthChangeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                locationBarTablet.startAnimatingWidthChange(toolbarStartPaddingDifference);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                // Only reset values if the animation is ending because it's completely finished
+                // and not because it was canceled.
+                if (locationBarTablet.getWidthChangeFraction() == 1.f) {
+                    locationBarTablet.finishAnimatingWidthChange();
+                    locationBarTablet.resetValuesAfterAnimation();
+                    setShouldShowButtonsWhenUnfocusedForTablet(false);
+                }
+            }
+        });
+        animators.add(widthChangeAnimator);
+
+        // When buttons show in the unfocused location bar, either the delete button or bookmark
+        // button will be showing. If the delete button is currently showing, the bookmark button
+        // should not fade out.
+        if (!locationBarTablet.isDeleteButtonVisible()) {
+            animators.add(createHideButtonAnimatorForTablet(
+                    locationBarTablet.getBookmarkButtonForAnimation()));
+        }
+
+        if (shouldShowSaveOfflineButton() && locationBarTablet.isSaveOfflineButtonVisible()) {
+            animators.add(createHideButtonAnimatorForTablet(
+                    locationBarTablet.getSaveOfflineButtonForAnimation()));
+        } else if (!(mUrlHasFocus && !locationBarTablet.isDeleteButtonVisible())) {
+            // If the save offline button isn't enabled, the microphone button always shows when
+            // buttons are shown in the unfocused location bar. When buttons are hidden in the
+            // unfocused location bar, the microphone shows if the location bar is focused and the
+            // delete button isn't showing. The microphone button should not be hidden if the
+            // url bar is currently focused and the delete button isn't showing.
+            animators.add(createHideButtonAnimatorForTablet(
+                    locationBarTablet.getMicButtonForAnimation()));
+        }
+
+        return animators;
+    }
+
     /**
      * Changes the text on the url bar.  The text update will be applied regardless of the current
      * focus state (comparing to {@link LocationBarMediator#setUrl} which only applies text updates
@@ -668,9 +893,8 @@
     }
 
     private void updateShouldAnimateIconChanges() {
-        boolean shouldAnimate = mIsTablet
-                ? isUrlBarFocused()
-                : isUrlBarFocused() || mLocationBarLayout.isUrlFocusChangeInProgress();
+        boolean shouldAnimate =
+                mIsTablet ? isUrlBarFocused() : isUrlBarFocused() || mIsUrlFocusChangeInProgress;
         mStatusCoordinator.setShouldAnimateIconChanges(shouldAnimate);
     }
 
@@ -679,6 +903,126 @@
                 "Android.OmniboxFocusReason", reason, OmniboxFocusReason.NUM_ENTRIES);
     }
 
+    private void updateMicButtonState() {
+        updateButtonVisibility();
+    }
+
+    /**
+     * Updates the display of the mic button.
+     */
+    private void updateMicButtonVisibility() {
+        mLocationBarLayout.setMicButtonVisibility(shouldShowMicButton());
+    }
+
+    private void updateDeleteButtonVisibility() {
+        mLocationBarLayout.setDeleteButtonVisibility(shouldShowDeleteButton());
+    }
+
+    private void updateTabletButtonsVisibility() {
+        assert mIsTablet;
+        LocationBarTablet locationBarTablet = (LocationBarTablet) mLocationBarLayout;
+        boolean showBookmarkButton =
+                mShouldShowButtonsWhenUnfocused && shouldShowPageActionButtons();
+        locationBarTablet.setBookmarkButtonVisibility(showBookmarkButton);
+
+        boolean showSaveOfflineButton =
+                mShouldShowButtonsWhenUnfocused && shouldShowSaveOfflineButton();
+        locationBarTablet.setSaveOfflineButtonVisibility(
+                showSaveOfflineButton, isSaveOfflineButtonEnabled());
+    }
+
+    /**
+     * @return Whether the delete button should be shown.
+     */
+    private boolean shouldShowDeleteButton() {
+        // Show the delete button at the end when the bar has focus and has some text.
+        boolean hasText = mUrlCoordinator != null
+                && !TextUtils.isEmpty(mUrlCoordinator.getTextWithAutocomplete());
+        return hasText && (mUrlHasFocus || mIsUrlFocusChangeInProgress);
+    }
+
+    private boolean shouldShowMicButton() {
+        if (mIsTablet && mShouldShowButtonsWhenUnfocused) {
+            return mVoiceRecognitionHandler != null
+                    && mVoiceRecognitionHandler.isVoiceSearchEnabled() && mNativeInitialized
+                    && (mUrlHasFocus || mIsUrlFocusChangeInProgress);
+        } else {
+            boolean deleteButtonVisible = shouldShowDeleteButton();
+            return mVoiceRecognitionHandler != null
+                    && mVoiceRecognitionHandler.isVoiceSearchEnabled() && !deleteButtonVisible
+                    && (mUrlHasFocus || mIsUrlFocusChangeInProgress || mUrlFocusChangeFraction > 0f
+                            || mShouldShowMicButtonWhenUnfocused);
+        }
+    }
+
+    private boolean shouldShowSaveOfflineButton() {
+        assert mIsTablet;
+        if (!mNativeInitialized || mLocationBarDataProvider == null) return false;
+        Tab tab = mLocationBarDataProvider.getTab();
+        if (tab == null) return false;
+        // The save offline button should not be shown on native pages. Currently, trying to
+        // save an offline page in incognito crashes, so don't show it on incognito either.
+        return shouldShowPageActionButtons() && !tab.isIncognito();
+    }
+
+    private boolean isSaveOfflineButtonEnabled() {
+        if (mLocationBarDataProvider == null) return false;
+        return DownloadUtils.isAllowedToDownloadPage(mLocationBarDataProvider.getTab());
+    }
+
+    private boolean shouldShowPageActionButtons() {
+        assert mIsTablet;
+        if (!mNativeInitialized) return true;
+
+        // There are two actions, bookmark and save offline, and they should be shown if the
+        // omnibox isn't focused.
+        return !(mUrlHasFocus || mIsUrlFocusChangeInProgress);
+    }
+
+    private void updateUrl() {
+        setUrl(mLocationBarDataProvider.getCurrentUrl(), mLocationBarDataProvider.getUrlBarData());
+    }
+
+    private void updateOmniboxPrerender() {
+        if (mOmniboxPrerender == null) return;
+        // Profile may be null if switching to a tab that has not yet been initialized.
+        Profile profile = mProfileSupplier.get();
+        if (profile == null) return;
+        mOmniboxPrerender.clear(profile);
+    }
+
+    private boolean handleKeyEvent(View view, int keyCode, KeyEvent event) {
+        boolean isRtl = view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        if (mAutocompleteCoordinator.handleKeyEvent(keyCode, event)) {
+            return true;
+        } else if (keyCode == KeyEvent.KEYCODE_BACK) {
+            if (KeyNavigationUtil.isActionDown(event) && event.getRepeatCount() == 0) {
+                // Tell the framework to start tracking this event.
+                mLocationBarLayout.getKeyDispatcherState().startTracking(event, this);
+                return true;
+            } else if (KeyNavigationUtil.isActionUp(event)) {
+                mLocationBarLayout.getKeyDispatcherState().handleUpEvent(event);
+                if (event.isTracking() && !event.isCanceled()) {
+                    backKeyPressed();
+                    return true;
+                }
+            }
+        } else if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
+            if (KeyNavigationUtil.isActionDown(event) && event.getRepeatCount() == 0) {
+                revertChanges();
+                return true;
+            }
+        } else if ((!isRtl && KeyNavigationUtil.isGoRight(event))
+                || (isRtl && KeyNavigationUtil.isGoLeft(event))) {
+            // Ensures URL bar doesn't lose focus, when RIGHT or LEFT (RTL) key is pressed while
+            // the cursor is positioned at the end of the text.
+            TextView tv = (TextView) view;
+            return tv.getSelectionStart() == tv.getSelectionEnd()
+                    && tv.getSelectionEnd() == tv.getText().length();
+        }
+        return false;
+    }
+
     // LocationBarData.Observer implementation
     // Using the default empty onSecurityStateChanged.
     // Using the default empty onTitleChanged.
@@ -707,23 +1051,11 @@
         updateButtonVisibility();
     }
 
-    private void updateUrl() {
-        setUrl(mLocationBarDataProvider.getCurrentUrl(), mLocationBarDataProvider.getUrlBarData());
-    }
-
-    private void updateOmniboxPrerender() {
-        if (mOmniboxPrerender == null) return;
-        // Profile may be null if switching to a tab that has not yet been initialized.
-        Profile profile = mProfileSupplier.get();
-        if (profile == null) return;
-        mOmniboxPrerender.clear(profile);
-    }
-
     // FakeboxDelegate implementation.
 
     @Override
     public void setUrlBarFocus(boolean shouldBeFocused, @Nullable String pastedText, int reason) {
-        boolean urlHasFocus = mLocationBarLayout.isUrlBarFocused();
+        boolean urlHasFocus = mUrlHasFocus;
         if (shouldBeFocused) {
             if (!urlHasFocus) recordOmniboxFocusReason(reason);
             if (reason == OmniboxFocusReason.FAKE_BOX_TAP
@@ -792,7 +1124,7 @@
 
     @Override
     public boolean isUrlBarFocused() {
-        return mLocationBarLayout.isUrlBarFocused();
+        return mUrlHasFocus;
     }
 
     @Override
@@ -819,12 +1151,6 @@
         updateMicButtonState();
     }
 
-    private void updateMicButtonState() {
-        mLocationBarLayout.setVoiceSearchEnabled(mVoiceRecognitionHandler != null
-                && mVoiceRecognitionHandler.isVoiceSearchEnabled());
-        updateButtonVisibility();
-    }
-
     @Override
     public void setSearchQuery(String query) {
         if (TextUtils.isEmpty(query)) return;
@@ -897,7 +1223,7 @@
     public boolean onKey(View view, int keyCode, KeyEvent event) {
         boolean result = handleKeyEvent(view, keyCode, event);
 
-        if (result && mLocationBarLayout.isUrlBarFocused() && mUrlFocusedWithoutAnimations
+        if (result && mUrlHasFocus && mUrlFocusedWithoutAnimations
                 && event.getAction() == KeyEvent.ACTION_DOWN && event.isPrintingKey()
                 && event.hasNoModifiers()) {
             handleUrlFocusAnimation(/*hasFocus=*/true);
@@ -906,43 +1232,11 @@
         return result;
     }
 
-    private boolean handleKeyEvent(View view, int keyCode, KeyEvent event) {
-        boolean isRtl = view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        if (mAutocompleteCoordinator.handleKeyEvent(keyCode, event)) {
-            return true;
-        } else if (keyCode == KeyEvent.KEYCODE_BACK) {
-            if (KeyNavigationUtil.isActionDown(event) && event.getRepeatCount() == 0) {
-                // Tell the framework to start tracking this event.
-                mLocationBarLayout.getKeyDispatcherState().startTracking(event, this);
-                return true;
-            } else if (KeyNavigationUtil.isActionUp(event)) {
-                mLocationBarLayout.getKeyDispatcherState().handleUpEvent(event);
-                if (event.isTracking() && !event.isCanceled()) {
-                    backKeyPressed();
-                    return true;
-                }
-            }
-        } else if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
-            if (KeyNavigationUtil.isActionDown(event) && event.getRepeatCount() == 0) {
-                revertChanges();
-                return true;
-            }
-        } else if ((!isRtl && KeyNavigationUtil.isGoRight(event))
-                || (isRtl && KeyNavigationUtil.isGoLeft(event))) {
-            // Ensures URL bar doesn't lose focus, when RIGHT or LEFT (RTL) key is pressed while
-            // the cursor is positioned at the end of the text.
-            TextView tv = (TextView) view;
-            return tv.getSelectionStart() == tv.getSelectionEnd()
-                    && tv.getSelectionEnd() == tv.getText().length();
-        }
-        return false;
-    }
-
     // ComponentCallbacks implementation.
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
-        if (mLocationBarLayout.isUrlBarFocused() && mUrlFocusedWithoutAnimations
+        if (mUrlHasFocus && mUrlFocusedWithoutAnimations
                 && newConfig.keyboard != Configuration.KEYBOARD_QWERTY) {
             // If we lose the hardware keyboard and the focus animations were not run, then the
             // user has not typed any text, so we will just clear the focus instead.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
index eb2c44fb9..6c0ccce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
@@ -87,28 +87,6 @@
         setShowIconsWhenUrlFocused(shouldShowSearchEngineLogo);
     }
 
-    /**
-     * Updates progress of current the URL focus change animation.
-     *
-     * @param fraction 1.0 is 100% focused, 0 is completely unfocused.
-     */
-    @Override
-    public void setUrlFocusChangeFraction(float fraction) {
-        super.setUrlFocusChangeFraction(fraction);
-
-        if (fraction > 0f) {
-            mUrlActionContainer.setVisibility(VISIBLE);
-        } else if (fraction == 0f && !isUrlFocusChangeInProgress()) {
-            // If a URL focus change is in progress, then it will handle setting the visibility
-            // correctly after it completes.  If done here, it would cause the URL to jump due
-            // to a badly timed layout call.
-            mUrlActionContainer.setVisibility(GONE);
-        }
-
-        updateButtonVisibility();
-        mStatusCoordinator.setUrlFocusChangePercent(fraction);
-    }
-
     @Override
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
         boolean needsCanvasRestore = false;
@@ -134,12 +112,6 @@
     }
 
     @Override
-    protected void updateButtonVisibility() {
-        super.updateButtonVisibility();
-        updateMicButtonVisibility();
-    }
-
-    @Override
     public void setShowIconsWhenUrlFocused(boolean showIcon) {
         super.setShowIconsWhenUrlFocused(showIcon);
         mFirstVisibleFocusedView = showIcon ? mStatusView : mUrlBar;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
index b475737..2e33211 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
@@ -4,28 +4,14 @@
 
 package org.chromium.chrome.browser.omnibox;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.Property;
 import android.view.MotionEvent;
 import android.view.View;
 
-import androidx.annotation.NonNull;
-
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.download.DownloadUtils;
-import org.chromium.chrome.browser.omnibox.status.StatusCoordinator;
-import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.ui.base.LocalizationUtils;
-import org.chromium.ui.interpolators.BakedBezierInterpolator;
-
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Location bar for tablet form factors.
@@ -34,34 +20,12 @@
     // The number of toolbar buttons that can be hidden at small widths (reload, back, forward).
     private static final int HIDEABLE_BUTTON_COUNT = 3;
 
-    private static final int ICON_FADE_ANIMATION_DURATION_MS = 150;
-    private static final int ICON_FADE_ANIMATION_DELAY_MS = 75;
-    private static final int WIDTH_CHANGE_ANIMATION_DURATION_MS = 225;
-    private static final int WIDTH_CHANGE_ANIMATION_DELAY_MS = 75;
-
-    private final Property<LocationBarTablet, Float> mWidthChangeFractionProperty =
-            new Property<LocationBarTablet, Float>(Float.class, "") {
-                @Override
-                public Float get(LocationBarTablet object) {
-                    return object.mWidthChangeFraction;
-                }
-
-                @Override
-                public void set(LocationBarTablet object, Float value) {
-                    setWidthChangeAnimationFraction(value);
-                }
-            };
-
     private View mLocationBarIcon;
     private View mBookmarkButton;
     private View mSaveOfflineButton;
     private View[] mTargets;
     private final Rect mCachedTargetBounds = new Rect();
 
-    // Whether the microphone and bookmark buttons should be shown in the location bar. These
-    // buttons are hidden if the window size is < 600dp.
-    private boolean mShouldShowButtonsWhenUnfocused;
-
     // Variables needed for animating the location bar and toolbar buttons hiding/showing.
     private final int mToolbarButtonsWidth;
     private final int mMicButtonWidth;
@@ -76,7 +40,6 @@
      */
     public LocationBarTablet(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mShouldShowButtonsWhenUnfocused = true;
 
         mToolbarButtonsWidth = getResources().getDimensionPixelOffset(R.dimen.toolbar_button_width)
                 * HIDEABLE_BUTTON_COUNT;
@@ -95,20 +58,6 @@
     }
 
     @Override
-    public void initialize(@NonNull AutocompleteCoordinator autocompleteCoordinator,
-            @NonNull UrlBarCoordinator urlCoordinator, @NonNull StatusCoordinator statusCoordinator,
-            @NonNull LocationBarDataProvider locationBarDataProvider,
-            @NonNull SearchEngineLogoUtils searchEngineLogoUtils) {
-        super.initialize(autocompleteCoordinator, urlCoordinator, statusCoordinator,
-                locationBarDataProvider, searchEngineLogoUtils);
-        mStatusCoordinator.setShowIconsWhenUrlFocused(true);
-        if (mSearchEngineLogoUtils.shouldShowSearchEngineLogo(
-                    mLocationBarDataProvider.isIncognito())) {
-            mStatusCoordinator.setStatusIconShown(true);
-        }
-    }
-
-    @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (mTargets == null) return true;
 
@@ -141,38 +90,6 @@
         return selectedTarget.onTouchEvent(event);
     }
 
-    /**
-     * Updates progress of current the URL focus change animation.
-     *
-     * @param fraction 1.0 is 100% focused, 0 is completely unfocused.
-     */
-    @Override
-    public void setUrlFocusChangeFraction(float fraction) {
-        super.setUrlFocusChangeFraction(fraction);
-        mLocationBarDataProvider.getNewTabPageDelegate().setUrlFocusChangeAnimationPercent(
-                fraction);
-    }
-
-    @Override
-    public void updateButtonVisibility() {
-        super.updateButtonVisibility();
-
-        boolean showBookmarkButton =
-                mShouldShowButtonsWhenUnfocused && shouldShowPageActionButtons();
-        mBookmarkButton.setVisibility(showBookmarkButton ? View.VISIBLE : View.GONE);
-
-        boolean showSaveOfflineButton =
-                mShouldShowButtonsWhenUnfocused && shouldShowSaveOfflineButton();
-        mSaveOfflineButton.setVisibility(showSaveOfflineButton ? View.VISIBLE : View.GONE);
-        if (showSaveOfflineButton) mSaveOfflineButton.setEnabled(isSaveOfflineButtonEnabled());
-
-        if (!mShouldShowButtonsWhenUnfocused) {
-            updateMicButtonVisibility();
-        } else {
-            mMicButton.setVisibility(shouldShowMicButton() ? View.VISIBLE : View.GONE);
-        }
-    }
-
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int measuredWidth = getMeasuredWidth();
@@ -196,156 +113,6 @@
         }
     }
 
-    /**
-     * @param shouldShowButtons Whether buttons should be displayed in the URL bar when it's not
-     *                          focused.
-     */
-    public void setShouldShowButtonsWhenUnfocused(boolean shouldShowButtons) {
-        mShouldShowButtonsWhenUnfocused = shouldShowButtons;
-        updateButtonVisibility();
-    }
-
-    /**
-     * @param button The {@link View} of the button to show.
-     * @return An animator to run for the given view when showing buttons in the unfocused location
-     *         bar. This should also be used to create animators for showing toolbar buttons.
-     */
-    public ObjectAnimator createShowButtonAnimator(View button) {
-        if (button.getVisibility() != View.VISIBLE) {
-            button.setAlpha(0.f);
-        }
-        ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(button, View.ALPHA, 1.f);
-        buttonAnimator.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
-        buttonAnimator.setStartDelay(ICON_FADE_ANIMATION_DELAY_MS);
-        buttonAnimator.setDuration(ICON_FADE_ANIMATION_DURATION_MS);
-        return buttonAnimator;
-    }
-
-    /**
-     * @param button The {@link View} of the button to hide.
-     * @return An animator to run for the given view when hiding buttons in the unfocused location
-     *         bar. This should also be used to create animators for hiding toolbar buttons.
-     */
-    public ObjectAnimator createHideButtonAnimator(View button) {
-        ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(button, View.ALPHA, 0.f);
-        buttonAnimator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
-        buttonAnimator.setDuration(ICON_FADE_ANIMATION_DURATION_MS);
-        return buttonAnimator;
-    }
-
-    /**
-     * Creates animators for showing buttons in the unfocused location bar. The buttons fade in
-     * while width of the location bar gets smaller. There are toolbar buttons that also show at
-     * the same time, causing the width of the location bar to change.
-     *
-     * @param toolbarStartPaddingDifference The difference in the toolbar's start padding between
-     *                                      the beginning and end of the animation.
-     * @return An ArrayList of animators to run.
-     */
-    public List<Animator> getShowButtonsWhenUnfocusedAnimators(int toolbarStartPaddingDifference) {
-        mToolbarStartPaddingDifference = toolbarStartPaddingDifference;
-
-        ArrayList<Animator> animators = new ArrayList<>();
-
-        Animator widthChangeAnimator =
-                ObjectAnimator.ofFloat(this, mWidthChangeFractionProperty, 0f);
-        widthChangeAnimator.setDuration(WIDTH_CHANGE_ANIMATION_DURATION_MS);
-        widthChangeAnimator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
-        widthChangeAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mAnimatingWidthChange = true;
-                setShouldShowButtonsWhenUnfocused(true);
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                // Only reset values if the animation is ending because it's completely finished
-                // and not because it was canceled.
-                if (mWidthChangeFraction == 0.f) {
-                    mAnimatingWidthChange = false;
-                    resetValuesAfterAnimation();
-                }
-            }
-        });
-        animators.add(widthChangeAnimator);
-
-        // When buttons show in the unfocused location bar, either the delete button or bookmark
-        // button will be showing. If the delete button is currently showing, the bookmark button
-        // should not fade in.
-        if (mDeleteButton.getVisibility() != View.VISIBLE) {
-            animators.add(createShowButtonAnimator(mBookmarkButton));
-        }
-
-        if (shouldShowSaveOfflineButton()) {
-            animators.add(createShowButtonAnimator(mSaveOfflineButton));
-        } else if (mMicButton.getVisibility() != View.VISIBLE || mMicButton.getAlpha() != 1.f) {
-            // If the microphone button is already fully visible, don't animate its appearance.
-            animators.add(createShowButtonAnimator(mMicButton));
-        }
-
-        return animators;
-    }
-
-    /**
-     * Creates animators for hiding buttons in the unfocused location bar. The buttons fade out
-     * while width of the location bar gets larger. There are toolbar buttons that also hide at the
-     * same time, causing the width of the location bar to change.
-     *
-     * @param toolbarStartPaddingDifference The difference in the toolbar's start padding between
-     *                                      the beginning and end of the animation.
-     * @return An ArrayList of animators to run.
-     */
-    public List<Animator> getHideButtonsWhenUnfocusedAnimators(int toolbarStartPaddingDifference) {
-        mToolbarStartPaddingDifference = toolbarStartPaddingDifference;
-
-        ArrayList<Animator> animators = new ArrayList<>();
-
-        Animator widthChangeAnimator =
-                ObjectAnimator.ofFloat(this, mWidthChangeFractionProperty, 1f);
-        widthChangeAnimator.setStartDelay(WIDTH_CHANGE_ANIMATION_DELAY_MS);
-        widthChangeAnimator.setDuration(WIDTH_CHANGE_ANIMATION_DURATION_MS);
-        widthChangeAnimator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
-        widthChangeAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mAnimatingWidthChange = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                // Only reset values if the animation is ending because it's completely finished
-                // and not because it was canceled.
-                if (mWidthChangeFraction == 1.f) {
-                    mAnimatingWidthChange = false;
-                    resetValuesAfterAnimation();
-                    setShouldShowButtonsWhenUnfocused(false);
-                }
-            }
-        });
-        animators.add(widthChangeAnimator);
-
-        // When buttons show in the unfocused location bar, either the delete button or bookmark
-        // button will be showing. If the delete button is currently showing, the bookmark button
-        // should not fade out.
-        if (mDeleteButton.getVisibility() != View.VISIBLE) {
-            animators.add(createHideButtonAnimator(mBookmarkButton));
-        }
-
-        if (shouldShowSaveOfflineButton() && mSaveOfflineButton.getVisibility() == View.VISIBLE) {
-            animators.add(createHideButtonAnimator(mSaveOfflineButton));
-        } else if (!(mUrlBar.hasFocus() && mDeleteButton.getVisibility() != View.VISIBLE)) {
-            // If the save offline button isn't enabled, the microphone button always shows when
-            // buttons are shown in the unfocused location bar. When buttons are hidden in the
-            // unfocused location bar, the microphone shows if the location bar is focused and the
-            // delete button isn't showing. The microphone button should not be hidden if the
-            // url bar is currently focused and the delete button isn't showing.
-            animators.add(createHideButtonAnimator(mMicButton));
-        }
-
-        return animators;
-    }
-
     /** Returns amount by which to adjust to move value inside the given range. */
     private static float distanceToRange(float min, float max, float value) {
         return value < min ? (min - value) : value > max ? (max - value) : 0;
@@ -355,7 +122,7 @@
      * Resets the alpha and translation X for all views affected by the animations for showing or
      * hiding buttons.
      */
-    private void resetValuesAfterAnimation() {
+    /* package */ void resetValuesAfterAnimation() {
         mMicButton.setTranslationX(0);
         mDeleteButton.setTranslationX(0);
         mBookmarkButton.setTranslationX(0);
@@ -376,7 +143,7 @@
      *         buttons fully visible) and 1.f represents the expanded width (toolbar buttons fully
      *         hidden).
      */
-    private void setWidthChangeAnimationFraction(float fraction) {
+    /* package */ void setWidthChangeAnimationFraction(float fraction) {
         mWidthChangeFraction = fraction;
 
         float offset = (mToolbarButtonsWidth + mToolbarStartPaddingDifference) * fraction;
@@ -400,6 +167,10 @@
         setChildTranslationsForWidthChangeAnimation((int) offset, deleteOffset);
     }
 
+    /* package */ float getWidthChangeFraction() {
+        return mWidthChangeFraction;
+    }
+
     /**
      * Sets the translation X values for child views during the width change animation. This
      * compensates for the change to the left/right position of the location bar and ensures child
@@ -441,32 +212,66 @@
         }
     }
 
-    private boolean shouldShowSaveOfflineButton() {
-        if (!mNativeInitialized || mLocationBarDataProvider == null) return false;
-        Tab tab = mLocationBarDataProvider.getTab();
-        if (tab == null) return false;
-        // The save offline button should not be shown on native pages. Currently, trying to
-        // save an offline page in incognito crashes, so don't show it on incognito either.
-        return shouldShowPageActionButtons() && !tab.isIncognito();
+    /* package */ void setBookmarkButtonVisibility(boolean showBookmarkButton) {
+        mBookmarkButton.setVisibility(showBookmarkButton ? View.VISIBLE : View.GONE);
     }
 
-    private boolean isSaveOfflineButtonEnabled() {
-        if (mLocationBarDataProvider == null) return false;
-        return DownloadUtils.isAllowedToDownloadPage(mLocationBarDataProvider.getTab());
+    /* package */ void setSaveOfflineButtonVisibility(
+            boolean showSaveOfflineButton, boolean isSaveOfflineButtonEnabled) {
+        mSaveOfflineButton.setVisibility(showSaveOfflineButton ? View.VISIBLE : View.GONE);
+        if (showSaveOfflineButton) mSaveOfflineButton.setEnabled(isSaveOfflineButtonEnabled);
     }
 
-    private boolean shouldShowPageActionButtons() {
-        if (!mNativeInitialized) return true;
-
-        // There are two actions, bookmark and save offline, and they should be shown if the
-        // omnibox isn't focused.
-        return !(mUrlBar.hasFocus() || isUrlFocusChangeInProgress());
+    /* package */ boolean isSaveOfflineButtonVisible() {
+        return mSaveOfflineButton.getVisibility() == VISIBLE;
     }
 
-    private boolean shouldShowMicButton() {
-        // If the download UI is enabled, the mic button should be only be shown when the url bar
-        // is focused.
-        return mVoiceSearchEnabled && mNativeInitialized
-                && (mUrlBar.hasFocus() || isUrlFocusChangeInProgress());
+    /* package */ boolean isDeleteButtonVisible() {
+        return mDeleteButton.getVisibility() == VISIBLE;
+    }
+
+    /* package */ boolean isMicButtonVisible() {
+        return mMicButton.getVisibility() == VISIBLE;
+    }
+
+    /* package */ float getMicButtonAlpha() {
+        return mMicButton.getAlpha();
+    }
+
+    /**
+     * Gets the bookmark button view for the purposes of creating an animator that targets it.
+     * Don't use this for any other reason, e.g. to access or modify the view's properties directly.
+     */
+    @Deprecated
+    /* package */ View getBookmarkButtonForAnimation() {
+        return mBookmarkButton;
+    }
+
+    /**
+     * Gets the save offline button view for the purposes of creating an animator that targets it.
+     * Don't use this for any other reason, e.g. to access or modify the view's properties directly.
+     */
+    @Deprecated
+    /* package */ View getSaveOfflineButtonForAnimation() {
+        return mSaveOfflineButton;
+    }
+
+    /**
+     * Gets the mic button view for the purposes of creating an animator that targets it. Don't use
+     * this for any other reason, e.g. to access or modify the view's properties directly.
+     */
+    @Deprecated
+    /* package */ View getMicButtonForAnimation() {
+        return mMicButton;
+    }
+
+    /* package */ void startAnimatingWidthChange(int toolbarStartPaddingDifference) {
+        mAnimatingWidthChange = true;
+        mToolbarStartPaddingDifference = toolbarStartPaddingDifference;
+    }
+
+    /* package */ void finishAnimatingWidthChange() {
+        mAnimatingWidthChange = false;
+        mToolbarStartPaddingDifference = 0;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index aee6158d..62ee847 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -182,6 +182,7 @@
                 },
                 /*backKeyBehavior=*/this, SearchEngineLogoUtils.getInstance());
         mLocationBarCoordinator.setUrlBarFocusable(true);
+        mLocationBarCoordinator.setShouldShowMicButtonWhenUnfocused(true);
         mLocationBarCoordinator.getFakeboxDelegate().addUrlFocusChangeListener(this);
 
         // Kick off everything needed for the user to type into the box.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
index 4989c67..7cf6444 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
@@ -37,8 +37,6 @@
     public SearchActivityLocationBarLayout(Context context, AttributeSet attrs) {
         super(context, attrs, R.layout.location_bar_base);
         setBackground(ToolbarPhone.createModernLocationBarBackground(getResources()));
-        setShouldShowMicButtonWhenUnfocused(true);
-
     }
 
     @Override
@@ -51,6 +49,7 @@
         mPendingSearchPromoDecision = LocaleManager.getInstance().needToCheckForSearchEnginePromo();
         getAutocompleteCoordinator().setShouldPreventOmniboxAutocomplete(
                 mPendingSearchPromoDecision);
+        findViewById(R.id.url_action_container).setVisibility(View.VISIBLE);
     }
 
     @Override
@@ -122,13 +121,6 @@
         }
     }
 
-    @Override
-    protected void updateButtonVisibility() {
-        super.updateButtonVisibility();
-        updateMicButtonVisibility();
-        findViewById(R.id.url_action_container).setVisibility(View.VISIBLE);
-    }
-
     // TODO(tedchoc): Investigate focusing regardless of the search promo state and just ensure
     //                we don't start processing non-cached suggestion requests until that state
     //                is finalized after native has been initialized.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerService.java b/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerService.java
index f85b000..f114cc32 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerService.java
@@ -4,10 +4,16 @@
 
 package org.chromium.chrome.browser.services.gcm;
 
+import android.annotation.SuppressLint;
+
 import org.chromium.chrome.browser.base.SplitCompatGcmListenerService;
 import org.chromium.chrome.browser.base.SplitCompatUtils;
 
-/** See {@link ChromeGcmListenerServiceImpl}. */
+/**
+ * See {@link ChromeGcmListenerServiceImpl}.
+ * Suppressing linting as onNewToken() is implemented in base class.
+ */
+@SuppressLint("MissingFirebaseInstanceTokenRefresh")
 public class ChromeGcmListenerService extends SplitCompatGcmListenerService {
     public ChromeGcmListenerService() {
         super(SplitCompatUtils.getIdentifierName(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerServiceImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerServiceImpl.java
index e6a19988..4abcd6d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerServiceImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerServiceImpl.java
@@ -60,14 +60,14 @@
 
     @Override
     public void onMessageSent(String msgId) {
-        Log.d(TAG, "Message sent successfully. Message id: " + msgId);
+        Log.d(TAG, "Message sent successfully. Message id: %s", msgId);
         GcmUma.recordGcmUpstreamHistogram(
                 ContextUtils.getApplicationContext(), GcmUma.UMA_UPSTREAM_SUCCESS);
     }
 
     @Override
-    public void onSendError(String msgId, String error) {
-        Log.w(TAG, "Error in sending message. Message id: " + msgId + " Error: " + error);
+    public void onSendError(String msgId, Exception error) {
+        Log.w(TAG, "Error in sending message. Message id: %s", msgId, error);
         GcmUma.recordGcmUpstreamHistogram(
                 ContextUtils.getApplicationContext(), GcmUma.UMA_UPSTREAM_SEND_FAILED);
     }
@@ -81,6 +81,14 @@
         GcmUma.recordDeletedMessages(ContextUtils.getApplicationContext());
     }
 
+    @Override
+    public void onNewToken(String token) {
+        // TODO(crbug.com/1138706): Figure out if we can use this method or if
+        // we need another mechanism that supports multiple FirebaseApp
+        // instances.
+        Log.d(TAG, "New FCM Token: %s", token);
+    }
+
     /**
      * Returns if we deliver the GCMMessage with a background service by calling
      * Context#startService. This will only work if Android has put us in a whitelist to allow
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index c6c6402e..cfb5d65a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -406,7 +406,7 @@
 
     @Override
     void updateButtonVisibility() {
-        mLocationBar.getTabletCoordinator().updateButtonVisibility();
+        mLocationBar.updateButtonVisibility();
     }
 
     @Override
@@ -605,7 +605,7 @@
             for (ImageButton button : mToolbarButtons) {
                 button.setVisibility(visible ? View.VISIBLE : View.GONE);
             }
-            mLocationBar.getTabletCoordinator().setShouldShowButtonsWhenUnfocused(visible);
+            mLocationBar.setShouldShowButtonsWhenUnfocusedForTablet(visible);
             setStartPaddingBasedOnButtonVisibility(visible);
         }
     }
@@ -646,11 +646,11 @@
 
         // Create animators for all of the toolbar buttons.
         for (ImageButton button : mToolbarButtons) {
-            animators.add(mLocationBar.getTabletCoordinator().createShowButtonAnimator(button));
+            animators.add(mLocationBar.createShowButtonAnimatorForTablet(button));
         }
 
         // Add animators for location bar.
-        animators.addAll(mLocationBar.getTabletCoordinator().getShowButtonsWhenUnfocusedAnimators(
+        animators.addAll(mLocationBar.getShowButtonsWhenUnfocusedAnimatorsForTablet(
                 getStartPaddingDifferenceForButtonVisibilityAnimation()));
 
         AnimatorSet set = new AnimatorSet();
@@ -681,11 +681,11 @@
 
         // Create animators for all of the toolbar buttons.
         for (ImageButton button : mToolbarButtons) {
-            animators.add(mLocationBar.getTabletCoordinator().createHideButtonAnimator(button));
+            animators.add(mLocationBar.createHideButtonAnimatorForTablet(button));
         }
 
         // Add animators for location bar.
-        animators.addAll(mLocationBar.getTabletCoordinator().getHideButtonsWhenUnfocusedAnimators(
+        animators.addAll(mLocationBar.getHideButtonsWhenUnfocusedAnimatorsForTablet(
                 getStartPaddingDifferenceForButtonVisibilityAnimation()));
 
         AnimatorSet set = new AnimatorSet();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java
index 52d11c9..62cb098 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java
@@ -17,7 +17,6 @@
 import org.chromium.base.test.UiThreadTest;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.RequiresRestart;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -39,6 +38,7 @@
  */
 @RunWith(BaseJUnit4ClassRunner.class)
 @Batch(Batch.PER_CLASS)
+@Batch.SplitByFeature
 public class BookmarkBridgeTest {
     @Rule
     public final ChromeBrowserTestRule mChromeBrowserTestRule = new ChromeBrowserTestRule();
@@ -332,7 +332,6 @@
     @SmallTest
     @UiThreadTest
     @Features.EnableFeatures({ChromeFeatureList.READ_LATER})
-    @RequiresRestart
     public void testAddToReadingList() {
         Assert.assertNull("Should return null for non http/https URLs.",
                 mBookmarkBridge.addToReadingList("a", new GURL("chrome://flags")));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
index 33f24a4..4038fd1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
@@ -505,7 +505,7 @@
             locationBarMediator.setUrlBarFocus(
                     true, SEARCH_TERMS_URL, OmniboxFocusReason.FAKE_BOX_LONG_PRESS);
         });
-        Assert.assertTrue(locationBar.isUrlBarFocused());
+        Assert.assertTrue(getLocationBarMediator().isUrlBarFocused());
         Assert.assertTrue(getLocationBarMediator().didFocusUrlFromFakebox());
         Assert.assertEquals(SEARCH_TERMS_URL, getUrlText(getUrlBar()));
         Assert.assertEquals(
@@ -514,7 +514,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             locationBarMediator.setUrlBarFocus(true, SEARCH_TERMS, OmniboxFocusReason.SEARCH_QUERY);
         });
-        Assert.assertTrue(locationBar.isUrlBarFocused());
+        Assert.assertTrue(getLocationBarMediator().isUrlBarFocused());
         Assert.assertTrue(getLocationBarMediator().didFocusUrlFromFakebox());
         Assert.assertEquals(SEARCH_TERMS, getUrlText(getUrlBar()));
         Assert.assertEquals(
@@ -523,7 +523,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             locationBarMediator.setUrlBarFocus(false, null, OmniboxFocusReason.UNFOCUS);
         });
-        Assert.assertFalse(locationBar.isUrlBarFocused());
+        Assert.assertFalse(getLocationBarMediator().isUrlBarFocused());
         Assert.assertFalse(getLocationBarMediator().didFocusUrlFromFakebox());
         Assert.assertEquals(
                 1, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
@@ -531,7 +531,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             locationBarMediator.setUrlBarFocus(true, null, OmniboxFocusReason.OMNIBOX_TAP);
         });
-        Assert.assertTrue(locationBar.isUrlBarFocused());
+        Assert.assertTrue(getLocationBarMediator().isUrlBarFocused());
         Assert.assertFalse(getLocationBarMediator().didFocusUrlFromFakebox());
         Assert.assertEquals(
                 2, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
index dd1259b9..3a42944 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
@@ -40,6 +40,7 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.locale.LocaleManager;
+import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -81,6 +82,8 @@
     private TemplateUrl mNonGoogleSearchEngine;
     @Mock
     private LocaleManager mLocaleManager;
+    @Mock
+    VoiceRecognitionHandler mVoiceRecognitionHandler;
 
     ChromeTabbedActivity mActivity;
     UrlBar mUrlBar;
@@ -144,6 +147,7 @@
                                            .getLocationBar());
         mLocationBarMediator = mLocationBarCoordinator.getMediatorForTesting();
         mSearchUrl = mActivityTestRule.getEmbeddedTestServerRule().getServer().getURL("/search");
+        mLocationBarCoordinator.setVoiceRecognitionHandlerForTesting(mVoiceRecognitionHandler);
     }
 
     @Test
@@ -257,15 +261,13 @@
     @MediumTest
     public void testPostDestroyFocusLogic() {
         startActivityNormally();
-        LocationBarLayout locationBarLayout =
-                mActivity.findViewById(org.chromium.chrome.R.id.location_bar);
         TestThreadUtils.runOnUiThreadBlocking(() -> { mActivity.finish(); });
 
         CriteriaHelper.pollUiThread(
                 () -> mActivity.getLifecycle().getCurrentState().equals(Lifecycle.State.DESTROYED));
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            locationBarLayout.setUrlFocusChangeInProgress(false);
+            mLocationBarMediator.setUrlFocusChangeInProgress(false);
             mLocationBarMediator.finishUrlFocusChange(true, true);
         });
     }
@@ -294,6 +296,7 @@
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
     public void testFocusLogic_buttonVisibilityPhone() {
         startActivityNormally();
+        doReturn(true).when(mVoiceRecognitionHandler).isVoiceSearchEnabled();
         String url = mActivityTestRule.getEmbeddedTestServerRule().getServer().getURLWithHostName(
                 HOSTNAME, "/");
         mActivityTestRule.loadUrl(url);
@@ -363,9 +366,11 @@
         onView(withId(R.id.delete_button))
                 .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
 
-        TestThreadUtils.runOnUiThreadBlocking(() -> { mUrlBar.clearFocus(); });
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mUrlBar.clearFocus();
+            mLocationBarCoordinator.setShouldShowButtonsWhenUnfocusedForTablet(false);
+        });
 
-        mLocationBarCoordinator.getTabletCoordinator().setShouldShowButtonsWhenUnfocused(false);
         onView(withId(R.id.bookmark_button))
                 .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)));
         onView(withId(R.id.save_offline_button))
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
index 903f192..f6f3c65 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
@@ -34,7 +34,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.Log;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
@@ -116,14 +115,23 @@
     }
 
     private void openPageInfo() {
-        for (int i = 0; i < 5; i++) {
-            try {
-                onViewWaiting(allOf(withId(R.id.location_bar_status_icon), isDisplayed()));
-            } catch (AssertionError e) {
-                Log.e(TAG, "Lock icon hasn't shown up yet...");
-            }
+        Tab tab = mActivityTestRule.getActivity().getActivityTab();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            PageInfoController.show(mActivityTestRule.getActivity(), tab.getWebContents(), null,
+                    PageInfoController.OpenedFromSource.TOOLBAR,
+                    new ChromePageInfoControllerDelegate(mActivityTestRule.getActivity(),
+                            tab.getWebContents(),
+                            mActivityTestRule.getActivity().getModalDialogManagerSupplier(),
+                            /*offlinePageLoadUrlDelegate=*/
+                            new OfflinePageUtils.TabOfflinePageLoadUrlDelegate(tab)),
+                    new ChromePermissionParamsListBuilderDelegate());
+        });
+
+        if (PageInfoFeatureList.isEnabled(PageInfoFeatureList.PAGE_INFO_V2)) {
+            onViewWaiting(allOf(withId(R.id.page_info_url_wrapper), isDisplayed()));
+        } else {
+            onViewWaiting(allOf(withId(R.id.page_info_url), isDisplayed()));
         }
-        onView(withId(R.id.location_bar_status_icon)).perform(click());
     }
 
     private View getPageInfoView() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
index f7c7dff..feae7ac 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java
@@ -291,6 +291,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug/1171794")
     public void testTypeBeforeNativeIsLoaded() throws Exception {
         // Wait for the activity to load, but don't let it load the native library.
         mTestDelegate.shouldDelayLoadingNative = true;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
index ec885eb..0a05504 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
@@ -17,6 +17,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -55,9 +56,9 @@
 import org.chromium.base.BuildConfig;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplier;
-import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.gsa.GSAState;
 import org.chromium.chrome.browser.locale.LocaleManager;
@@ -66,6 +67,7 @@
 import org.chromium.chrome.browser.omnibox.status.StatusCoordinator;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
 import org.chromium.chrome.browser.omnibox.voice.AssistantVoiceSearchService;
+import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileJni;
@@ -89,7 +91,8 @@
 @Config(shadows = {LocationBarMediatorTest.ShadowUrlUtilities.class,
                 LocationBarMediatorTest.ShadowGeolocationHeader.class,
                 LocationBarMediatorTest.ObjectAnimatorShadow.class,
-                LocationBarMediatorTest.GSAStateShadow.class})
+                LocationBarMediatorTest.GSAStateShadow.class,
+                LocationBarMediatorTest.DownloadUtilsShadow.class})
 @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_ASSISTANT_VOICE_SEARCH)
 public class LocationBarMediatorTest {
     @Implements(UrlUtilities.class)
@@ -135,6 +138,14 @@
         }
     }
 
+    @Implements(DownloadUtils.class)
+    static class DownloadUtilsShadow {
+        @Implementation
+        public static boolean isAllowedToDownloadPage(Tab tab) {
+            return true;
+        }
+    }
+
     private static final String TEST_URL = "http://www.example.org";
 
     private static int sGeoHeaderPrimeCount;
@@ -149,12 +160,12 @@
     @Mock
     private LocationBarLayout mLocationBarLayout;
     @Mock
+    private LocationBarTablet mLocationBarTablet;
+    @Mock
     private TemplateUrlService mTemplateUrlService;
     @Mock
     private LocationBarDataProvider mLocationBarDataProvider;
     @Mock
-    private OneshotSupplierImpl<AssistantVoiceSearchService> mAssistantVoiceSearchSupplier;
-    @Mock
     private OverrideUrlLoadingDelegate mOverrideUrlLoadingDelegate;
     @Mock
     private LocaleManager mLocaleManager;
@@ -204,22 +215,35 @@
 
     private ObservableSupplierImpl<Profile> mProfileSupplier = new ObservableSupplierImpl<>();
     private LocationBarMediator mMediator;
+    private LocationBarMediator mTabletMediator;
+    private UrlBarData mUrlBarData;
 
     @Before
     public void setUp() {
+        mUrlBarData = UrlBarData.create(null, "text", 0, 0, "text");
+        doReturn(mUrlBarData).when(mLocationBarDataProvider).getUrlBarData();
         doReturn(mTemplateUrlService).when(mTemplateUrlServiceSupplier).get();
         doReturn(mRootView).when(mLocationBarLayout).getRootView();
+        doReturn(mRootView).when(mLocationBarTablet).getRootView();
         mJniMocker.mock(ProfileJni.TEST_HOOKS, mProfileNativesJniMock);
         mJniMocker.mock(OmniboxPrerenderJni.TEST_HOOKS, mPrerenderJni);
         SearchEngineLogoUtils.setInstanceForTesting(mSearchEngineLogoUtils);
-        mMediator = new LocationBarMediator(/* context= */ RuntimeEnvironment.application,
+        mMediator = new LocationBarMediator(/*context=*/RuntimeEnvironment.application,
                 mLocationBarLayout, mLocationBarDataProvider, mProfileSupplier,
                 mPrivacyPreferencesManager, mOverrideUrlLoadingDelegate, mLocaleManager,
                 mTemplateUrlServiceSupplier, mOverrideBackKeyBehaviorDelegate, mWindowAndroid,
-                false, mSearchEngineLogoUtils);
+                /*isTablet=*/false, mSearchEngineLogoUtils);
         mMediator.setCoordinators(mUrlCoordinator, mAutocompleteCoordinator, mStatusCoordinator);
         ObjectAnimatorShadow.setUrlAnimator(mUrlAnimator);
         GSAStateShadow.setGSAState(mGSAState);
+
+        mTabletMediator = new LocationBarMediator(RuntimeEnvironment.application,
+                mLocationBarTablet, mLocationBarDataProvider, mProfileSupplier,
+                mPrivacyPreferencesManager, mOverrideUrlLoadingDelegate, mLocaleManager,
+                mTemplateUrlServiceSupplier, mOverrideBackKeyBehaviorDelegate, mWindowAndroid,
+                /*isTablet=*/true, mSearchEngineLogoUtils);
+        mTabletMediator.setCoordinators(
+                mUrlCoordinator, mAutocompleteCoordinator, mStatusCoordinator);
     }
 
     @Test
@@ -250,7 +274,7 @@
 
     @Test
     public void testRevertChanges_focused() {
-        doReturn(true).when(mLocationBarLayout).isUrlBarFocused();
+        mMediator.onUrlFocusChange(true);
         UrlBarData urlBarData = mock(UrlBarData.class);
         doReturn(urlBarData).when(mLocationBarDataProvider).getUrlBarData();
         mMediator.revertChanges();
@@ -261,7 +285,7 @@
     @Test
     public void testRevertChanges_focusedNativePage() {
         doReturn(UrlConstants.NTP_URL).when(mLocationBarDataProvider).getCurrentUrl();
-        doReturn(true).when(mLocationBarLayout).isUrlBarFocused();
+        mMediator.onUrlFocusChange(true);
         mMediator.revertChanges();
         verify(mUrlCoordinator)
                 .setUrlBarData(UrlBarData.EMPTY, UrlBar.ScrollType.SCROLL_TO_BEGINNING,
@@ -302,7 +326,7 @@
         doReturn("text").when(mUrlCoordinator).getTextWithoutAutocomplete();
         doReturn(true).when(mUrlCoordinator).shouldAutocomplete();
         mMediator.setIsUrlBarFocusedWithoutAnimationsForTesting(true);
-        doReturn(true).when(mLocationBarLayout).isUrlBarFocused();
+        mMediator.onUrlFocusChange(true);
 
         mMediator.onSuggestionsChanged("textWithAutocomplete", true);
         verify(mPrerenderJni)
@@ -369,7 +393,7 @@
 
         doReturn(null).when(mLocationBarDataProvider).getTab();
         assertNull(mMediator.getViewForUrlBackFocus());
-        verify(mLocationBarDataProvider, times(2)).getTab();
+        verify(mLocationBarDataProvider, times(3)).getTab();
         verify(mTab, times(1)).getView();
     }
 
@@ -467,7 +491,7 @@
 
     @Test
     public void testOnConfigurationChanged_qwertyKeyboard() {
-        doReturn(true).when(mLocationBarLayout).isUrlBarFocused();
+        mMediator.onUrlFocusChange(true);
         mMediator.setIsUrlBarFocusedWithoutAnimationsForTesting(true);
         Configuration newConfig = new Configuration();
         newConfig.keyboard = Configuration.KEYBOARD_QWERTY;
@@ -478,13 +502,12 @@
 
     @Test
     public void testOnConfigurationChanged_nonQwertyKeyboard() {
-        doReturn(false).when(mLocationBarLayout).isUrlBarFocused();
         Configuration newConfig = new Configuration();
         newConfig.keyboard = Configuration.KEYBOARD_NOKEYS;
         mMediator.onConfigurationChanged(newConfig);
         verify(mUrlCoordinator, never()).clearFocus();
 
-        doReturn(true).when(mLocationBarLayout).isUrlBarFocused();
+        mMediator.onUrlFocusChange(true);
         mMediator.onConfigurationChanged(newConfig);
         verify(mUrlCoordinator, never()).clearFocus();
 
@@ -570,10 +593,10 @@
         doReturn(true).when(mAutocompleteCoordinator).handleKeyEvent(KeyEvent.KEYCODE_9, mKeyEvent);
         doReturn(true).when(mKeyEvent).isPrintingKey();
         doReturn(true).when(mKeyEvent).hasNoModifiers();
-        doReturn(true).when(mLocationBarLayout).isUrlBarFocused();
+        mMediator.onUrlFocusChange(true);
         mMediator.setIsUrlBarFocusedWithoutAnimationsForTesting(true);
         assertTrue(mMediator.onKey(mView, KeyEvent.KEYCODE_9, mKeyEvent));
-        verify(mUrlCoordinator).onUrlFocusChange(true);
+        verify(mUrlCoordinator, times(2)).onUrlFocusChange(true);
     }
 
     @Test
@@ -696,7 +719,7 @@
 
     @Test
     public void testSetUrlBarFocus_triggersObservers() {
-        doReturn(true).when(mLocationBarLayout).isUrlBarFocused();
+        mMediator.onUrlFocusChange(true);
         mMediator.addUrlFocusChangeListener(mUrlCoordinator);
         mMediator.addUrlFocusChangeListener(mAutocompleteCoordinator);
         mMediator.setIsUrlBarFocusedWithoutAnimationsForTesting(true);
@@ -728,11 +751,10 @@
         mMediator.addUrlFocusChangeListener(mUrlCoordinator);
         UrlBarData urlBarData = UrlBarData.create(null, "text", 0, 0, "text");
         doReturn(urlBarData).when(mLocationBarDataProvider).getUrlBarData();
-        doReturn(true).when(mLocationBarLayout).isUrlBarFocused();
 
         mMediator.onUrlFocusChange(true);
 
-        verify(mLocationBarLayout).setUrlHasFocus(true);
+        assertTrue(mMediator.isUrlBarFocused());
         verify(mStatusCoordinator).setShouldAnimateIconChanges(true);
         verify(mUrlCoordinator)
                 .setUrlBarData(urlBarData, UrlBar.ScrollType.NO_SCROLL, SelectionState.SELECT_ALL);
@@ -778,16 +800,19 @@
     }
 
     @Test
-    public void testOnUrlFocusChange_notFocused() {
-        mMediator.addUrlFocusChangeListener(mUrlCoordinator);
+    public void testOnUrlFocusChange_notFocusedTablet() {
+        NewTabPageDelegate newTabPageDelegate = mock(NewTabPageDelegate.class);
+        doReturn(newTabPageDelegate).when(mLocationBarDataProvider).getNewTabPageDelegate();
+        mTabletMediator.addUrlFocusChangeListener(mUrlCoordinator);
         doReturn(true).when(mLocationBarDataProvider).hasTab();
         UrlBarData urlBarData = UrlBarData.create(null, "text", 0, 0, "text");
         doReturn(urlBarData).when(mLocationBarDataProvider).getUrlBarData();
+        mTabletMediator.onUrlFocusChange(true);
         Mockito.reset(mStatusCoordinator);
 
-        mMediator.onUrlFocusChange(false);
+        mTabletMediator.onUrlFocusChange(false);
 
-        verify(mLocationBarLayout).setUrlHasFocus(false);
+        assertFalse(mTabletMediator.isUrlBarFocused());
         verify(mStatusCoordinator).setShouldAnimateIconChanges(false);
         verify(mStatusCoordinator).onUrlFocusChange(false);
         verify(mUrlCoordinator).onUrlFocusChange(false);
@@ -807,16 +832,8 @@
                 .when(mRootView)
                 .getLocalVisibleRect(any());
 
-        LocationBarMediator tabletMediator = new LocationBarMediator(RuntimeEnvironment.application,
-                mLocationBarLayout, mLocationBarDataProvider, mProfileSupplier,
-                mPrivacyPreferencesManager, mOverrideUrlLoadingDelegate, mLocaleManager,
-                mTemplateUrlServiceSupplier, mOverrideBackKeyBehaviorDelegate, mWindowAndroid, true,
-                mSearchEngineLogoUtils);
-        tabletMediator.setCoordinators(
-                mUrlCoordinator, mAutocompleteCoordinator, mStatusCoordinator);
-        tabletMediator.addUrlFocusChangeListener(mUrlCoordinator);
-
-        tabletMediator.handleUrlFocusAnimation(true);
+        mTabletMediator.addUrlFocusChangeListener(mUrlCoordinator);
+        mTabletMediator.handleUrlFocusAnimation(true);
 
         verify(mUrlCoordinator).onUrlFocusChange(true);
         verify(mUrlAnimator).start();
@@ -830,16 +847,8 @@
         doReturn(true).when(newTabPageDelegate).isCurrentlyVisible();
         doReturn(newTabPageDelegate).when(mLocationBarDataProvider).getNewTabPageDelegate();
 
-        LocationBarMediator tabletMediator = new LocationBarMediator(RuntimeEnvironment.application,
-                mLocationBarLayout, mLocationBarDataProvider, mProfileSupplier,
-                mPrivacyPreferencesManager, mOverrideUrlLoadingDelegate, mLocaleManager,
-                mTemplateUrlServiceSupplier, mOverrideBackKeyBehaviorDelegate, mWindowAndroid, true,
-                mSearchEngineLogoUtils);
-        tabletMediator.setCoordinators(
-                mUrlCoordinator, mAutocompleteCoordinator, mStatusCoordinator);
-        tabletMediator.addUrlFocusChangeListener(mUrlCoordinator);
-
-        tabletMediator.handleUrlFocusAnimation(true);
+        mTabletMediator.addUrlFocusChangeListener(mUrlCoordinator);
+        mTabletMediator.handleUrlFocusAnimation(true);
 
         verify(mUrlCoordinator).onUrlFocusChange(true);
         verify(mUrlAnimator, never()).start();
@@ -862,17 +871,14 @@
     public void testSetUrlFocusChangeInProgress() {
         mMediator.addUrlFocusChangeListener(mUrlCoordinator);
         mMediator.setUrlFocusChangeInProgress(true);
-        verify(mLocationBarLayout).setUrlFocusChangeInProgress(true);
 
         ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(true);
         mMediator.setUrlBarFocus(true, null, OmniboxFocusReason.FAKE_BOX_TAP);
-        doReturn(true).when(mLocationBarLayout).isUrlBarFocused();
+        mMediator.onUrlFocusChange(true);
         doReturn("text").when(mUrlCoordinator).getTextWithoutAutocomplete();
 
         mMediator.setUrlFocusChangeInProgress(false);
 
-        verify(mLocationBarLayout).setUrlFocusChangeInProgress(false);
-        verify(mLocationBarLayout).updateButtonVisibility();
         verify(mUrlCoordinator).onUrlAnimationFinished(true);
         verify(mUrlCoordinator).clearFocus();
         // The first invocation of requestFocus() is from setUrlBarFocus, which we use above to set
@@ -887,8 +893,80 @@
     @Test
     public void testMicUpdatedAfterEventTriggered() {
         mMediator.onVoiceAvailabilityImpacted();
-        verify(mLocationBarLayout).setVoiceSearchEnabled(false);
-        verify(mLocationBarLayout).updateButtonVisibility();
+        verify(mLocationBarLayout, atLeast(1)).setMicButtonVisibility(false);
+        verify(mLocationBarLayout, never()).setMicButtonVisibility(true);
+
+        Mockito.reset(mLocationBarLayout);
+        VoiceRecognitionHandler voiceRecognitionHandler = mock(VoiceRecognitionHandler.class);
+        mMediator.setVoiceRecognitionHandlerForTesting(voiceRecognitionHandler);
+        mMediator.onFinishNativeInitialization();
+        mMediator.onVoiceAvailabilityImpacted();
+
+        verify(mLocationBarLayout, atLeast(1)).setMicButtonVisibility(false);
+        verify(mLocationBarLayout, never()).setMicButtonVisibility(true);
+
+        mMediator.onUrlFocusChange(true);
+        doReturn(true).when(voiceRecognitionHandler).isVoiceSearchEnabled();
+        mMediator.onVoiceAvailabilityImpacted();
+
+        verify(mLocationBarLayout).setMicButtonVisibility(true);
+    }
+
+    @Test
+    public void testButtonVisibility_phone() {
+        VoiceRecognitionHandler voiceRecognitionHandler = mock(VoiceRecognitionHandler.class);
+        mMediator.setVoiceRecognitionHandlerForTesting(voiceRecognitionHandler);
+        mMediator.onFinishNativeInitialization();
+        Mockito.reset(mLocationBarLayout);
+
+        mMediator.updateButtonVisibility();
+        verify(mLocationBarLayout).setDeleteButtonVisibility(false);
+
+        mMediator.onUrlFocusChange(true);
+        doReturn("").when(mUrlCoordinator).getTextWithAutocomplete();
+        doReturn(true).when(voiceRecognitionHandler).isVoiceSearchEnabled();
+        mMediator.updateButtonVisibility();
+        verify(mLocationBarLayout).setMicButtonVisibility(true);
+        verify(mLocationBarLayout, never()).setDeleteButtonVisibility(true);
+
+        doReturn("text").when(mUrlCoordinator).getTextWithAutocomplete();
+        mMediator.updateButtonVisibility();
+        verify(mLocationBarLayout).setDeleteButtonVisibility(true);
+    }
+
+    @Test
+    public void testButtonVisibility_showMicUnfocused() {
+        VoiceRecognitionHandler voiceRecognitionHandler = mock(VoiceRecognitionHandler.class);
+        mMediator.setVoiceRecognitionHandlerForTesting(voiceRecognitionHandler);
+        mMediator.onFinishNativeInitialization();
+        mMediator.setShouldShowMicButtonWhenUnfocusedForPhone(true);
+        doReturn(true).when(voiceRecognitionHandler).isVoiceSearchEnabled();
+
+        mMediator.updateButtonVisibility();
+        verify(mLocationBarLayout).setMicButtonVisibility(true);
+    }
+
+    @Test
+    public void testButtonVisibility_tablet() {
+        doReturn(mTab).when(mLocationBarDataProvider).getTab();
+        mTabletMediator.onFinishNativeInitialization();
+        Mockito.reset(mLocationBarTablet);
+        mTabletMediator.updateButtonVisibility();
+
+        verify(mLocationBarTablet).setBookmarkButtonVisibility(true);
+        verify(mLocationBarTablet).setSaveOfflineButtonVisibility(true, true);
+    }
+
+    @Test
+    public void testButtonVisibility_tabletDontShowUnfocused() {
+        doReturn(mTab).when(mLocationBarDataProvider).getTab();
+        mTabletMediator.onFinishNativeInitialization();
+        mTabletMediator.setShouldShowButtonsWhenUnfocusedForTablet(false);
+        Mockito.reset(mLocationBarTablet);
+        mTabletMediator.updateButtonVisibility();
+
+        verify(mLocationBarTablet).setBookmarkButtonVisibility(false);
+        verify(mLocationBarTablet).setSaveOfflineButtonVisibility(false, true);
     }
 
     private ArgumentMatcher<UrlBarData> matchesUrlBarDataForQuery(String query) {
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_HIDDEN_NETWORK_WARNING.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_SETTINGS_HIDDEN_NETWORK_WARNING.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_HIDDEN_NETWORK_WARNING.png.sha1
rename to chrome/app/chromeos_strings_grdp/IDS_SETTINGS_HIDDEN_NETWORK_WARNING.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ABOUT_PAGE_END_OF_LIFE_MESSAGE_PAST.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ABOUT_PAGE_END_OF_LIFE_MESSAGE_PAST.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ABOUT_PAGE_END_OF_LIFE_MESSAGE_PAST.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ABOUT_PAGE_END_OF_LIFE_MESSAGE_PAST.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_AUDIO_AND_CAPTIONS_HEADING.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_AUDIO_AND_CAPTIONS_HEADING.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_AUDIO_AND_CAPTIONS_HEADING.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_AUDIO_AND_CAPTIONS_HEADING.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION_WITHOUT_KEYBOARD.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION_WITHOUT_KEYBOARD.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION_WITHOUT_KEYBOARD.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION_WITHOUT_KEYBOARD.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_DESCRIPTION.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_DESCRIPTION.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_DESCRIPTION.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_EDUCATION_ACCOUNT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_EDUCATION_ACCOUNT.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_EDUCATION_ACCOUNT.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_EDUCATION_ACCOUNT.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_MANAGED_ACCOUNT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_MANAGED_ACCOUNT.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_MANAGED_ACCOUNT.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_MANAGED_ACCOUNT.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_UNMANAGED_ACCOUNT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_UNMANAGED_ACCOUNT.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_UNMANAGED_ACCOUNT.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_UNMANAGED_ACCOUNT.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MIGRATION_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MIGRATION_LABEL.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MIGRATION_LABEL.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_MIGRATION_LABEL.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_PAGE_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_PAGE_TITLE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_PAGE_TITLE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_PAGE_TITLE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_PRIMARY_ACCOUNT_TOOLTIP.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_PRIMARY_ACCOUNT_TOOLTIP.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_PRIMARY_ACCOUNT_TOOLTIP.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_PRIMARY_ACCOUNT_TOOLTIP.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_REAUTHENTICATION_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_REAUTHENTICATION_LABEL.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_REAUTHENTICATION_LABEL.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_REAUTHENTICATION_LABEL.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_REMOVE_ACCOUNT_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_REMOVE_ACCOUNT_LABEL.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_REMOVE_ACCOUNT_LABEL.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_REMOVE_ACCOUNT_LABEL.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SECONDARY_ACCOUNTS_DISABLED_CHILD_TEXT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SECONDARY_ACCOUNTS_DISABLED_CHILD_TEXT.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SECONDARY_ACCOUNTS_DISABLED_CHILD_TEXT.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SECONDARY_ACCOUNTS_DISABLED_CHILD_TEXT.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SECONDARY_ACCOUNTS_DISABLED_TEXT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SECONDARY_ACCOUNTS_DISABLED_TEXT.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SECONDARY_ACCOUNTS_DISABLED_TEXT.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SECONDARY_ACCOUNTS_DISABLED_TEXT.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SIGNED_OUT_ACCOUNT_PLACEHOLDER.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SIGNED_OUT_ACCOUNT_PLACEHOLDER.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SIGNED_OUT_ACCOUNT_PLACEHOLDER.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_SIGNED_OUT_ACCOUNT_PLACEHOLDER.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_UNMIGRATED_ACCOUNT_PLACEHOLDER.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_UNMIGRATED_ACCOUNT_PLACEHOLDER.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_UNMIGRATED_ACCOUNT_PLACEHOLDER.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ACCOUNT_MANAGER_UNMIGRATED_ACCOUNT_PLACEHOLDER.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_KEYBOARD_BOTTOM_LEFT_ARIA_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_KEYBOARD_BOTTOM_LEFT_ARIA_LABEL.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_KEYBOARD_BOTTOM_LEFT_ARIA_LABEL.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_KEYBOARD_BOTTOM_LEFT_ARIA_LABEL.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_DEFAULT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_DEFAULT.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_DEFAULT.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_DEFAULT.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_EXTRA_LARGE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_EXTRA_LARGE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_EXTRA_LARGE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_EXTRA_LARGE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_EXTRA_SMALL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_EXTRA_SMALL.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_EXTRA_SMALL.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_EXTRA_SMALL.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_LABEL.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_LABEL.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_LABEL.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_LARGE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_LARGE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_LARGE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_LARGE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_SMALL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_SMALL.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_SMALL.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_SMALL.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_REVERT_TO_LEFT_CLICK.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_REVERT_TO_LEFT_CLICK.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_REVERT_TO_LEFT_CLICK.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_REVERT_TO_LEFT_CLICK.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_STABILIZE_CURSOR_POSITION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_STABILIZE_CURSOR_POSITION.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_STABILIZE_CURSOR_POSITION.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUTOCLICK_STABILIZE_CURSOR_POSITION.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_CONFIRM_IMPORT_DIALOG_WINDOW_MESSAGE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_CONFIRM_IMPORT_DIALOG_WINDOW_MESSAGE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_CONFIRM_IMPORT_DIALOG_WINDOW_MESSAGE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_CONFIRM_IMPORT_DIALOG_WINDOW_MESSAGE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_CONFIRM_IMPORT_DIALOG_WINDOW_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_CONFIRM_IMPORT_DIALOG_WINDOW_TITLE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_CONFIRM_IMPORT_DIALOG_WINDOW_TITLE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_CONFIRM_IMPORT_DIALOG_WINDOW_TITLE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT_IMPORT_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT_IMPORT_TITLE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT_IMPORT_TITLE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT_IMPORT_TITLE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT_LABEL.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT_LABEL.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXPORT_LABEL.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_IMPORT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_IMPORT.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_IMPORT.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_IMPORT.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_IMPORT_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_IMPORT_LABEL.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_IMPORT_LABEL.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_IMPORT_LABEL.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_BUTTON.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_BUTTON.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_BUTTON.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_BUTTON.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_TITLE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_TITLE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_TITLE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_DESCRIPTION.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_DESCRIPTION.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_DESCRIPTION.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_LIST_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_LIST_LABEL.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_LIST_LABEL.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_LIST_LABEL.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_LIST_PORT_NUMBER.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_LIST_PORT_NUMBER.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_LIST_PORT_NUMBER.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_LIST_PORT_NUMBER.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_NO_PORTS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_NO_PORTS.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_NO_PORTS.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_NO_PORTS.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_ALL_PORTS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_ALL_PORTS.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_ALL_PORTS.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_ALL_PORTS.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_PORT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_PORT.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_PORT.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_PORT.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TABLE_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TABLE_TITLE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TABLE_TITLE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TABLE_TITLE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TCP.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TCP.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TCP.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TCP.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_UDP.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_UDP.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_UDP.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_UDP.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_DEFAULT_PERCENTAGE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DEFAULT_PERCENTAGE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_DEFAULT_PERCENTAGE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DEFAULT_PERCENTAGE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP_OFF.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP_OFF.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP_OFF.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP_OFF.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP_ON.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP_ON.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP_ON.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP_ON.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_ADD_PRINT_SERVER_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_ADD_PRINT_SERVER_TITLE.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_ADD_PRINT_SERVER_TITLE.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_ADD_PRINT_SERVER_TITLE.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_ADDRESS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_ADDRESS.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_ADDRESS.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_ADDRESS.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_CONNECTION_ERROR.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_CONNECTION_ERROR.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_CONNECTION_ERROR.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_CONNECTION_ERROR.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_MANY_PRINTERS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_MANY_PRINTERS.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_MANY_PRINTERS.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_MANY_PRINTERS.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ONE_PRINTER.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ONE_PRINTER.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ONE_PRINTER.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ONE_PRINTER.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ZERO_PRINTERS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ZERO_PRINTERS.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ZERO_PRINTERS.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ZERO_PRINTERS.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_INVALID_URL_ADDRESS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_INVALID_URL_ADDRESS.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_INVALID_URL_ADDRESS.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_INVALID_URL_ADDRESS.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_REACHABLE_BUT_CANNOT_ADD.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_REACHABLE_BUT_CANNOT_ADD.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_REACHABLE_BUT_CANNOT_ADD.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_REACHABLE_BUT_CANNOT_ADD.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_OFF.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_OFF.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_OFF.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_OFF.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ON.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ON.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ON.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ON.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_STORAGE_ITEM_APPS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_STORAGE_ITEM_APPS.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_STORAGE_ITEM_APPS.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_STORAGE_ITEM_APPS.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_STORAGE_ITEM_SYSTEM.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_STORAGE_ITEM_SYSTEM.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_STORAGE_ITEM_SYSTEM.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_STORAGE_ITEM_SYSTEM.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_TEXT_TO_SPEECH_MORE_LANGUAGES.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_TEXT_TO_SPEECH_MORE_LANGUAGES.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_TEXT_TO_SPEECH_MORE_LANGUAGES.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_TEXT_TO_SPEECH_MORE_LANGUAGES.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_USERS_REMOVE_USER_TOOLTIP.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_USERS_REMOVE_USER_TOOLTIP.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_USERS_REMOVE_USER_TOOLTIP.png.sha1
rename to chrome/app/os_settings_strings_grdp/IDS_SETTINGS_USERS_REMOVE_USER_TOOLTIP.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION.png.sha1
deleted file mode 100644
index 1ca2cc9..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4100665fd8b165870bcc0a2e0e9ac21b19f2d94c
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DISABLED_DESCRIPTION.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DISABLED_DESCRIPTION.png.sha1
deleted file mode 100644
index 4202d8cb..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DISABLED_DESCRIPTION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ee92dfbad59306253f8548ac58b41e9079eaacd1
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS.png.sha1
deleted file mode 100644
index bb673d3..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-9e5cc39d8f7e3468604037bee09decfa463359ff
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_DETAILS_ADVANCED.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_ADVANCED.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_DETAILS_ADVANCED.png.sha1
rename to chrome/app/settings_strings_grdp/IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_ADVANCED.png.sha1
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_ERROR.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_ERROR.png.sha1
deleted file mode 100644
index ef697db..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_ERROR.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bc716d3b0c747350720faefe5a1f53e64c0ec524
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_LABEL.png.sha1
deleted file mode 100644
index ef697db..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bc716d3b0c747350720faefe5a1f53e64c0ec524
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS.png.sha1
deleted file mode 100644
index 9e18b2b..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bd6f4bce403a5570e20c63d0ecbb0a41e185a2f7
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_NO_BLUETOOTH_DEVICES_FOUND.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_NO_BLUETOOTH_DEVICES_FOUND.png.sha1
similarity index 100%
rename from chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_NO_BLUETOOTH_DEVICES_FOUND.png.sha1
rename to chrome/app/settings_strings_grdp/IDS_SETTINGS_NO_BLUETOOTH_DEVICES_FOUND.png.sha1
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1244fc7..931d6170 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2075,6 +2075,7 @@
     "//components/download/database",
     "//components/download/public/background_service:public",
     "//components/embedder_support",
+    "//components/embedder_support:browser_util",
     "//components/encrypted_messages",
     "//components/enterprise",
     "//components/enterprise/common/proto:connectors_proto",
@@ -4717,6 +4718,7 @@
       "metrics/google_update_metrics_provider_win.h",
       "metrics/jumplist_metrics_win.cc",
       "metrics/jumplist_metrics_win.h",
+      "metrics/power/battery_level_provider_win.cc",
       "metrics/tab_stats_tracker_delegate_win.cc",
       "metrics/tab_stats_tracker_win.cc",
       "net/service_providers_win.cc",
diff --git a/chrome/browser/accessibility/soda_installer.h b/chrome/browser/accessibility/soda_installer.h
index 729735bc..80dd2e2 100644
--- a/chrome/browser/accessibility/soda_installer.h
+++ b/chrome/browser/accessibility/soda_installer.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_ACCESSIBILITY_SODA_INSTALLER_H_
 #define CHROME_BROWSER_ACCESSIBILITY_SODA_INSTALLER_H_
 
+#include "base/files/file_path.h"
 #include "base/observer_list.h"
 
 class PrefService;
@@ -38,6 +39,11 @@
   // instance.
   static SodaInstaller* GetInstance();
 
+  // Gets the directory path of the installed SODA lib bundle, or an empty path
+  // if not installed. Currently Chrome OS only, returns empty path on other
+  // platforms.
+  virtual base::FilePath GetSodaLibPath() const = 0;
+
   // Installs the SODA binary. Called by CaptionController when the
   // kLiveCaptionEnabled preference changes. PrefService is passed to share
   // Live Captions preferences: whether it is enabled, which language to
diff --git a/chrome/browser/accessibility/soda_installer_impl.cc b/chrome/browser/accessibility/soda_installer_impl.cc
index 574cc91..9a62105 100644
--- a/chrome/browser/accessibility/soda_installer_impl.cc
+++ b/chrome/browser/accessibility/soda_installer_impl.cc
@@ -63,6 +63,11 @@
   component_updater_observer_.RemoveAll();
 }
 
+base::FilePath SodaInstallerImpl::GetSodaLibPath() const {
+  DLOG(FATAL) << "GetSodaLibPath not supported on this platform";
+  return base::FilePath();
+}
+
 void SodaInstallerImpl::InstallSoda(PrefService* prefs) {
   component_updater::RegisterSodaComponent(
       g_browser_process->component_updater(), prefs,
diff --git a/chrome/browser/accessibility/soda_installer_impl.h b/chrome/browser/accessibility/soda_installer_impl.h
index 75a4d84..e9c6926 100644
--- a/chrome/browser/accessibility/soda_installer_impl.h
+++ b/chrome/browser/accessibility/soda_installer_impl.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <string>
 
+#include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/accessibility/soda_installer.h"
@@ -31,6 +32,9 @@
   SodaInstallerImpl(const SodaInstallerImpl&) = delete;
   SodaInstallerImpl& operator=(const SodaInstallerImpl&) = delete;
 
+  // Currently only implemented in the chromeos-specific subclass.
+  base::FilePath GetSodaLibPath() const override;
+
   // SodaInstaller:
   void InstallSoda(PrefService* prefs) override;
   void InstallLanguage(PrefService* prefs) override;
diff --git a/chrome/browser/android/content/content_utils.cc b/chrome/browser/android/content/content_utils.cc
index 3535fbf..3bbc7446 100644
--- a/chrome/browser/android/content/content_utils.cc
+++ b/chrome/browser/android/content/content_utils.cc
@@ -4,13 +4,14 @@
 
 #include "base/android/jni_string.h"
 #include "chrome/android/chrome_jni_headers/ContentUtils_jni.h"
-#include "chrome/browser/chrome_content_browser_client.h"
-#include "components/embedder_support/android/util/user_agent_utils.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/version_info/version_info.h"
+#include "content/public/browser/web_contents.h"
 
 static base::android::ScopedJavaLocalRef<jstring>
 JNI_ContentUtils_GetBrowserUserAgent(JNIEnv* env) {
-  return base::android::ConvertUTF8ToJavaString(env, GetUserAgent());
+  return base::android::ConvertUTF8ToJavaString(
+      env, embedder_support::GetUserAgent());
 }
 
 static void JNI_ContentUtils_SetUserAgentOverride(
@@ -18,6 +19,6 @@
     const base::android::JavaParamRef<jobject>& jweb_contents) {
   content::WebContents* web_contents =
       content::WebContents::FromJavaWebContents(jweb_contents);
-  embedder_support::SetDesktopUserAgentOverride(web_contents,
-                                                ::GetUserAgentMetadata());
+  embedder_support::SetDesktopUserAgentOverride(
+      web_contents, embedder_support::GetUserAgentMetadata());
 }
diff --git a/chrome/browser/ash/accessibility/soda_installer_impl_chromeos.cc b/chrome/browser/ash/accessibility/soda_installer_impl_chromeos.cc
index c17c9402..95b05be 100644
--- a/chrome/browser/ash/accessibility/soda_installer_impl_chromeos.cc
+++ b/chrome/browser/ash/accessibility/soda_installer_impl_chromeos.cc
@@ -32,6 +32,10 @@
   if (!base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption))
     return;
 
+  // Clear cached path in case this is a reinstallation (path could
+  // change).
+  SetSodaLibPath(base::FilePath());
+
   // Install SODA DLC.
   chromeos::DlcserviceClient::Get()->Install(
       kSodaDlcName,
@@ -41,6 +45,10 @@
                           base::Unretained(this)));
 }
 
+base::FilePath SodaInstallerImplChromeOS::GetSodaLibPath() const {
+  return soda_lib_path_;
+}
+
 void SodaInstallerImplChromeOS::InstallLanguage(PrefService* prefs) {
   // TODO(crbug.com/1111002): Install SODA language.
 }
@@ -50,9 +58,14 @@
   return !base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption);
 }
 
+void SodaInstallerImplChromeOS::SetSodaLibPath(base::FilePath new_path) {
+  soda_lib_path_ = new_path;
+}
+
 void SodaInstallerImplChromeOS::OnSodaInstaller(
     const chromeos::DlcserviceClient::InstallResult& install_result) {
   if (install_result.error == dlcservice::kErrorNone) {
+    SetSodaLibPath(base::FilePath(install_result.root_path));
     NotifyOnSodaInstalled();
   } else {
     NotifyOnSodaError();
diff --git a/chrome/browser/ash/accessibility/soda_installer_impl_chromeos.h b/chrome/browser/ash/accessibility/soda_installer_impl_chromeos.h
index 81103c2..31d0f78 100644
--- a/chrome/browser/ash/accessibility/soda_installer_impl_chromeos.h
+++ b/chrome/browser/ash/accessibility/soda_installer_impl_chromeos.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_ASH_ACCESSIBILITY_SODA_INSTALLER_IMPL_CHROMEOS_H_
 #define CHROME_BROWSER_ASH_ACCESSIBILITY_SODA_INSTALLER_IMPL_CHROMEOS_H_
 
+#include "base/files/file_path.h"
 #include "chrome/browser/accessibility/soda_installer.h"
 #include "chromeos/dbus/dlcservice/dlcservice_client.h"
 
@@ -22,18 +23,26 @@
   SodaInstallerImplChromeOS& operator=(const SodaInstallerImplChromeOS&) =
       delete;
 
+  // Where the SODA DLC was installed. Cached on completed installation.
+  // Empty if SODA DLC not installed yet.
+  base::FilePath GetSodaLibPath() const override;
+
   // SodaInstaller:
   void InstallSoda(PrefService* prefs) override;
   void InstallLanguage(PrefService* prefs) override;
   bool IsSodaRegistered() override;
 
  private:
+  void SetSodaLibPath(base::FilePath new_path);
+
   // This function is the InstallCallback for DlcserviceClient::Install().
   void OnSodaInstaller(
       const chromeos::DlcserviceClient::InstallResult& install_result);
 
   // This function is the ProgressCallback for DlcserviceClient::Install().
   void OnSodaProgress(double progress);
+
+  base::FilePath soda_lib_path_;
 };
 
 }  // namespace speech
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
index 2267c3f2..86043ac 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -7,6 +7,7 @@
 #include <queue>
 
 #include "ash/public/cpp/accelerators.h"
+#include "ash/public/cpp/accessibility_controller.h"
 #include "ash/public/cpp/event_rewriter_controller.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/root_window_controller.h"
@@ -20,6 +21,7 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/task/post_task.h"
+#include "base/test/simple_test_tick_clock.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
@@ -53,6 +55,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/test/ui_controls.h"
 #include "ui/base/ui_base_features.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/test/event_generator.h"
 #include "ui/views/widget/widget.h"
 
 namespace {
@@ -125,15 +129,6 @@
       ASSERT_TRUE(ui_controls::SendMouseMove(location.x(), location.y())));
 }
 
-void LoggedInSpokenFeedbackTest::SimulateTouchScreenInChromeVox() {
-  // ChromeVox looks at whether 'ontouchstart' exists to know whether
-  // or not it should respond to hover events. Fake it so that touch
-  // exploration events get spoken.
-  extensions::browsertest_util::ExecuteScriptInBackgroundPageNoWait(
-      browser()->profile(), extension_misc::kChromeVoxExtensionId,
-      "window.ontouchstart = function() {};");
-}
-
 bool LoggedInSpokenFeedbackTest::PerformAcceleratorAction(
     ash::AcceleratorAction action) {
   return ash::AcceleratorController::Get()->PerformActionIfEnabled(action, {});
@@ -806,7 +801,6 @@
 
 IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, TouchExploreStatusTray) {
   EnableChromeVox();
-  sm_.Call([this]() { SimulateTouchScreenInChromeVox(); });
 
   // Send an accessibility hover event on the system tray, which is
   // what we get when you tap it on a touch screen when ChromeVox is on.
@@ -823,6 +817,108 @@
   sm_.Replay();
 }
 
+IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest,
+                       TouchExploreRightEdgeVolumeSliderOn) {
+  EnableChromeVox();
+
+  base::SimpleTestTickClock clock;
+  auto* clock_ptr = &clock;
+  ui::SetEventTickClockForTesting(clock_ptr);
+
+  auto* root_window = ash::Shell::Get()->GetPrimaryRootWindow();
+  ui::test::EventGenerator generator(root_window);
+  auto* generator_ptr = &generator;
+
+  // Force right edge volume slider gesture on.
+  sm_.Call([] {
+    ash::AccessibilityController::Get()->EnableChromeVoxVolumeSlideGesture();
+  });
+
+  // Touch and slide on the right edge of the screen.
+  sm_.Call([clock_ptr, generator_ptr]() {
+    ui::TouchEvent touch_press(
+        ui::ET_TOUCH_PRESSED, gfx::Point(1280, 200), base::TimeTicks::Now(),
+        ui::PointerDetails(ui::EventPointerType::kTouch, 0));
+    generator_ptr->Dispatch(&touch_press);
+
+    clock_ptr->Advance(base::TimeDelta::FromSeconds(1));
+
+    ui::TouchEvent touch_move(
+        ui::ET_TOUCH_MOVED, gfx::Point(1280, 300), base::TimeTicks::Now(),
+        ui::PointerDetails(ui::EventPointerType::kTouch, 0));
+    generator_ptr->Dispatch(&touch_move);
+
+    clock_ptr->Advance(base::TimeDelta::FromSeconds(1));
+
+    ui::TouchEvent touch_move2(
+        ui::ET_TOUCH_MOVED, gfx::Point(1280, 400), base::TimeTicks::Now(),
+        ui::PointerDetails(ui::EventPointerType::kTouch, 0));
+    generator_ptr->Dispatch(&touch_move2);
+  });
+
+  sm_.ExpectSpeech("Volume");
+  sm_.ExpectSpeech("Slider");
+
+  sm_.Replay();
+}
+
+IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest,
+                       TouchExploreRightEdgeVolumeSliderOff) {
+  EnableChromeVox();
+
+  base::SimpleTestTickClock clock;
+  auto* clock_ptr = &clock;
+  ui::SetEventTickClockForTesting(clock_ptr);
+
+  auto* root_window = ash::Shell::Get()->GetPrimaryRootWindow();
+  ui::test::EventGenerator generator(root_window);
+  auto* generator_ptr = &generator;
+
+  // Build a simple window with a button and position it at the right edge of
+  // the screen.
+  views::Widget* widget = new views::Widget;
+  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
+
+  // This is the right edge of the screen.
+  params.bounds = {1250, 0, 50, 700};
+  widget->Init(std::move(params));
+
+  views::View* view = new views::View();
+  view->GetViewAccessibility().OverrideName("hello");
+  view->GetViewAccessibility().OverrideRole(ax::mojom::Role::kButton);
+  view->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
+  widget->GetRootView()->AddChildView(view);
+
+  // Show the widget, then touch and slide on the right edge of the screen.
+  sm_.Call([widget, clock_ptr, generator_ptr]() {
+    widget->Show();
+    ui::TouchEvent touch_press(
+        ui::ET_TOUCH_PRESSED, gfx::Point(1280, 200), base::TimeTicks::Now(),
+        ui::PointerDetails(ui::EventPointerType::kTouch, 0));
+    generator_ptr->Dispatch(&touch_press);
+
+    clock_ptr->Advance(base::TimeDelta::FromSeconds(1));
+
+    ui::TouchEvent touch_move(
+        ui::ET_TOUCH_MOVED, gfx::Point(1280, 300), base::TimeTicks::Now(),
+        ui::PointerDetails(ui::EventPointerType::kTouch, 0));
+    generator_ptr->Dispatch(&touch_move);
+
+    clock_ptr->Advance(base::TimeDelta::FromSeconds(1));
+
+    ui::TouchEvent touch_move2(
+        ui::ET_TOUCH_MOVED, gfx::Point(1280, 400), base::TimeTicks::Now(),
+        ui::PointerDetails(ui::EventPointerType::kTouch, 0));
+    generator_ptr->Dispatch(&touch_move2);
+  });
+
+  // This should trigger reading of the button.
+  sm_.ExpectSpeech("hello");
+  sm_.ExpectSpeech("Button");
+
+  sm_.Replay();
+}
+
 IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, ChromeVoxNextTabRecovery) {
   EnableChromeVox();
 
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.h b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.h
index fec6ae4..e086c30 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.h
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.h
@@ -38,8 +38,6 @@
 
   void SendMouseMoveTo(const gfx::Point& location);
 
-  void SimulateTouchScreenInChromeVox();
-
   bool PerformAcceleratorAction(ash::AcceleratorAction action);
 
   void DisableEarcons();
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller.cc b/chrome/browser/ash/account_manager/account_manager_policy_controller.cc
index c1c85d7..db105b1b 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller.cc
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller.cc
@@ -48,9 +48,10 @@
 
   chromeos::ChildAccountTypeChangedUserData* user_data =
       chromeos::ChildAccountTypeChangedUserData::GetForProfile(profile_);
-  child_account_type_changed_subscription_ = user_data->RegisterCallback(
-      base::Bind(&AccountManagerPolicyController::OnChildAccountTypeChanged,
-                 base::Unretained(this)));
+  child_account_type_changed_subscription_ =
+      user_data->RegisterCallback(base::BindRepeating(
+          &AccountManagerPolicyController::OnChildAccountTypeChanged,
+          base::Unretained(this)));
   // Take any necessary initial action based on the current value.
   OnChildAccountTypeChanged(user_data->value());
 
diff --git a/chrome/browser/ash/account_manager/account_manager_ui_impl.cc b/chrome/browser/ash/account_manager/account_manager_ui_impl.cc
index 41d04da..3b26bc1 100644
--- a/chrome/browser/ash/account_manager/account_manager_ui_impl.cc
+++ b/chrome/browser/ash/account_manager/account_manager_ui_impl.cc
@@ -15,9 +15,10 @@
   InlineLoginDialogChromeOS::Show(std::move(close_dialog_closure));
 }
 
-void AccountManagerUIImpl::ShowReauthAccountDialog(const std::string& email) {
-  InlineLoginDialogChromeOS::Show(email,
-                                  /*close_dialog_closure=*/base::DoNothing());
+void AccountManagerUIImpl::ShowReauthAccountDialog(
+    const std::string& email,
+    base::OnceClosure close_dialog_closure) {
+  InlineLoginDialogChromeOS::Show(email, std::move(close_dialog_closure));
 }
 
 bool AccountManagerUIImpl::IsDialogShown() {
diff --git a/chrome/browser/ash/account_manager/account_manager_ui_impl.h b/chrome/browser/ash/account_manager/account_manager_ui_impl.h
index a21ef4d..047bc5c 100644
--- a/chrome/browser/ash/account_manager/account_manager_ui_impl.h
+++ b/chrome/browser/ash/account_manager/account_manager_ui_impl.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_ASH_ACCOUNT_MANAGER_ACCOUNT_MANAGER_UI_IMPL_H_
 #define CHROME_BROWSER_ASH_ACCOUNT_MANAGER_ACCOUNT_MANAGER_UI_IMPL_H_
 
+#include "base/callback_forward.h"
 #include "chromeos/components/account_manager/account_manager_ui.h"
 
 namespace chromeos {
@@ -19,7 +20,8 @@
  private:
   // AccountManagerUI overrides:
   void ShowAddAccountDialog(base::OnceClosure close_dialog_closure) override;
-  void ShowReauthAccountDialog(const std::string& email) override;
+  void ShowReauthAccountDialog(const std::string& email,
+                               base::OnceClosure close_dialog_closure) override;
   bool IsDialogShown() override;
 };
 
diff --git a/chrome/browser/autofill/risk_util.cc b/chrome/browser/autofill/risk_util.cc
index fb31a9c..1694ef00 100644
--- a/chrome/browser/autofill/risk_util.cc
+++ b/chrome/browser/autofill/risk_util.cc
@@ -13,11 +13,11 @@
 #include "build/build_config.h"
 #include "chrome/browser/apps/platform_apps/app_window_registry_util.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "components/autofill/content/browser/risk/fingerprint.h"
 #include "components/autofill/content/browser/risk/proto/fingerprint.pb.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/metrics/metrics_service.h"
 #include "components/prefs/pref_service.h"
@@ -89,11 +89,12 @@
       g_browser_process->metrics_service()->GetInstallDate());
 
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  risk::GetFingerprint(
-      obfuscated_gaia_id, window_bounds, web_contents,
-      version_info::GetVersionNumber(), charset, accept_languages, install_time,
-      g_browser_process->GetApplicationLocale(), GetUserAgent(),
-      base::BindOnce(PassRiskData, std::move(callback)));
+  risk::GetFingerprint(obfuscated_gaia_id, window_bounds, web_contents,
+                       version_info::GetVersionNumber(), charset,
+                       accept_languages, install_time,
+                       g_browser_process->GetApplicationLocale(),
+                       embedder_support::GetUserAgent(),
+                       base::BindOnce(PassRiskData, std::move(callback)));
 }
 
 }  // namespace risk_util
diff --git a/chrome/browser/banners/app_banner_manager_android.cc b/chrome/browser/banners/app_banner_manager_android.cc
index f9705ca..f19df64 100644
--- a/chrome/browser/banners/app_banner_manager_android.cc
+++ b/chrome/browser/banners/app_banner_manager_android.cc
@@ -30,6 +30,7 @@
 #include "components/infobars/core/infobar_delegate.h"
 #include "components/site_engagement/content/site_engagement_service.h"
 #include "components/version_info/channel.h"
+#include "components/version_info/version_info.h"
 #include "components/webapps/browser/android/add_to_homescreen_params.h"
 #include "components/webapps/browser/android/shortcut_info.h"
 #include "components/webapps/browser/android/webapps_icon_utils.h"
@@ -381,9 +382,15 @@
   // AppBannerManager#fetchAppDetails() only works on Beta and Stable because
   // the called Google Play API uses an old way of checking whether the Chrome
   // app is first party. See http://b/147780265
+  // Run AppBannerManager#fetchAppDetails() for local builds regardless of Android channel to avoid
+  // having to set android_channel GN flag for manual testing. Do not run
+  // AppBannerManager#fetchAppDetails() for non-official local builds to ensure that tests use
+  // gIgnoreChromeChannelForTesting rather than relying on the local build exemption.
   version_info::Channel channel = chrome::GetChannel();
-  if (!gIgnoreChromeChannelForTesting &&
-      !(channel == version_info::Channel::BETA ||
+  bool local_build = channel == version_info::Channel::UNKNOWN &&
+                     version_info::IsOfficialBuild();
+  if (!(local_build || gIgnoreChromeChannelForTesting ||
+        channel == version_info::Channel::BETA ||
         channel == version_info::Channel::STABLE)) {
     return PREFER_RELATED_APPLICATIONS_SUPPORTED_ONLY_BETA_STABLE;
   }
diff --git a/chrome/browser/cart/cart_service.cc b/chrome/browser/cart/cart_service.cc
index 557825f..f2424c9 100644
--- a/chrome/browser/cart/cart_service.cc
+++ b/chrome/browser/cart/cart_service.cc
@@ -26,6 +26,11 @@
              ? std::string(kFakeDataPrefix) + domain
              : domain;
 }
+
+bool CompareTimeStampForProtoPair(const CartDB::KeyAndValue pair1,
+                                  CartDB::KeyAndValue pair2) {
+  return pair1.second.timestamp() > pair2.second.timestamp();
+}
 }  // namespace
 
 CartService::CartService(Profile* profile)
@@ -197,6 +202,7 @@
   dummy_proto1.set_key(std::string(kFakeDataPrefix) + eTLDPlusOne(dummy_url1));
   dummy_proto1.set_merchant("Cart Foo");
   dummy_proto1.set_merchant_cart_url(dummy_url1.spec());
+  dummy_proto1.set_timestamp(6);
   dummy_proto1.add_product_image_urls(
       "https://encrypted-tbn3.gstatic.com/"
       "shopping?q=tbn:ANd9GcQpn38jB2_BANnHUFa7kHJsf6SyubcgeU1lNYO_"
@@ -221,6 +227,7 @@
   dummy_proto2.set_key(std::string(kFakeDataPrefix) + eTLDPlusOne(dummy_url2));
   dummy_proto2.set_merchant("Cart Bar");
   dummy_proto2.set_merchant_cart_url(dummy_url2.spec());
+  dummy_proto2.set_timestamp(5);
   cart_db_->AddCart(dummy_proto2.key(), dummy_proto2,
                     base::BindOnce(&CartService::OnOperationFinished,
                                    weak_ptr_factory_.GetWeakPtr()));
@@ -230,6 +237,7 @@
   dummy_proto3.set_key(std::string(kFakeDataPrefix) + eTLDPlusOne(dummy_url3));
   dummy_proto3.set_merchant("Cart Baz");
   dummy_proto3.set_merchant_cart_url(dummy_url3.spec());
+  dummy_proto3.set_timestamp(4);
   cart_db_->AddCart(dummy_proto3.key(), dummy_proto3,
                     base::BindOnce(&CartService::OnOperationFinished,
                                    weak_ptr_factory_.GetWeakPtr()));
@@ -239,6 +247,7 @@
   dummy_proto4.set_key(std::string(kFakeDataPrefix) + eTLDPlusOne(dummy_url4));
   dummy_proto4.set_merchant("Cart Qux");
   dummy_proto4.set_merchant_cart_url(dummy_url4.spec());
+  dummy_proto4.set_timestamp(3);
   cart_db_->AddCart(dummy_proto4.key(), dummy_proto4,
                     base::BindOnce(&CartService::OnOperationFinished,
                                    weak_ptr_factory_.GetWeakPtr()));
@@ -248,6 +257,7 @@
   dummy_proto5.set_key(std::string(kFakeDataPrefix) + eTLDPlusOne(dummy_url5));
   dummy_proto5.set_merchant("Cart Corge");
   dummy_proto5.set_merchant_cart_url(dummy_url5.spec());
+  dummy_proto5.set_timestamp(2);
   cart_db_->AddCart(dummy_proto5.key(), dummy_proto5,
                     base::BindOnce(&CartService::OnOperationFinished,
                                    weak_ptr_factory_.GetWeakPtr()));
@@ -257,6 +267,7 @@
   dummy_proto6.set_key(std::string(kFakeDataPrefix) + eTLDPlusOne(dummy_url6));
   dummy_proto6.set_merchant("Cart Flob");
   dummy_proto6.set_merchant_cart_url(dummy_url6.spec());
+  dummy_proto6.set_timestamp(1);
   cart_db_->AddCart(dummy_proto6.key(), dummy_proto6,
                     base::BindOnce(&CartService::OnOperationFinished,
                                    weak_ptr_factory_.GetWeakPtr()));
@@ -287,6 +298,9 @@
                                             kv.second.is_removed();
                                    }),
                     proto_pairs.end());
+  // Sort items in timestamp descending order.
+  std::sort(proto_pairs.begin(), proto_pairs.end(),
+            CompareTimeStampForProtoPair);
   std::move(callback).Run(success, std::move(proto_pairs));
 }
 
diff --git a/chrome/browser/cart/cart_service_unittest.cc b/chrome/browser/cart/cart_service_unittest.cc
index 01c4c98..fbc8019 100644
--- a/chrome/browser/cart/cart_service_unittest.cc
+++ b/chrome/browser/cart/cart_service_unittest.cc
@@ -29,6 +29,8 @@
 const char kMockMerchantURLA[] = "https://www.foo.com";
 const char kMockMerchantB[] = "bar.com";
 const char kMockMerchantURLB[] = "https://www.bar.com";
+const char kMockMerchantC[] = "baz.com";
+const char kMockMerchantURLC[] = "https://www.baz.com";
 const cart_db::ChromeCartContentProto kMockProtoA =
     BuildProto(kMockMerchantA, kMockMerchantURLA);
 const cart_db::ChromeCartContentProto kMockProtoB =
@@ -449,3 +451,54 @@
   EXPECT_EQ(limit, profile_.GetPrefs()->GetInteger(
                        prefs::kCartModuleWelcomeSurfaceShownTimes));
 }
+
+// Tests cart data is loaded in the order of timestamp.
+TEST_F(CartServiceTest, TestOrderInTimestamp) {
+  base::RunLoop run_loop[3];
+  cart_db::ChromeCartContentProto merchant_A_proto =
+      BuildProto(kMockMerchantA, kMockMerchantURLA);
+  merchant_A_proto.set_timestamp(0);
+  cart_db::ChromeCartContentProto merchant_B_proto =
+      BuildProto(kMockMerchantB, kMockMerchantURLB);
+  merchant_B_proto.set_timestamp(1);
+  cart_db::ChromeCartContentProto merchant_C_proto =
+      BuildProto(kMockMerchantC, kMockMerchantURLC);
+  merchant_C_proto.set_timestamp(2);
+  service_->AddCart(kMockMerchantA, merchant_A_proto);
+  service_->AddCart(kMockMerchantB, merchant_B_proto);
+  service_->AddCart(kMockMerchantC, merchant_C_proto);
+
+  const std::vector<
+      ProfileProtoDB<cart_db::ChromeCartContentProto>::KeyAndValue>
+      result1 = {{kMockMerchantC, merchant_C_proto},
+                 {kMockMerchantB, merchant_B_proto},
+                 {kMockMerchantA, merchant_A_proto}};
+  service_->LoadAllActiveCarts(base::BindOnce(
+      &CartServiceTest::GetEvaluationLoadCarts, base::Unretained(this),
+      run_loop[0].QuitClosure(), result1));
+  run_loop[0].Run();
+
+  merchant_A_proto.set_timestamp(3);
+  service_->AddCart(kMockMerchantA, merchant_A_proto);
+  const std::vector<
+      ProfileProtoDB<cart_db::ChromeCartContentProto>::KeyAndValue>
+      result2 = {{kMockMerchantA, merchant_A_proto},
+                 {kMockMerchantC, merchant_C_proto},
+                 {kMockMerchantB, merchant_B_proto}};
+  service_->LoadAllActiveCarts(base::BindOnce(
+      &CartServiceTest::GetEvaluationLoadCarts, base::Unretained(this),
+      run_loop[1].QuitClosure(), result2));
+  run_loop[1].Run();
+
+  merchant_C_proto.set_timestamp(4);
+  service_->AddCart(kMockMerchantC, merchant_C_proto);
+  const std::vector<
+      ProfileProtoDB<cart_db::ChromeCartContentProto>::KeyAndValue>
+      result3 = {{kMockMerchantC, merchant_C_proto},
+                 {kMockMerchantA, merchant_A_proto},
+                 {kMockMerchantB, merchant_B_proto}};
+  service_->LoadAllActiveCarts(base::BindOnce(
+      &CartServiceTest::GetEvaluationLoadCarts, base::Unretained(this),
+      run_loop[2].QuitClosure(), result3));
+  run_loop[2].Run();
+}
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index d4a42ca..f21470e 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -220,6 +220,7 @@
 #include "components/dom_distiller/core/dom_distiller_switches.h"
 #include "components/dom_distiller/core/url_constants.h"
 #include "components/embedder_support/switches.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/error_page/common/error.h"
 #include "components/error_page/common/error_page_switches.h"
 #include "components/error_page/common/localized_error.h"
@@ -351,7 +352,6 @@
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "net/base/load_flags.h"
 #include "net/base/mime_util.h"
-#include "net/http/http_util.h"
 #include "net/ssl/client_cert_store.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "pdf/buildflags.h"
@@ -1096,10 +1096,6 @@
   }
 }
 
-std::string GetProduct() {
-  return version_info::GetProductNameAndVersionForUserAgent();
-}
-
 void MaybeAppendSecureOriginsAllowlistSwitch(base::CommandLine* cmdline) {
   // |allowlist| combines pref/policy + cmdline switch in the browser process.
   // For renderer and utility (e.g. NetworkService) processes the switch is the
@@ -1217,125 +1213,6 @@
 
 }  // namespace
 
-// Generate a pseudo-random permutation of the following brand/version pairs:
-//   1. The base project (i.e. Chromium)
-//   2. The browser brand, if available
-//   3. A randomized string containing escaped characters to ensure proper
-//      header parsing, along with an arbitrarily low version to ensure proper
-//      version checking.
-blink::UserAgentBrandList GenerateBrandVersionList(
-    int seed,
-    base::Optional<std::string> brand,
-    std::string major_version,
-    base::Optional<std::string> maybe_greasey_brand) {
-  DCHECK_GE(seed, 0);
-  const int npermutations = 6;  // 3!
-  int permutation = seed % npermutations;
-
-  // Pick a stable permutation seeded by major version number. any values here
-  // and in order should be under three.
-  const std::vector<std::vector<int>> orders{{0, 1, 2}, {0, 2, 1}, {1, 0, 2},
-                                             {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
-  const std::vector<int> order = orders[permutation];
-  DCHECK_EQ(6u, orders.size());
-  DCHECK_EQ(3u, order.size());
-
-  // Previous values for indexes 0 and 1 were '\' and '"', temporarily removed
-  // because of compat issues
-  const std::vector<std::string> escaped_chars = {" ", " ", ";"};
-  std::string greasey_brand =
-      base::StrCat({escaped_chars[order[0]], "Not", escaped_chars[order[1]],
-                    "A", escaped_chars[order[2]], "Brand"});
-
-  blink::UserAgentBrandVersion greasey_bv = {
-      maybe_greasey_brand.value_or(greasey_brand), "99"};
-  blink::UserAgentBrandVersion chromium_bv = {"Chromium", major_version};
-
-  blink::UserAgentBrandList greased_brand_version_list(3);
-
-  if (brand) {
-    blink::UserAgentBrandVersion brand_bv = {brand.value(), major_version};
-
-    greased_brand_version_list[order[0]] = greasey_bv;
-    greased_brand_version_list[order[1]] = chromium_bv;
-    greased_brand_version_list[order[2]] = brand_bv;
-  } else {
-    greased_brand_version_list[seed % 2] = greasey_bv;
-    greased_brand_version_list[(seed + 1) % 2] = chromium_bv;
-
-    // If left, the last element would make a blank "" at the end of the header.
-    greased_brand_version_list.pop_back();
-  }
-
-  return greased_brand_version_list;
-}
-
-const blink::UserAgentBrandList& GetBrandVersionList() {
-  static const base::NoDestructor<blink::UserAgentBrandList>
-      greased_brand_version_list([] {
-        int major_version_number;
-        std::string major_version = version_info::GetMajorVersionNumber();
-        base::StringToInt(major_version, &major_version_number);
-        base::Optional<std::string> brand;
-#if !BUILDFLAG(CHROMIUM_BRANDING)
-        brand = version_info::GetProductName();
-#endif
-        base::Optional<std::string> maybe_param_override =
-            base::GetFieldTrialParamValueByFeature(features::kGreaseUACH,
-                                                   "brand_override");
-        if (maybe_param_override->empty())
-          maybe_param_override = base::nullopt;
-
-        return GenerateBrandVersionList(major_version_number, brand,
-                                        major_version, maybe_param_override);
-      }());
-  return *greased_brand_version_list;
-}
-
-std::string GetUserAgent() {
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kUserAgent)) {
-    std::string ua = command_line->GetSwitchValueASCII(switches::kUserAgent);
-    if (net::HttpUtil::IsValidHeaderValue(ua))
-      return ua;
-    LOG(WARNING) << "Ignored invalid value for flag --" << switches::kUserAgent;
-  }
-
-  if (base::FeatureList::IsEnabled(blink::features::kFreezeUserAgent)) {
-    return content::GetFrozenUserAgent(
-        command_line->HasSwitch(switches::kUseMobileUserAgent),
-        version_info::GetMajorVersionNumber());
-  }
-
-  std::string product = GetProduct();
-#if defined(OS_ANDROID)
-  if (command_line->HasSwitch(switches::kUseMobileUserAgent))
-    product += " Mobile";
-#endif
-  return content::BuildUserAgentFromProduct(product);
-}
-
-blink::UserAgentMetadata GetUserAgentMetadata() {
-  blink::UserAgentMetadata metadata;
-
-  metadata.brand_version_list = GetBrandVersionList();
-  metadata.full_version = version_info::GetVersionNumber();
-  metadata.platform = version_info::GetOSType();
-  metadata.platform_version =
-      content::GetOSVersion(content::IncludeAndroidBuildNumber::Exclude,
-                            content::IncludeAndroidModel::Exclude);
-  metadata.architecture = content::GetLowEntropyCpuArchitecture();
-  metadata.model = content::BuildModelInfo();
-
-  metadata.mobile = false;
-#if defined(OS_ANDROID)
-  metadata.mobile = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kUseMobileUserAgent);
-#endif
-
-  return metadata;
-}
-
 #if !defined(OS_ANDROID)
 base::TimeDelta GetKeepaliveTimerTimeout() {
   constexpr base::TimeDelta kDefaultValue = base::TimeDelta::FromSeconds(1);
@@ -2290,7 +2167,7 @@
       *base::CommandLine::ForCurrentProcess();
 
   static const char* const kCommonSwitchNames[] = {
-      switches::kUserAgent,
+      embedder_support::kUserAgent,
       switches::kUserDataDir,  // Make logs go to the right file.
   };
   command_line->CopySwitchesFrom(browser_command_line, kCommonSwitchNames,
@@ -5772,15 +5649,15 @@
 }
 
 std::string ChromeContentBrowserClient::GetProduct() {
-  return ::GetProduct();
+  return embedder_support::GetProduct();
 }
 
 std::string ChromeContentBrowserClient::GetUserAgent() {
-  return ::GetUserAgent();
+  return embedder_support::GetUserAgent();
 }
 
 blink::UserAgentMetadata ChromeContentBrowserClient::GetUserAgentMetadata() {
-  return ::GetUserAgentMetadata();
+  return embedder_support::GetUserAgentMetadata();
 }
 
 base::Optional<gfx::ImageSkia> ChromeContentBrowserClient::GetProductLogo() {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 362cbe10..6dbae2cd 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -108,17 +108,6 @@
 }
 #endif
 
-// Returns the user agent of Chrome.
-std::string GetUserAgent();
-
-blink::UserAgentMetadata GetUserAgentMetadata();
-
-blink::UserAgentBrandList GenerateBrandVersionList(
-    int seed,
-    base::Optional<std::string> brand,
-    std::string major_version,
-    base::Optional<std::string> maybe_greasey_brand);
-
 class ChromeContentBrowserClient : public content::ContentBrowserClient {
  public:
   ChromeContentBrowserClient();
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index ee3a02a1..6beabb0 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -14,11 +14,8 @@
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
 #include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/system/sys_info.h"
 #include "base/test/gtest_util.h"
-#include "base/test/scoped_command_line.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -42,21 +39,14 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/user_agent.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "media/media_buildflags.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/switches.h"
-#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "url/gurl.h"
 
-#if defined(USE_X11) || defined(USE_OZONE)
-#include <sys/utsname.h>
-#endif
-
 #if !defined(OS_ANDROID)
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -90,168 +80,6 @@
 using testing::_;
 using ChromeContentBrowserClientTest = testing::Test;
 
-namespace {
-
-void CheckUserAgentStringOrdering(bool mobile_device) {
-  std::vector<std::string> pieces;
-
-  // Check if the pieces of the user agent string come in the correct order.
-  ChromeContentBrowserClient content_browser_client;
-  std::string buffer = content_browser_client.GetUserAgent();
-
-  pieces = base::SplitStringUsingSubstr(
-      buffer, "Mozilla/5.0 (", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  ASSERT_EQ(2u, pieces.size());
-  buffer = pieces[1];
-  EXPECT_EQ("", pieces[0]);
-
-  pieces = base::SplitStringUsingSubstr(
-      buffer, ") AppleWebKit/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  ASSERT_EQ(2u, pieces.size());
-  buffer = pieces[1];
-  std::string os_str = pieces[0];
-
-  pieces =
-      base::SplitStringUsingSubstr(buffer, " (KHTML, like Gecko) ",
-                                   base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  ASSERT_EQ(2u, pieces.size());
-  buffer = pieces[1];
-  std::string webkit_version_str = pieces[0];
-
-  pieces = base::SplitStringUsingSubstr(
-      buffer, " Safari/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  ASSERT_EQ(2u, pieces.size());
-  std::string product_str = pieces[0];
-  std::string safari_version_str = pieces[1];
-
-  EXPECT_FALSE(os_str.empty());
-
-  pieces = base::SplitStringUsingSubstr(os_str, "; ", base::KEEP_WHITESPACE,
-                                        base::SPLIT_WANT_ALL);
-#if defined(OS_WIN)
-  // Windows NT 10.0; Win64; x64
-  // Windows NT 10.0; WOW64
-  // Windows NT 10.0
-  std::string os_and_version = pieces[0];
-  for (unsigned int i = 1; i < pieces.size(); ++i) {
-    bool equals = ((pieces[i] == "WOW64") || (pieces[i] == "Win64") ||
-                   pieces[i] == "x64");
-    ASSERT_TRUE(equals);
-  }
-  pieces = base::SplitStringUsingSubstr(pieces[0], " ", base::KEEP_WHITESPACE,
-                                        base::SPLIT_WANT_ALL);
-  ASSERT_EQ(3u, pieces.size());
-  ASSERT_EQ("Windows", pieces[0]);
-  ASSERT_EQ("NT", pieces[1]);
-  double version;
-  ASSERT_TRUE(base::StringToDouble(pieces[2], &version));
-  ASSERT_LE(4.0, version);
-  ASSERT_GT(11.0, version);
-#elif defined(OS_MAC)
-  // Macintosh; Intel Mac OS X 10_15_4
-  ASSERT_EQ(2u, pieces.size());
-  ASSERT_EQ("Macintosh", pieces[0]);
-  pieces = base::SplitStringUsingSubstr(pieces[1], " ", base::KEEP_WHITESPACE,
-                                        base::SPLIT_WANT_ALL);
-  ASSERT_EQ(5u, pieces.size());
-  ASSERT_EQ("Intel", pieces[0]);
-  ASSERT_EQ("Mac", pieces[1]);
-  ASSERT_EQ("OS", pieces[2]);
-  ASSERT_EQ("X", pieces[3]);
-  pieces = base::SplitStringUsingSubstr(pieces[4], "_", base::KEEP_WHITESPACE,
-                                        base::SPLIT_WANT_ALL);
-  {
-    int major, minor, patch;
-    base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &patch);
-    ASSERT_EQ(base::StringPrintf("%d", major), pieces[0]);
-  }
-  int value;
-  ASSERT_TRUE(base::StringToInt(pieces[1], &value));
-  ASSERT_LE(0, value);
-  ASSERT_TRUE(base::StringToInt(pieces[2], &value));
-  ASSERT_LE(0, value);
-#elif defined(USE_X11) || defined(USE_OZONE)
-  // X11; Linux x86_64
-  // X11; CrOS armv7l 4537.56.0
-  struct utsname unixinfo;
-  uname(&unixinfo);
-  std::string machine = unixinfo.machine;
-  if (strcmp(unixinfo.machine, "x86_64") == 0 &&
-      sizeof(void*) == sizeof(int32_t)) {
-    machine = "i686 (x86_64)";
-  }
-  ASSERT_EQ(2u, pieces.size());
-  ASSERT_EQ("X11", pieces[0]);
-  pieces = base::SplitStringUsingSubstr(pieces[1], " ", base::KEEP_WHITESPACE,
-                                        base::SPLIT_WANT_ALL);
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
-  // X11; CrOS armv7l 4537.56.0
-  //      ^^
-  ASSERT_EQ(3u, pieces.size());
-  ASSERT_EQ("CrOS", pieces[0]);
-  ASSERT_EQ(machine, pieces[1]);
-  pieces = base::SplitStringUsingSubstr(pieces[2], ".", base::KEEP_WHITESPACE,
-                                        base::SPLIT_WANT_ALL);
-  for (unsigned int i = 1; i < pieces.size(); ++i) {
-    int value;
-    ASSERT_TRUE(base::StringToInt(pieces[i], &value));
-  }
-#else
-  // X11; Linux x86_64
-  //      ^^
-  ASSERT_EQ(2u, pieces.size());
-  // This may not be Linux in all cases in the wild, but it is on the bots.
-  ASSERT_EQ("Linux", pieces[0]);
-  ASSERT_EQ(machine, pieces[1]);
-#endif
-#elif defined(OS_ANDROID)
-  // Linux; Android 7.1.1; Samsung Chromebook 3
-  ASSERT_GE(3u, pieces.size());
-  ASSERT_EQ("Linux", pieces[0]);
-  std::string model;
-  if (pieces.size() > 2)
-    model = pieces[2];
-
-  pieces = base::SplitStringUsingSubstr(pieces[1], " ", base::KEEP_WHITESPACE,
-                                        base::SPLIT_WANT_ALL);
-  ASSERT_EQ(2u, pieces.size());
-  ASSERT_EQ("Android", pieces[0]);
-  pieces = base::SplitStringUsingSubstr(pieces[1], ".", base::KEEP_WHITESPACE,
-                                        base::SPLIT_WANT_ALL);
-  for (unsigned int i = 1; i < pieces.size(); ++i) {
-    int value;
-    ASSERT_TRUE(base::StringToInt(pieces[i], &value));
-  }
-
-  if (!model.empty()) {
-    if (base::SysInfo::GetAndroidBuildCodename() == "REL")
-      ASSERT_EQ(base::SysInfo::HardwareModelName(), model);
-    else
-      ASSERT_EQ("", model);
-  }
-#elif defined(OS_FUCHSIA)
-  // X11; Fuchsia
-  ASSERT_EQ(2u, pieces.size());
-  ASSERT_EQ("X11", pieces[0]);
-  ASSERT_EQ("Fuchsia", pieces[1]);
-#endif
-
-  // Check that the version numbers match.
-  EXPECT_FALSE(webkit_version_str.empty());
-  EXPECT_FALSE(safari_version_str.empty());
-  EXPECT_EQ(webkit_version_str, safari_version_str);
-
-  EXPECT_TRUE(
-      base::StartsWith(product_str, "Chrome/", base::CompareCase::SENSITIVE));
-  if (mobile_device) {
-    // "Mobile" gets tacked on to the end for mobile devices, like phones.
-    EXPECT_TRUE(
-        base::EndsWith(product_str, " Mobile", base::CompareCase::SENSITIVE));
-  }
-}
-
-}  // namespace
-
 TEST_F(ChromeContentBrowserClientTest, ShouldAssignSiteForURL) {
   ChromeContentBrowserClient client;
   EXPECT_FALSE(client.ShouldAssignSiteForURL(GURL("chrome-native://test")));
@@ -627,157 +455,6 @@
                                                              nullptr));
 }
 
-TEST(ChromeContentBrowserClientTest, UserAgentStringFrozen) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(blink::features::kFreezeUserAgent);
-
-#if defined(OS_ANDROID)
-  // Verify the correct user agent is returned when the UseMobileUserAgent
-  // command line flag is present.
-  const char* const kArguments[] = {"chrome"};
-  base::test::ScopedCommandLine scoped_command_line;
-  base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
-  command_line->InitFromArgv(1, kArguments);
-
-  // Verify the mobile user agent string is not returned when not using a mobile
-  // user agent.
-  ASSERT_FALSE(command_line->HasSwitch(switches::kUseMobileUserAgent));
-  {
-    ChromeContentBrowserClient content_browser_client;
-    std::string buffer = content_browser_client.GetUserAgent();
-    EXPECT_EQ(buffer, base::StringPrintf(
-                          content::frozen_user_agent_strings::kAndroid,
-                          version_info::GetMajorVersionNumber().c_str()));
-  }
-
-  // Verify the mobile user agent string is returned when using a mobile user
-  // agent.
-  command_line->AppendSwitch(switches::kUseMobileUserAgent);
-  ASSERT_TRUE(command_line->HasSwitch(switches::kUseMobileUserAgent));
-  {
-    ChromeContentBrowserClient content_browser_client;
-    std::string buffer = content_browser_client.GetUserAgent();
-    EXPECT_EQ(buffer, base::StringPrintf(
-                          content::frozen_user_agent_strings::kAndroidMobile,
-                          version_info::GetMajorVersionNumber().c_str()));
-  }
-#else
-  {
-    ChromeContentBrowserClient content_browser_client;
-    std::string buffer = content_browser_client.GetUserAgent();
-    EXPECT_EQ(buffer, base::StringPrintf(
-                          content::frozen_user_agent_strings::kDesktop,
-                          version_info::GetMajorVersionNumber().c_str()));
-  }
-#endif
-}
-
-TEST(ChromeContentBrowserClientTest, UserAgentStringOrdering) {
-#if defined(OS_ANDROID)
-  const char* const kArguments[] = {"chrome"};
-  base::test::ScopedCommandLine scoped_command_line;
-  base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
-  command_line->InitFromArgv(1, kArguments);
-
-  // Do it for regular devices.
-  ASSERT_FALSE(command_line->HasSwitch(switches::kUseMobileUserAgent));
-  CheckUserAgentStringOrdering(false);
-
-  // Do it for mobile devices.
-  command_line->AppendSwitch(switches::kUseMobileUserAgent);
-  ASSERT_TRUE(command_line->HasSwitch(switches::kUseMobileUserAgent));
-  CheckUserAgentStringOrdering(true);
-#else
-  CheckUserAgentStringOrdering(false);
-#endif
-}
-
-TEST(ChromeContentBrowserClientTest, UserAgentMetadata) {
-  ChromeContentBrowserClient content_browser_client;
-  auto metadata = content_browser_client.GetUserAgentMetadata();
-
-  std::string major_version = version_info::GetMajorVersionNumber();
-
-  // According to spec, Sec-CH-UA should contain what project the browser is
-  // based on (i.e. Chromium in this case) as well as the actual product.
-  // In CHROMIUM_BRANDING builds this will check chromium twice. That should be
-  // ok though.
-
-  const blink::UserAgentBrandVersion chromium_brand_version = {"Chromium",
-                                                               major_version};
-  const blink::UserAgentBrandVersion product_brand_version = {
-      version_info::GetProductName(), version_info::GetMajorVersionNumber()};
-  bool contains_chromium_brand_version = false;
-  bool contains_product_brand_version = false;
-
-  for (const auto& brand_version : metadata.brand_version_list) {
-    if (brand_version == chromium_brand_version) {
-      contains_chromium_brand_version = true;
-    }
-    if (brand_version == product_brand_version) {
-      contains_product_brand_version = true;
-    }
-  }
-
-  EXPECT_TRUE(contains_chromium_brand_version);
-  EXPECT_TRUE(contains_product_brand_version);
-
-  EXPECT_EQ(metadata.full_version, version_info::GetVersionNumber());
-  EXPECT_EQ(metadata.platform_version,
-            content::GetOSVersion(content::IncludeAndroidBuildNumber::Exclude,
-                                  content::IncludeAndroidModel::Exclude));
-  // This makes sure no extra information is added to the platform version.
-  EXPECT_EQ(metadata.platform_version.find(";"), std::string::npos);
-  EXPECT_EQ(metadata.platform, version_info::GetOSType());
-  EXPECT_EQ(metadata.architecture, content::GetLowEntropyCpuArchitecture());
-  EXPECT_EQ(metadata.model, content::BuildModelInfo());
-}
-
-TEST(ChromeContentBrowserClientTest, GenerateBrandVersionList) {
-  blink::UserAgentMetadata metadata;
-
-  metadata.brand_version_list =
-      GenerateBrandVersionList(84, base::nullopt, "84", base::nullopt);
-  std::string brand_list = metadata.SerializeBrandVersionList();
-  EXPECT_EQ(R"(" Not A;Brand";v="99", "Chromium";v="84")", brand_list);
-
-  metadata.brand_version_list =
-      GenerateBrandVersionList(85, base::nullopt, "85", base::nullopt);
-  std::string brand_list_diff = metadata.SerializeBrandVersionList();
-  // Make sure the lists are different for different seeds
-  EXPECT_EQ(R"("Chromium";v="85", " Not;A Brand";v="99")", brand_list_diff);
-  EXPECT_NE(brand_list, brand_list_diff);
-
-  metadata.brand_version_list =
-      GenerateBrandVersionList(84, "Totally A Brand", "84", base::nullopt);
-  std::string brand_list_w_brand = metadata.SerializeBrandVersionList();
-  EXPECT_EQ(
-      R"(" Not A;Brand";v="99", "Chromium";v="84", "Totally A Brand";v="84")",
-      brand_list_w_brand);
-
-  metadata.brand_version_list =
-      GenerateBrandVersionList(84, base::nullopt, "84", "Clean GREASE");
-  std::string brand_list_grease_override = metadata.SerializeBrandVersionList();
-  EXPECT_EQ(R"("Clean GREASE";v="99", "Chromium";v="84")",
-            brand_list_grease_override);
-  EXPECT_NE(brand_list, brand_list_grease_override);
-
-  // Should DCHECK on negative numbers
-  EXPECT_DCHECK_DEATH(
-      GenerateBrandVersionList(-1, base::nullopt, "99", base::nullopt));
-}
-
-TEST(ChromeContentBrowserClientTest, LowEntropyCpuArchitecture) {
-  std::string arch = content::GetLowEntropyCpuArchitecture();
-
-#if defined(OS_WIN) || defined(OS_MAC) || \
-    (defined(OS_POSIX) && !defined(OS_ANDROID))
-  EXPECT_TRUE("arm" == arch || "x86" == arch);
-#else
-  EXPECT_EQ("", arch);
-#endif
-}
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 class ChromeContentSettingsRedirectTest
     : public ChromeContentBrowserClientTest {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 6c2bd0c..c1a574c 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -259,6 +259,7 @@
     "//components/download/content/public",
     "//components/drive",
     "//components/drive:drive_chromeos",
+    "//components/embedder_support:browser_util",
     "//components/enterprise",
     "//components/exo",
     "//components/favicon/core",
@@ -3167,6 +3168,7 @@
     deps += [
       "//chromeos/components/chromebox_for_meetings/features",
       "//chromeos/dbus/chromebox_for_meetings",
+      "//chromeos/services/chromebox_for_meetings/public/cpp",
       "//chromeos/services/chromebox_for_meetings/public/mojom",
     ]
   }
diff --git a/chrome/browser/chromeos/arc/arc_util.cc b/chrome/browser/chromeos/arc/arc_util.cc
index ddac471..cf02910 100644
--- a/chrome/browser/chromeos/arc/arc_util.cc
+++ b/chrome/browser/chromeos/arc/arc_util.cc
@@ -23,7 +23,6 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/chromeos/arc/arc_web_contents_data.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
 #include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
@@ -49,6 +48,7 @@
 #include "components/arc/arc_features.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_util.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user.h"
@@ -659,7 +659,7 @@
   ua_override.ua_string_override = content::BuildUserAgentFromOSAndProduct(
       kOsOverrideForTabletSite, product);
 
-  ua_override.ua_metadata_override = ::GetUserAgentMetadata();
+  ua_override.ua_metadata_override = embedder_support::GetUserAgentMetadata();
   ua_override.ua_metadata_override->platform = "Android";
   ua_override.ua_metadata_override->platform_version = "9";
   ua_override.ua_metadata_override->model = "Chrome tablet";
diff --git a/chrome/browser/chromeos/chromebox_for_meetings/DEPS b/chrome/browser/chromeos/chromebox_for_meetings/DEPS
index 4061ba24..ee17db0 100644
--- a/chrome/browser/chromeos/chromebox_for_meetings/DEPS
+++ b/chrome/browser/chromeos/chromebox_for_meetings/DEPS
@@ -2,6 +2,7 @@
   "+chromeos/dbus/chromebox_for_meetings",
   "+chromeos/components/chromebox_for_meetings/buildflags",
   "+chromeos/components/chromebox_for_meetings/features",
+  "+chromeos/services/chromebox_for_meetings/public/cpp",
   "+chromeos/services/cros_healthd/public/cpp",
 ]
 
diff --git a/chrome/browser/chromeos/crosapi/browser_manager.cc b/chrome/browser/chromeos/crosapi/browser_manager.cc
index 21868f9f..bfc180c 100644
--- a/chrome/browser/chromeos/crosapi/browser_manager.cc
+++ b/chrome/browser/chromeos/crosapi/browser_manager.cc
@@ -219,7 +219,7 @@
     return;
   }
 
-  if (state_ == State::CREATING_LOG_FILE) {
+  if (state_ == State::CREATING_LOG_FILE || state_ == State::STARTING) {
     LOG(WARNING) << "lacros-chrome is in the process of launching";
     return;
   }
@@ -235,31 +235,31 @@
 }
 
 bool BrowserManager::GetFeedbackDataSupported() const {
-  return browser_service_version_ >= kGetFeedbackDataMinVersion;
+  return browser_service_.is_bound() && browser_service_.is_connected() &&
+         browser_service_.version() >= kGetFeedbackDataMinVersion;
 }
 
 void BrowserManager::GetFeedbackData(GetFeedbackDataCallback callback) {
-  DCHECK(browser_service_.is_connected());
   DCHECK(GetFeedbackDataSupported());
   browser_service_->GetFeedbackData(std::move(callback));
 }
 
 bool BrowserManager::GetHistogramsSupported() const {
-  return browser_service_version_ >= kGetHistogramsMinVersion;
+  return browser_service_.is_bound() && browser_service_.is_connected() &&
+         browser_service_.version() >= kGetHistogramsMinVersion;
 }
 
 void BrowserManager::GetHistograms(GetHistogramsCallback callback) {
-  DCHECK(browser_service_.is_connected());
   DCHECK(GetHistogramsSupported());
   browser_service_->GetHistograms(std::move(callback));
 }
 
 bool BrowserManager::GetActiveTabUrlSupported() const {
-  return browser_service_version_ >= kGetActiveTabUrlMinVersion;
+  return browser_service_.is_bound() && browser_service_.is_connected() &&
+         browser_service_.version() >= kGetActiveTabUrlMinVersion;
 }
 
 void BrowserManager::GetActiveTabUrl(GetActiveTabUrlCallback callback) {
-  DCHECK(browser_service_.is_connected());
   DCHECK(GetActiveTabUrlSupported());
   browser_service_->GetActiveTabUrl(std::move(callback));
 }
@@ -395,15 +395,11 @@
   channel.PrepareToPassRemoteEndpoint(&options, &command_line);
 
   // TODO(crbug.com/1124490): Support multiple mojo connections from lacros.
-  browser_service_ = browser_util::SendMojoInvitationToLacrosChrome(
+  CrosapiManager::Get()->SendInvitation(
       environment_provider_.get(), channel.TakeLocalEndpoint(),
       base::BindOnce(&BrowserManager::OnMojoDisconnected,
                      weak_factory_.GetWeakPtr()),
-      base::BindOnce(&BrowserManager::OnCrosapiReceiverReceived,
-                     weak_factory_.GetWeakPtr()));
-
-  browser_service_.QueryVersion(
-      base::BindOnce(&BrowserManager::OnBrowserServiceVersionReady,
+      base::BindOnce(&BrowserManager::OnCrosapiConnected,
                      weak_factory_.GetWeakPtr()));
 
   // Create the lacros-chrome subprocess.
@@ -422,16 +418,12 @@
   channel.RemoteProcessLaunchAttempted();
 }
 
-void BrowserManager::OnCrosapiReceiverReceived(
-    mojo::PendingReceiver<mojom::Crosapi> pending_receiver) {
+void BrowserManager::OnCrosapiConnected(
+    mojo::Remote<mojom::BrowserService> browser_service) {
   DCHECK_EQ(state_, State::STARTING);
-  // Transfer the disconnect handler from BrowserService to Crosapi.
-  browser_service_.set_disconnect_handler(base::OnceClosure());
-  CrosapiManager::Get()->BindCrosapi(
-      std::move(pending_receiver),
-      base::BindOnce(&BrowserManager::OnMojoDisconnected,
-                     weak_factory_.GetWeakPtr()));
+
   state_ = State::RUNNING;
+  browser_service_ = std::move(browser_service);
   base::UmaHistogramMediumTimes("ChromeOS.Lacros.StartTime",
                                 base::TimeTicks::Now() - lacros_launch_time_);
   // Set the launch-on-login pref every time lacros-chrome successfully starts,
@@ -519,10 +511,6 @@
     observer.OnMojoDisconnected();
 }
 
-void BrowserManager::OnBrowserServiceVersionReady(uint32_t version) {
-  browser_service_version_ = version;
-}
-
 void BrowserManager::SetDeviceAccountPolicy(const std::string& policy_blob) {
   environment_provider_->SetDeviceAccountPolicy(policy_blob);
 }
diff --git a/chrome/browser/chromeos/crosapi/browser_manager.h b/chrome/browser/chromeos/crosapi/browser_manager.h
index 11e6d3a..6739903 100644
--- a/chrome/browser/chromeos/crosapi/browser_manager.h
+++ b/chrome/browser/chromeos/crosapi/browser_manager.h
@@ -160,6 +160,10 @@
   void OnCrosapiReceiverReceived(
       mojo::PendingReceiver<mojom::Crosapi> pending_receiver);
 
+  // Called when crosapi connection is established.
+  void OnCrosapiConnected(
+      mojo::Remote<crosapi::mojom::BrowserService> browser_service);
+
   // Called when the Mojo connection to lacros-chrome is disconnected.
   // It may be "just a Mojo error" or "lacros-chrome crash".
   // In either case, terminates lacros-chrome, because there's no longer a
@@ -194,9 +198,6 @@
   // For example, "87.0.0.1 dev", "86.0.4240.38 beta".
   std::string browser_version_;
 
-  // Version of BrowserService mojo interface.
-  uint32_t browser_service_version_ = 0;
-
   // Called when the binary download completes.
   LoadCompleteCallback load_complete_callback_;
 
diff --git a/chrome/browser/chromeos/crosapi/browser_util.cc b/chrome/browser/chromeos/crosapi/browser_util.cc
index 8df4a24..94cb551 100644
--- a/chrome/browser/chromeos/crosapi/browser_util.cc
+++ b/chrome/browser/chromeos/crosapi/browser_util.cc
@@ -93,36 +93,6 @@
   (*map)[T::Uuid_] = T::Version_;
 }
 
-mojom::BrowserInitParamsPtr GetBrowserInitParams(
-    EnvironmentProvider* environment_provider) {
-  auto params = mojom::BrowserInitParams::New();
-  params->crosapi_version = crosapi::mojom::Crosapi::Version_;
-  params->deprecated_ash_metrics_enabled_has_value = true;
-  PrefService* local_state = g_browser_process->local_state();
-  params->ash_metrics_enabled =
-      local_state->GetBoolean(metrics::prefs::kMetricsReportingEnabled);
-  params->ash_metrics_managed =
-      local_state->IsManagedPreference(metrics::prefs::kMetricsReportingEnabled)
-          ? mojom::MetricsReportingManaged::kManaged
-          : mojom::MetricsReportingManaged::kNotManaged;
-
-  params->session_type = environment_provider->GetSessionType();
-  params->device_mode = environment_provider->GetDeviceMode();
-  params->interface_versions = GetInterfaceVersions();
-  params->default_paths = environment_provider->GetDefaultPaths();
-  params->device_account_gaia_id =
-      environment_provider->GetDeviceAccountGaiaId();
-  // TODO(crbug.com/1093194): This should be updated to a new value when
-  // the long term fix is made in ash-chrome, atomically.
-  params->exo_ime_support =
-      crosapi::mojom::ExoImeSupport::kConsumedByImeWorkaround;
-  params->cros_user_id_hash = chromeos::ProfileHelper::GetUserIdHashFromProfile(
-      ProfileManager::GetPrimaryUserProfile());
-  params->device_account_policy = GetDeviceAccountPolicy(environment_provider);
-
-  return params;
-}
-
 }  // namespace
 
 // When this feature is enabled, Lacros will be available on stable channel.
@@ -241,28 +211,34 @@
   return versions;
 }
 
-mojo::Remote<crosapi::mojom::BrowserService> SendMojoInvitationToLacrosChrome(
-    EnvironmentProvider* environment_provider,
-    mojo::PlatformChannelEndpoint local_endpoint,
-    base::OnceClosure mojo_disconnected_callback,
-    base::OnceCallback<void(mojo::PendingReceiver<crosapi::mojom::Crosapi>)>
-        crosapi_callback) {
-  mojo::OutgoingInvitation invitation;
-  mojo::Remote<crosapi::mojom::BrowserService> browser_service;
-  browser_service.Bind(mojo::PendingRemote<crosapi::mojom::BrowserService>(
-      invitation.AttachMessagePipe(0 /* token */), /*version=*/0));
-  browser_service.set_disconnect_handler(std::move(mojo_disconnected_callback));
+mojom::BrowserInitParamsPtr GetBrowserInitParams(
+    EnvironmentProvider* environment_provider) {
+  auto params = mojom::BrowserInitParams::New();
+  params->crosapi_version = crosapi::mojom::Crosapi::Version_;
+  params->deprecated_ash_metrics_enabled_has_value = true;
+  PrefService* local_state = g_browser_process->local_state();
+  params->ash_metrics_enabled =
+      local_state->GetBoolean(metrics::prefs::kMetricsReportingEnabled);
+  params->ash_metrics_managed =
+      local_state->IsManagedPreference(metrics::prefs::kMetricsReportingEnabled)
+          ? mojom::MetricsReportingManaged::kManaged
+          : mojom::MetricsReportingManaged::kNotManaged;
 
-  // This is for backward compatibility.
-  // TODO(crbug.com/1156033): Remove InitDeprecated() invocation when lacros
-  // becomes mature enough.
-  browser_service->InitDeprecated(GetBrowserInitParams(environment_provider));
+  params->session_type = environment_provider->GetSessionType();
+  params->device_mode = environment_provider->GetDeviceMode();
+  params->interface_versions = GetInterfaceVersions();
+  params->default_paths = environment_provider->GetDefaultPaths();
+  params->device_account_gaia_id =
+      environment_provider->GetDeviceAccountGaiaId();
+  // TODO(crbug.com/1093194): This should be updated to a new value when
+  // the long term fix is made in ash-chrome, atomically.
+  params->exo_ime_support =
+      crosapi::mojom::ExoImeSupport::kConsumedByImeWorkaround;
+  params->cros_user_id_hash = chromeos::ProfileHelper::GetUserIdHashFromProfile(
+      ProfileManager::GetPrimaryUserProfile());
+  params->device_account_policy = GetDeviceAccountPolicy(environment_provider);
 
-  browser_service->RequestCrosapiReceiver(std::move(crosapi_callback));
-  mojo::OutgoingInvitation::Send(std::move(invitation),
-                                 base::kNullProcessHandle,
-                                 std::move(local_endpoint));
-  return browser_service;
+  return params;
 }
 
 base::ScopedFD CreateStartupData(EnvironmentProvider* environment_provider) {
diff --git a/chrome/browser/chromeos/crosapi/browser_util.h b/chrome/browser/chromeos/crosapi/browser_util.h
index b7d16d5..3e97cdec 100644
--- a/chrome/browser/chromeos/crosapi/browser_util.h
+++ b/chrome/browser/chromeos/crosapi/browser_util.h
@@ -70,6 +70,11 @@
 // Returns the UUID and version for all tracked interfaces. Exposed for testing.
 base::flat_map<base::Token, uint32_t> GetInterfaceVersions();
 
+// Returns the initial parameter to be passed to Crosapi client,
+// such as lacros-chrome.
+mojom::BrowserInitParamsPtr GetBrowserInitParams(
+    EnvironmentProvider* environment_provider);
+
 // Invite the lacros-chrome to the mojo universe.
 // Queue messages to establish the mojo connection, so that the passed IPC is
 // available already when lacros-chrome accepts the invitation.
diff --git a/chrome/browser/chromeos/crosapi/crosapi_manager.cc b/chrome/browser/chromeos/crosapi/crosapi_manager.cc
index 7d7d6de..1b35a8c 100644
--- a/chrome/browser/chromeos/crosapi/crosapi_manager.cc
+++ b/chrome/browser/chromeos/crosapi/crosapi_manager.cc
@@ -6,8 +6,17 @@
 
 #include <utility>
 
+#include "base/bind.h"
+#include "base/check.h"
+#include "base/process/process_handle.h"
+#include "base/stl_util.h"
+#include "chrome/browser/chromeos/crosapi/browser_util.h"
 #include "chrome/browser/chromeos/crosapi/crosapi_ash.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/platform/platform_channel.h"
+#include "mojo/public/cpp/system/invitation.h"
 
 namespace crosapi {
 namespace {
@@ -16,6 +25,97 @@
 
 }  // namespace
 
+// Handles a flow to invite crosapi client (such as lacros-chrome) to Mojo
+// universe.
+// - Bind the given end point to BrowserService.
+// - Queuing an IPC to call InitDeprecated for backward compatibility.
+// - Queuing another IPC to call RequestCrosapiReceiver to obtain the
+//   pending_receiver from the client.
+// - Then, send the invitation to crosapi.
+// - On Crosapi receiver is arrived, it is bound to CrosapiAsh, then
+//   query BrowserService version.
+// - Finally, on version of BrowserService got available, completion_callback is
+//   invoked.
+class CrosapiManager::InvitationFlow {
+ public:
+  InvitationFlow(
+      base::OnceClosure disconnect_handler,
+      base::OnceCallback<void(mojo::Remote<crosapi::mojom::BrowserService>)>
+          completion_callback)
+      : disconnect_handler_(std::move(disconnect_handler)),
+        completion_callback_(std::move(completion_callback)) {}
+  InvitationFlow(const InvitationFlow&) = delete;
+  InvitationFlow& operator=(const InvitationFlow&) = delete;
+  ~InvitationFlow() = default;
+
+  void Run(EnvironmentProvider* environment_provider,
+           mojo::PlatformChannelEndpoint local_endpoint) {
+    mojo::OutgoingInvitation invitation;
+    browser_service_.Bind(mojo::PendingRemote<crosapi::mojom::BrowserService>(
+        invitation.AttachMessagePipe(/*token=*/0), /*version=*/0));
+    browser_service_.set_disconnect_handler(base::BindOnce(
+        &InvitationFlow::OnDisconnected, weak_factory_.GetWeakPtr()));
+
+    // This is for backward compatibility.
+    // TODO(crbug.com/1156033): Remove InitDeprecated() invocation when lacros
+    // becomes mature enough.
+    browser_service_->InitDeprecated(
+        browser_util::GetBrowserInitParams(environment_provider));
+
+    browser_service_->RequestCrosapiReceiver(
+        base::BindOnce(&InvitationFlow::OnCrosapiReceiverReceived,
+                       weak_factory_.GetWeakPtr()));
+    mojo::OutgoingInvitation::Send(std::move(invitation),
+                                   base::kNullProcessHandle,
+                                   std::move(local_endpoint));
+  }
+
+ private:
+  void OnDisconnected() {
+    // Preserve the callback before destroying itself.
+    auto disconnect_handler = std::move(disconnect_handler_);
+    OnComplete();  // |this| is deleted here.
+
+    if (!disconnect_handler.is_null())
+      std::move(disconnect_handler).Run();
+  }
+
+  void OnCrosapiReceiverReceived(
+      mojo::PendingReceiver<crosapi::mojom::Crosapi> pending_receiver) {
+    auto* crosapi_manager = CrosapiManager::Get();
+    crosapi_manager->crosapi_->BindReceiver(std::move(pending_receiver),
+                                            std::move(disconnect_handler_));
+    browser_service_.QueryVersion(base::BindOnce(
+        &InvitationFlow::OnVersionReady, weak_factory_.GetWeakPtr()));
+  }
+
+  void OnVersionReady(uint32_t version) {
+    // Preserve needed members before destroying itself.
+    auto browser_service = std::move(browser_service_);
+    auto completion_callback = std::move(completion_callback_);
+    // OnComplete here invalidates WeakPtr so disconnect_handler set to
+    // BrowserService is invalidated.
+    OnComplete();  // |this| is deleted here.
+
+    std::move(completion_callback).Run(std::move(browser_service));
+  }
+
+  void OnComplete() {
+    auto* crosapi_manager = CrosapiManager::Get();
+    base::EraseIf(crosapi_manager->pending_invitation_flow_list_,
+                  [this](const std::unique_ptr<InvitationFlow>& ptr) {
+                    return ptr.get() == this;
+                  });
+  }
+
+  mojo::Remote<crosapi::mojom::BrowserService> browser_service_;
+  base::OnceClosure disconnect_handler_;
+  base::OnceCallback<void(mojo::Remote<crosapi::mojom::BrowserService>)>
+      completion_callback_;
+
+  base::WeakPtrFactory<InvitationFlow> weak_factory_{this};
+};
+
 CrosapiManager* CrosapiManager::Get() {
   DCHECK(g_instance);
   return g_instance;
@@ -31,11 +131,17 @@
   g_instance = nullptr;
 }
 
-void CrosapiManager::BindCrosapi(
-    mojo::PendingReceiver<mojom::Crosapi> pending_receiver,
-    base::OnceClosure disconnect_handler) {
-  crosapi_->BindReceiver(std::move(pending_receiver),
-                         std::move(disconnect_handler));
+void CrosapiManager::SendInvitation(
+    EnvironmentProvider* environment_provider,
+    mojo::PlatformChannelEndpoint local_endpoint,
+    base::OnceClosure disconnect_handler,
+    base::OnceCallback<void(mojo::Remote<crosapi::mojom::BrowserService>)>
+        completion_callback) {
+  DCHECK(!completion_callback.is_null());
+  pending_invitation_flow_list_.push_back(std::make_unique<InvitationFlow>(
+      std::move(disconnect_handler), std::move(completion_callback)));
+  pending_invitation_flow_list_.back()->Run(environment_provider,
+                                            std::move(local_endpoint));
 }
 
 }  // namespace crosapi
diff --git a/chrome/browser/chromeos/crosapi/crosapi_manager.h b/chrome/browser/chromeos/crosapi/crosapi_manager.h
index c23778a..ebe6e81 100644
--- a/chrome/browser/chromeos/crosapi/crosapi_manager.h
+++ b/chrome/browser/chromeos/crosapi/crosapi_manager.h
@@ -6,16 +6,23 @@
 #define CHROME_BROWSER_CHROMEOS_CROSAPI_CROSAPI_MANAGER_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/callback.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace mojo {
+class PlatformChannelEndpoint;
+}  // namespace mojo
 
 namespace crosapi {
 namespace mojom {
+class BrowserService;
 class Crosapi;
 }  // namespace mojom
 
 class CrosapiAsh;
+class EnvironmentProvider;
 
 // Maintains the crosapi connection provided by ash-chrome.
 class CrosapiManager {
@@ -28,15 +35,27 @@
   CrosapiManager& operator=(const CrosapiManager&) = delete;
   ~CrosapiManager();
 
-  // Binds the given receiver to the CrosapiAsh implementation.
-  // |disconnect_handler| is called, when the connection is lost.
-  void BindCrosapi(mojo::PendingReceiver<mojom::Crosapi> pending_receiver,
-                   base::OnceClosure disconnect_handler);
-
-  // TODO(crbug.com/1148448): Move invitation sending flow to here.
+  // Binds local_endpoint to BrowserService, and invites to the Mojo universe.
+  // Then, request Crosapi pending receiver to the client, and on its callback,
+  // binds it to |crosapi_|.
+  // Also, BrowserService's version is queried, and on its completion,
+  // |completion_callback| is called. |disconnect_handler| invocation is bound
+  // to BrowserService at first, but on Crosapi binding, it is transferred to
+  // Crosapi. So, before Crosapi binding, |disconnect_handler| is called on
+  // BrowserService disconnection. After Crosapi binding, it is called on
+  // Crosapi disconnection, but not on BrowserService disconnection.
+  void SendInvitation(
+      EnvironmentProvider* environment_provider,
+      mojo::PlatformChannelEndpoint local_endpoint,
+      base::OnceClosure disconnect_handler,
+      base::OnceCallback<void(mojo::Remote<mojom::BrowserService>)>
+          completion_callback);
 
  private:
+  class InvitationFlow;
+
   std::unique_ptr<CrosapiAsh> crosapi_;
+  std::vector<std::unique_ptr<InvitationFlow>> pending_invitation_flow_list_;
 };
 
 }  // namespace crosapi
diff --git a/chrome/browser/chromeos/crosapi/keystore_service_ash.cc b/chrome/browser/chromeos/crosapi/keystore_service_ash.cc
index fbf9b825..dfea023 100644
--- a/chrome/browser/chromeos/crosapi/keystore_service_ash.cc
+++ b/chrome/browser/chromeos/crosapi/keystore_service_ash.cc
@@ -78,13 +78,9 @@
 
 }  // namespace
 
-KeystoreServiceAsh::KeystoreServiceAsh() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-}
+KeystoreServiceAsh::KeystoreServiceAsh() = default;
 
-KeystoreServiceAsh::~KeystoreServiceAsh() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-}
+KeystoreServiceAsh::~KeystoreServiceAsh() = default;
 
 void KeystoreServiceAsh::BindReceiver(
     mojo::PendingReceiver<mojom::KeystoreService> receiver) {
diff --git a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc
index f8741f3..9c96738 100644
--- a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc
+++ b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc
@@ -100,11 +100,11 @@
 
   // TODO(crbug.com/1124490): Support multiple mojo connections from lacros.
   mojo::PlatformChannel channel;
-  browser_service_ = browser_util::SendMojoInvitationToLacrosChrome(
+  CrosapiManager::Get()->SendInvitation(
       environment_provider_.get(), channel.TakeLocalEndpoint(),
       base::BindOnce(&TestMojoConnectionManager::OnMojoDisconnected,
                      weak_factory_.GetWeakPtr()),
-      base::BindOnce(&TestMojoConnectionManager::OnCrosapiReceiverReceived,
+      base::BindOnce(&TestMojoConnectionManager::OnCrosapiConnected,
                      weak_factory_.GetWeakPtr()));
 
   base::ScopedFD startup_fd =
@@ -129,14 +129,9 @@
   }
 }
 
-void TestMojoConnectionManager::OnCrosapiReceiverReceived(
-    mojo::PendingReceiver<crosapi::mojom::Crosapi> pending_receiver) {
-  // Transfer the disconnect handler to Crosapi.
-  browser_service_.set_disconnect_handler(base::OnceClosure());
-  CrosapiManager::Get()->BindCrosapi(
-      std::move(pending_receiver),
-      base::BindOnce(&TestMojoConnectionManager::OnMojoDisconnected,
-                     weak_factory_.GetWeakPtr()));
+void TestMojoConnectionManager::OnCrosapiConnected(
+    mojo::Remote<crosapi::mojom::BrowserService> browser_service) {
+  browser_service_ = std::move(browser_service);
   LOG(INFO) << "Connection to lacros-chrome is established.";
 }
 
diff --git a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h
index 7910380..26ecb09 100644
--- a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h
+++ b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h
@@ -19,7 +19,7 @@
 
 namespace crosapi {
 namespace mojom {
-class Crosapi;
+class BrowserService;
 }  // namespace mojom
 
 // An extension of BrowserManager to help set up and manage the mojo connections
@@ -53,9 +53,9 @@
   // Called when a client, such as a test launcher, attempts to connect.
   void OnTestingSocketAvailable();
 
-  // Called when PendingReceiver of Crosapi is passed from lacros-chrome.
-  void OnCrosapiReceiverReceived(
-      mojo::PendingReceiver<crosapi::mojom::Crosapi> pending_receiver);
+  // Called when Crosapi is connected.
+  void OnCrosapiConnected(
+      mojo::Remote<crosapi::mojom::BrowserService> browser_service);
 
   // Called when the Mojo connection to lacros-chrome is disconnected.
   // It may be "just a Mojo error" or "test is finished".
diff --git a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager_unittest.cc b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager_unittest.cc
index b1f01e9..2b56105 100644
--- a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager_unittest.cc
+++ b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager_unittest.cc
@@ -6,7 +6,11 @@
 
 #include <fcntl.h>
 
+#include <memory>
+#include <utility>
+
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_path_watcher.h"
@@ -20,10 +24,15 @@
 #include "base/test/multiprocess_test.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_timeouts.h"
+#include "chrome/browser/chromeos/crosapi/crosapi_manager.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#include "chromeos/login/login_state/login_state.h"
 #include "chromeos/startup/startup_switches.h"
+#include "components/account_id/account_id.h"
+#include "components/user_manager/fake_user_manager.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/platform/socket_utils_posix.h"
@@ -76,18 +85,38 @@
 using TestMojoConnectionManagerTest = testing::Test;
 
 TEST_F(TestMojoConnectionManagerTest, ConnectWithBrowser) {
-  // Constructing BrowserInitParams requires local state prefs.
-  ScopedTestingLocalState local_state(TestingBrowserProcess::GetGlobal());
-
   // Create temp dir before task environment, just in case lingering tasks need
   // to access it.
   base::ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
   // Use IO type to support the FileDescriptorWatcher API on POSIX.
+  // TestingProfileManager instantiated below requires a TaskRunner.
   base::test::TaskEnvironment task_environment{
       base::test::TaskEnvironment::MainThreadType::IO};
 
+  chromeos::LoginState::Initialize();
+  base::ScopedClosureRunner login_state_teardown(
+      base::BindOnce(&chromeos::LoginState::Shutdown));
+
+  // Constructing CrosapiManager requires ProfileManager.
+  // Also, constructing BrowserInitParams requires local state prefs.
+  TestingProfileManager testing_profile_manager(
+      TestingBrowserProcess::GetGlobal());
+  ASSERT_TRUE(testing_profile_manager.SetUp());
+
+  // Set up UserManager to fake the login state.
+  user_manager::FakeUserManager user_manager;
+  user_manager.Initialize();
+  base::ScopedClosureRunner user_manager_teardown(
+      base::BindOnce(base::BindLambdaForTesting(
+          [&user_manager]() { user_manager.Destroy(); })));
+  const AccountId account = AccountId::FromUserEmail("test@test");
+  const user_manager::User* user = user_manager.AddUser(account);
+  user_manager.UserLoggedIn(account, user->username_hash(), false, false);
+
+  auto crosapi_manager = std::make_unique<CrosapiManager>();
+
   // Ash-chrome queues an invitation, drop a socket and wait for connection.
   std::string socket_path =
       temp_dir.GetPath().MaybeAsASCII() + "/lacros.socket";
diff --git a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
index b277dfd..e78e384e 100644
--- a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
+++ b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "chromeos/dbus/kerberos/kerberos_client.h"
 #include "chromeos/dbus/kerberos/kerberos_service.pb.h"
 #include "chromeos/network/onc/variable_expander.h"
@@ -931,12 +932,18 @@
 
 void KerberosCredentialsManager::OnTicketExpiryNotificationClick(
     const std::string& principal_name) {
+  // The correct URL path for Kerberos accounts subpage, according to the
+  // Kerberos settings section flag.
+  const std::string kSubpagePath =
+      chromeos::features::IsKerberosSettingsSectionEnabled()
+          ? chromeos::settings::mojom::kKerberosAccountsV2SubpagePath
+          : chromeos::settings::mojom::kKerberosAccountsSubpagePath;
+
   // TODO(https://crbug.com/952245): Right now, the reauth dialog is tied to the
   // settings. Consider creating a standalone reauth dialog.
   chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
       primary_profile_,
-      chromeos::settings::mojom::kKerberosAccountsSubpagePath +
-          std::string("?kerberos_reauth=") +
+      kSubpagePath + "?kerberos_reauth=" +
           net::EscapeQueryParamValue(principal_name, false /* use_plus */));
 
   // Close last! |principal_name| is owned by the notification.
diff --git a/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc b/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc
index 1bbd1137..a744551 100644
--- a/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc
+++ b/chrome/browser/chromeos/web_applications/camera_system_web_app_info.cc
@@ -7,8 +7,9 @@
 #include "chrome/browser/chromeos/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
-#include "chromeos/components/camera_app_ui/resources.h"
+#include "chromeos/components/camera_app_ui/resources/strings/grit/chromeos_camera_app_strings.h"
 #include "chromeos/components/camera_app_ui/url_constants.h"
+#include "chromeos/grit/chromeos_camera_app_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
 std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForCameraSystemWebApp() {
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index ae08e5bd..b048d55 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -14,7 +14,6 @@
 #include "base/synchronization/lock.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/policy/policy_test_utils.h"
@@ -27,6 +26,7 @@
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/pref_names.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/metrics/content/subprocess_metrics_provider.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_pref_names.h"
@@ -1073,7 +1073,7 @@
 IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, UserAgentVersion) {
   const GURL gurl = accept_ch_with_lifetime_url();
 
-  blink::UserAgentMetadata ua = ::GetUserAgentMetadata();
+  blink::UserAgentMetadata ua = embedder_support::GetUserAgentMetadata();
 
   // Navigate to a page that opts-into the header: the value should end with
   // the major version, and not contain the full version.
@@ -1095,7 +1095,7 @@
 IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, UAHintsTabletMode) {
   const GURL gurl = accept_ch_with_lifetime_url();
 
-  blink::UserAgentMetadata ua = ::GetUserAgentMetadata();
+  blink::UserAgentMetadata ua = embedder_support::GetUserAgentMetadata();
 
   // First request: only minimal hints, no tablet override.
   SetClientHintExpectationsOnMainFrame(false);
@@ -1202,7 +1202,7 @@
                                                      Browser* browser_b) {
   const GURL gurl = accept_ch_with_lifetime_url();
 
-  blink::UserAgentMetadata ua = ::GetUserAgentMetadata();
+  blink::UserAgentMetadata ua = embedder_support::GetUserAgentMetadata();
 
   // Navigate |browser_a| to a page that opts-into the header: the value should
   // end with the major version, and not contain the full version.
diff --git a/chrome/browser/client_hints/client_hints_factory.cc b/chrome/browser/client_hints/client_hints_factory.cc
index 8088f088..29a8614 100644
--- a/chrome/browser/client_hints/client_hints_factory.cc
+++ b/chrome/browser/client_hints/client_hints_factory.cc
@@ -5,10 +5,10 @@
 #include "chrome/browser/client_hints/client_hints_factory.h"
 
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "components/client_hints/browser/client_hints.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -46,7 +46,7 @@
   return new client_hints::ClientHints(
       context, g_browser_process->network_quality_tracker(),
       HostContentSettingsMapFactory::GetForProfile(context),
-      GetUserAgentMetadata(), local_state);
+      embedder_support::GetUserAgentMetadata(), local_state);
 }
 
 content::BrowserContext* ClientHintsFactory::GetBrowserContextToUse(
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc
index d1fc689..aec2154 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc
@@ -19,7 +19,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/data_use_measurement/chrome_data_use_measurement.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/previews/previews_service.h"
@@ -40,6 +39,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/previews/content/previews_ui_service.h"
@@ -233,7 +233,8 @@
           content::GetNetworkConnectionTracker(),
           data_use_measurement::ChromeDataUseMeasurement::GetInstance(),
           db_task_runner, commit_delay, GetClient(),
-          version_info::GetChannelString(chrome::GetChannel()), GetUserAgent());
+          version_info::GetChannelString(chrome::GetChannel()),
+          embedder_support::GetUserAgent());
   data_reduction_proxy::DataReductionProxySettings::
       InitDataReductionProxySettings(profile_prefs, std::move(service));
 
diff --git a/chrome/browser/download/android/intercept_oma_download_navigation_throttle.cc b/chrome/browser/download/android/intercept_oma_download_navigation_throttle.cc
index cf05b50..e520efe 100644
--- a/chrome/browser/download/android/intercept_oma_download_navigation_throttle.cc
+++ b/chrome/browser/download/android/intercept_oma_download_navigation_throttle.cc
@@ -6,8 +6,8 @@
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/download/android/download_controller_base.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_process_host.h"
@@ -89,9 +89,9 @@
 
   DownloadControllerBase::Get()->CreateAndroidDownload(
       base::BindRepeating(&GetWebContents, process_id, routing_id),
-      DownloadInfo(navigation_handle()->GetURL(), original_url,
-                   content_disposition, mime_type, GetUserAgent(),
-                   // TODO(qinmin): Get the cookie from cookie store.
-                   std::string(),
-                   navigation_handle()->GetReferrer().url.spec()));
+      DownloadInfo(
+          navigation_handle()->GetURL(), original_url, content_disposition,
+          mime_type, embedder_support::GetUserAgent(),
+          // TODO(qinmin): Get the cookie from cookie store.
+          std::string(), navigation_handle()->GetReferrer().url.spec()));
 }
diff --git a/chrome/browser/enterprise/connectors/file_system/download_controller.cc b/chrome/browser/enterprise/connectors/file_system/download_controller.cc
index 03cf7d1..53d7f35 100644
--- a/chrome/browser/enterprise/connectors/file_system/download_controller.cc
+++ b/chrome/browser/enterprise/connectors/file_system/download_controller.cc
@@ -11,7 +11,10 @@
 namespace enterprise_connectors {
 
 FileSystemDownloadController::FileSystemDownloadController(
-    download::DownloadItem* download_item) {}
+    download::DownloadItem* download_item)
+    : local_file_path_(download_item->GetFullPath()),
+      target_file_name_(download_item->GetTargetFilePath().BaseName()),
+      file_size_(download_item->GetTotalBytes()) {}
 
 FileSystemDownloadController::~FileSystemDownloadController() = default;
 
@@ -41,7 +44,6 @@
     // Terminate for now until the whole workflow is implemented.
     // Callback with false so that temporary file gets handled?
     // Remember to report via callback(true) when upload is done in:
-    // TODO(https://crbug.com/1157635) OnWholeFileUploadResponse(), and
     // TODO(https://crbug.com/1157636) OnCommitUploadSessionResponse().
     std::move(download_callback_).Run(false);
   } else {
@@ -116,10 +118,28 @@
 
 std::unique_ptr<OAuth2ApiCallFlow>
 FileSystemDownloadController::CreateUploadApiCall() {
-  // TODO(https://crbug.com/1157635, https://crbug.com/1157636) check file size:
-  // if big, start an upload session to the chunked file upload endpoint; else,
-  // do a whole file upload.
-  return nullptr;
+  if (file_size_ > BoxApiCallFlow::kChunkFileUploadMinSize) {
+    return nullptr;
+    // TODO(https://crbug.com/1157636) Start an upload session to the chunked
+    // file upload endpoint instead.
+  } else {
+    return std::make_unique<BoxWholeFileUploadApiCallFlow>(
+        base::BindOnce(&FileSystemDownloadController::OnWholeFileUploadResponse,
+                       weak_factory_.GetWeakPtr()),
+        folder_id_, target_file_name_, local_file_path_);
+  }
+}
+
+void FileSystemDownloadController::OnWholeFileUploadResponse(
+    bool success,
+    int response_code) {
+  if (!EnsureSuccessResponse(success, response_code)) {
+    current_api_call_ = CreateUploadApiCall();
+    return;
+  }
+
+  // Report upload success back to the download thread.
+  std::move(download_callback_).Run(success);
 }
 
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/file_system/download_controller.h b/chrome/browser/enterprise/connectors/file_system/download_controller.h
index 7fdb733..ec62629e 100644
--- a/chrome/browser/enterprise/connectors/file_system/download_controller.h
+++ b/chrome/browser/enterprise/connectors/file_system/download_controller.h
@@ -50,6 +50,7 @@
                                       int response_code,
                                       const std::string& folder_id);
   std::unique_ptr<OAuth2ApiCallFlow> CreateUploadApiCall();
+  void OnWholeFileUploadResponse(bool success, int response_code);
 
   // Callback when API call gives Authenetication Error.
   base::RepeatingCallback<void(void)> authentication_retry_callback_;
@@ -68,6 +69,9 @@
   // Folder id used to specify the destination folder to the Service Provider in
   // the cloud.
   std::string folder_id_;
+  const base::FilePath local_file_path_;
+  const base::FilePath target_file_name_;
+  const size_t file_size_;
 
   base::WeakPtrFactory<FileSystemDownloadController> weak_factory_{this};
 };
diff --git a/chrome/browser/enterprise/connectors/file_system/download_controller_unittest.cc b/chrome/browser/enterprise/connectors/file_system/download_controller_unittest.cc
index 5f6cfe3..67ff38e 100644
--- a/chrome/browser/enterprise/connectors/file_system/download_controller_unittest.cc
+++ b/chrome/browser/enterprise/connectors/file_system/download_controller_unittest.cc
@@ -7,9 +7,12 @@
 #include "chrome/browser/enterprise/connectors/file_system/download_controller.h"
 
 #include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h"
+#include "content/public/test/fake_download_item.h"
 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -17,15 +20,39 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::_;
-using testing::Return;
-
 namespace enterprise_connectors {
 
+class DownloadItemForTest : public content::FakeDownloadItem {
+ public:
+  explicit DownloadItemForTest(base::FilePath::StringPieceType file_name) {
+    if (temp_dir_.CreateUniqueTempDir()) {
+      base::FilePath file_path = temp_dir_.GetPath().Append(file_name);
+      SetTemporaryFilePath(file_path);
+      SetTargetFilePath(file_path.RemoveFinalExtension());
+    }
+  }
+  void SetTemporaryFilePath(const base::FilePath& file_path) {
+    file_path_ = file_path;
+  }
+
+  // GetFullPath() is expected to be merged with GetTemporaryPath(). Using
+  // GetFullPath() in DownloadController but using GetTemporaryPath() in test
+  // code here for clarity.
+  base::FilePath GetTemporaryFilePath() const override { return file_path_; }
+  const base::FilePath& GetFullPath() const override { return file_path_; }
+
+ protected:
+  base::ScopedTempDir temp_dir_;
+
+  base::FilePath file_path_;
+};
+
 class FileSystemDownloadControllerTest : public testing::Test {
  public:
   FileSystemDownloadControllerTest()
-      : controller_(item_),
+      : test_item_(FILE_PATH_LITERAL(
+            "file_system_download_controller_test.txt.crdownload")),
+        controller_(&test_item_),
         url_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_url_loader_factory_)) {
@@ -36,29 +63,46 @@
                        weak_factory_.GetWeakPtr()));
   }
 
+  void SetUp() override {
+    if (test_item_.GetTemporaryFilePath().empty() ||
+        !base::WriteFile(test_item_.GetTemporaryFilePath(),
+                         "FileSystemDownloadControllerTest")) {
+      FAIL() << "Failed to create temporary file "
+             << test_item_.GetTemporaryFilePath();
+    }
+  }
+
   void AddFetchResult(const std::string& url,
                       net::HttpStatusCode response_code,
                       const std::string& body) {
     test_url_loader_factory_.AddResponse(url, body, response_code);
   }
 
-  void AuthenRetry() { ++authentication_retry_; }
+  void AuthenRetry() {
+    ++authentication_retry_;
+    std::move(quit_closure_).Run();
+  }
+
   void DownloadComplete(bool success) {
     download_callback_called_ = true;
     upload_success_ = success;
+    std::move(quit_closure_).Run();
   }
 
  protected:
-  download::DownloadItem* item_{nullptr};
+  DownloadItemForTest test_item_;
+
   FileSystemDownloadController controller_;
 
   int authentication_retry_{0};
   bool download_callback_called_{false};
   bool upload_success_{false};
 
-  base::test::SingleThreadTaskEnvironment task_environment_;
+  base::test::TaskEnvironment task_environment_;
   data_decoder::test::InProcessDataDecoder decoder_;
-  // Decoder can only be declared after SingleThreadTaskEnvironment.
+  // Decoder can only be declared after TaskEnvironment.
+
+  base::OnceClosure quit_closure_;
 
   // For controller.TryTask().
   network::TestURLLoaderFactory test_url_loader_factory_;
@@ -69,16 +113,20 @@
 TEST_F(FileSystemDownloadControllerTest, HasExistingFolder) {
   AddFetchResult(kFileSystemBoxFindFolderUrl, net::HTTP_OK,
                  kFileSystemBoxFindFolderResponseBody);
+  AddFetchResult(
+      kFileSystemBoxWholeFileUploadUrl, net::HTTP_CREATED,
+      std::string());  // Dummy body since we are not reading from body.
 
   controller_.TryTask(url_factory_, "dummytoken");
-  base::RunLoop().RunUntilIdle();
+  base::RunLoop run_loop;
+  quit_closure_ = run_loop.QuitClosure();
+  run_loop.Run();
 
   ASSERT_EQ(authentication_retry_, 0);
-  EXPECT_TRUE(download_callback_called_);
-  EXPECT_FALSE(upload_success_);
-  // Until entire upload flow is implemented to notify success in final step.
   EXPECT_EQ(controller_.GetFolderIdForTesting(),
             kFileSystemBoxFindFolderResponseFolderId);
+  EXPECT_TRUE(download_callback_called_);
+  EXPECT_TRUE(upload_success_);
 }
 
 TEST_F(FileSystemDownloadControllerTest, NoExistingFolder) {
@@ -86,16 +134,20 @@
                  kFileSystemBoxFindFolderResponseEmptyEntriesList);
   AddFetchResult(kFileSystemBoxCreateFolderUrl, net::HTTP_CREATED,
                  kFileSystemBoxCreateFolderResponseBody);
+  AddFetchResult(
+      kFileSystemBoxWholeFileUploadUrl, net::HTTP_CREATED,
+      std::string());  // Dummy body since we are not reading from body.
 
   controller_.TryTask(url_factory_, "dummytoken");
-  base::RunLoop().RunUntilIdle();
+  base::RunLoop run_loop;
+  quit_closure_ = run_loop.QuitClosure();
+  run_loop.Run();
 
   EXPECT_EQ(authentication_retry_, 0);
-  EXPECT_TRUE(download_callback_called_);
-  EXPECT_FALSE(upload_success_);
-  // Until entire upload flow is implemented to notify success in final step.
   EXPECT_EQ(controller_.GetFolderIdForTesting(),
             kFileSystemBoxCreateFolderResponseFolderId);
+  EXPECT_TRUE(download_callback_called_);
+  EXPECT_TRUE(upload_success_);
 }
 
 TEST_F(FileSystemDownloadControllerTest, AuthenticationFailureInTryTask) {
@@ -104,7 +156,9 @@
   AddFetchResult(kFileSystemBoxFindFolderUrl, net::HTTP_UNAUTHORIZED,
                  std::string());
   controller_.TryTask(url_factory_, "dummytoken");
-  base::RunLoop().RunUntilIdle();
+  base::RunLoop run_loop1;
+  quit_closure_ = run_loop1.QuitClosure();
+  run_loop1.Run();
   ASSERT_EQ(authentication_retry_, 1);
 
   // Should be retrying authentication, no report via callback yet.
@@ -115,14 +169,18 @@
   // Check that it's able to continue after authentication has been refreshed.
   AddFetchResult(kFileSystemBoxFindFolderUrl, net::HTTP_OK,
                  kFileSystemBoxFindFolderResponseBody);
+  AddFetchResult(
+      kFileSystemBoxWholeFileUploadUrl, net::HTTP_CREATED,
+      std::string());  // Dummy body since we are not reading from body.
   controller_.TryTask(url_factory_, "dummytoken");
-  base::RunLoop().RunUntilIdle();
+  base::RunLoop run_loop2;
+  quit_closure_ = run_loop2.QuitClosure();
+  run_loop2.Run();
   ASSERT_EQ(authentication_retry_, 1);
-  EXPECT_TRUE(download_callback_called_);
-  EXPECT_FALSE(upload_success_);
-  // Until entire upload flow is implemented to notify success in final step.
   EXPECT_EQ(controller_.GetFolderIdForTesting(),
             kFileSystemBoxFindFolderResponseFolderId);
+  EXPECT_TRUE(download_callback_called_);
+  EXPECT_TRUE(upload_success_);
 }
 
 TEST_F(FileSystemDownloadControllerTest, UnexpectedFailureInTryTask) {
@@ -133,7 +191,9 @@
   AddFetchResult(kFileSystemBoxCreateFolderUrl, net::HTTP_NOT_FOUND,
                  std::string());
   controller_.TryTask(url_factory_, "dummytoken");
-  base::RunLoop().RunUntilIdle();
+  base::RunLoop run_loop;
+  quit_closure_ = run_loop.QuitClosure();
+  run_loop.Run();
   ASSERT_EQ(authentication_retry_, 0);
 
   // Should just report failure via callback.
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index ae5e66ff..73ed8c5 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -812,6 +812,7 @@
     "//components/download/public/common:public",
     "//components/drive",
     "//components/embedder_support",
+    "//components/embedder_support:browser_util",
     "//components/enterprise",
     "//components/favicon/content",
     "//components/feedback",
diff --git a/chrome/browser/extensions/chrome_extension_function_unittest.cc b/chrome/browser/extensions/chrome_extension_function_unittest.cc
index 1977653..3f2e31e 100644
--- a/chrome/browser/extensions/chrome_extension_function_unittest.cc
+++ b/chrome/browser/extensions/chrome_extension_function_unittest.cc
@@ -5,11 +5,16 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/dcheck_is_on.h"
+#include "base/memory/scoped_refptr.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "extensions/browser/extension_function.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace extensions {
@@ -75,4 +80,49 @@
   EXPECT_TRUE(function->did_respond());
 }
 
+// Verifies that destroying the ExtensionFunction without responding is ok if
+// the extension has been unloaded.
+TEST_F(ChromeExtensionFunctionUnitTest, DestructionWithoutResponseOnUnload) {
+  InitializeEmptyExtensionService();
+  scoped_refptr<const Extension> extension = ExtensionBuilder("foo").Build();
+  service()->AddExtension(extension.get());
+  ASSERT_TRUE(registry()->enabled_extensions().Contains(extension->id()));
+
+  auto function = base::MakeRefCounted<ValidationFunction>(false);
+  function->set_extension(extension);
+  function->set_browser_context(browser_context());
+
+  service()->DisableExtension(extension->id(),
+                              disable_reason::DISABLE_USER_ACTION);
+  ASSERT_TRUE(registry()->disabled_extensions().Contains(extension->id()));
+
+  // Destroying the extension function without responding if the extension has
+  // been unloaded should not cause a crash.
+  function.reset();
+}
+
+#if DCHECK_IS_ON()
+using ChromeExtensionFunctionDeathTest = ChromeExtensionFunctionUnitTest;
+
+// Verify that destroying the extension function without responding causes a
+// DCHECK failure.
+TEST_F(ChromeExtensionFunctionDeathTest, DestructionWithoutResponse) {
+  ASSERT_DEATH(
+      {
+        InitializeEmptyExtensionService();
+        scoped_refptr<const Extension> extension =
+            ExtensionBuilder("foo").Build();
+        service()->AddExtension(extension.get());
+
+        ASSERT_TRUE(registry()->enabled_extensions().Contains(extension->id()));
+
+        auto function = base::MakeRefCounted<ValidationFunction>(false);
+        function->set_extension(extension);
+        function->set_browser_context(browser_context());
+        function.reset();
+      },
+      "");
+}
+#endif  // DCHECK_IS_ON()
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.cc b/chrome/browser/extensions/chrome_extensions_browser_client.cc
index da3b4b4..943d5c7 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.cc
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.cc
@@ -15,7 +15,6 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/extensions/activity_log/activity_log.h"
 #include "chrome/browser/extensions/api/chrome_extensions_api_client.h"
@@ -51,6 +50,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/sessions/content/session_tab_helper.h"
 #include "components/update_client/update_client.h"
 #include "components/version_info/version_info.h"
@@ -533,7 +533,7 @@
 }
 
 std::string ChromeExtensionsBrowserClient::GetUserAgent() const {
-  return ::GetUserAgent();
+  return embedder_support::GetUserAgent();
 }
 
 bool ChromeExtensionsBrowserClient::ShouldSchemeBypassNavigationChecks(
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
index 217e35ac..258b726a 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
@@ -475,29 +475,30 @@
   return path_info;
 }
 
-base::FilePath ChromeFileSystemAccessPermissionContext::GetCommonDirectoryPath(
-    blink::mojom::CommonDirectory directory) {
+base::FilePath
+ChromeFileSystemAccessPermissionContext::GetWellKnownDirectoryPath(
+    blink::mojom::WellKnownDirectory directory) {
   int key = base::PATH_START;
   switch (directory) {
-    case blink::mojom::CommonDirectory::kDefault:
+    case blink::mojom::WellKnownDirectory::kDefault:
       key = chrome::DIR_USER_DOCUMENTS;
       break;
-    case blink::mojom::CommonDirectory::kDirDesktop:
+    case blink::mojom::WellKnownDirectory::kDirDesktop:
       key = base::DIR_USER_DESKTOP;
       break;
-    case blink::mojom::CommonDirectory::kDirDocuments:
+    case blink::mojom::WellKnownDirectory::kDirDocuments:
       key = chrome::DIR_USER_DOCUMENTS;
       break;
-    case blink::mojom::CommonDirectory::kDirDownloads:
+    case blink::mojom::WellKnownDirectory::kDirDownloads:
       key = chrome::DIR_DEFAULT_DOWNLOADS;
       break;
-    case blink::mojom::CommonDirectory::kDirMusic:
+    case blink::mojom::WellKnownDirectory::kDirMusic:
       key = chrome::DIR_USER_MUSIC;
       break;
-    case blink::mojom::CommonDirectory::kDirPictures:
+    case blink::mojom::WellKnownDirectory::kDirPictures:
       key = chrome::DIR_USER_PICTURES;
       break;
-    case blink::mojom::CommonDirectory::kDirVideos:
+    case blink::mojom::WellKnownDirectory::kDirVideos:
       key = chrome::DIR_USER_VIDEOS;
       break;
   }
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
index d97af838..e7210fd0 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.h
@@ -64,8 +64,8 @@
                               const base::FilePath& path,
                               const PathType type) override;
   PathInfo GetLastPickedDirectory(const url::Origin& origin) override;
-  base::FilePath GetCommonDirectoryPath(
-      blink::mojom::CommonDirectory directory) override;
+  base::FilePath GetWellKnownDirectoryPath(
+      blink::mojom::WellKnownDirectory directory) override;
 
   ContentSetting GetReadGuardContentSetting(const url::Origin& origin);
   ContentSetting GetWriteGuardContentSetting(const url::Origin& origin);
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index f94bfdf..aef430c4 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
@@ -464,29 +464,29 @@
 }
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
-       GetCommonDirectoryPath_Base_OK) {
+       GetWellKnownDirectoryPath_Base_OK) {
   base::ScopedPathOverride user_desktop_override(
       base::DIR_USER_DESKTOP, temp_dir_.GetPath(), true, true);
-  EXPECT_EQ(permission_context_->GetCommonDirectoryPath(
-                blink::mojom::CommonDirectory::kDirDesktop),
+  EXPECT_EQ(permission_context_->GetWellKnownDirectoryPath(
+                blink::mojom::WellKnownDirectory::kDirDesktop),
             temp_dir_.GetPath());
 }
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
-       GetCommonDirectoryPath_Chrome_OK) {
+       GetWellKnownDirectoryPath_Chrome_OK) {
   base::ScopedPathOverride user_documents_override(
       chrome::DIR_USER_DOCUMENTS, temp_dir_.GetPath(), true, true);
-  EXPECT_EQ(permission_context_->GetCommonDirectoryPath(
-                blink::mojom::CommonDirectory::kDirDocuments),
+  EXPECT_EQ(permission_context_->GetWellKnownDirectoryPath(
+                blink::mojom::WellKnownDirectory::kDirDocuments),
             temp_dir_.GetPath());
 }
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
-       GetCommonDirectoryPath_Default) {
+       GetWellKnownDirectoryPath_Default) {
   base::ScopedPathOverride user_documents_override(
       chrome::DIR_USER_DOCUMENTS, temp_dir_.GetPath(), true, true);
-  EXPECT_EQ(permission_context_->GetCommonDirectoryPath(
-                blink::mojom::CommonDirectory::kDefault),
+  EXPECT_EQ(permission_context_->GetWellKnownDirectoryPath(
+                blink::mojom::WellKnownDirectory::kDefault),
             temp_dir_.GetPath());
 }
 
diff --git a/chrome/browser/media/router/providers/cast/chrome_cast_message_handler.cc b/chrome/browser/media/router/providers/cast/chrome_cast_message_handler.cc
index 0b78829..ae2a7167 100644
--- a/chrome/browser/media/router/providers/cast/chrome_cast_message_handler.cc
+++ b/chrome/browser/media/router/providers/cast/chrome_cast_message_handler.cc
@@ -8,10 +8,10 @@
 
 #include "base/bind.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/media/router/data_decoder_util.h"
 #include "components/cast_channel/cast_message_handler.h"
 #include "components/cast_channel/cast_socket_service.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -35,8 +35,8 @@
   static cast_channel::CastMessageHandler* instance =
       new cast_channel::CastMessageHandler(
           cast_channel::CastSocketService::GetInstance(),
-          base::BindRepeating(&ParseJsonFromIoThread), GetUserAgent(),
-          version_info::GetVersionNumber(),
+          base::BindRepeating(&ParseJsonFromIoThread),
+          embedder_support::GetUserAgent(), version_info::GetVersionNumber(),
           g_browser_process->GetApplicationLocale());
   return instance;
 }
diff --git a/chrome/browser/metrics/power/battery_level_provider_win.cc b/chrome/browser/metrics/power/battery_level_provider_win.cc
new file mode 100644
index 0000000..e6881bd
--- /dev/null
+++ b/chrome/browser/metrics/power/battery_level_provider_win.cc
@@ -0,0 +1,194 @@
+// Copyright 2021 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/metrics/power/battery_level_provider.h"
+
+#define INITGUID
+#include <windows.h>  // Must be in front of other Windows header files.
+
+#include <devguid.h>
+#include <poclass.h>
+#include <setupapi.h>
+#include <winioctl.h>
+
+#include <vector>
+
+#include "base/threading/scoped_blocking_call.h"
+#include "base/win/scoped_devinfo.h"
+#include "base/win/scoped_handle.h"
+
+namespace {
+
+// Returns a handle to the battery interface identified by |interface_data|, or
+// nullopt if the request failed. |devices| is a device information set that
+// contains battery devices information, obtained with ::SetupDiGetClassDevs().
+base::win::ScopedHandle GetBatteryHandle(
+    HDEVINFO devices,
+    SP_DEVICE_INTERFACE_DATA* interface_data) {
+  // Query size required to hold |interface_detail|.
+  DWORD required_size = 0;
+  ::SetupDiGetDeviceInterfaceDetail(devices, interface_data, nullptr, 0,
+                                    &required_size, nullptr);
+  DWORD error = ::GetLastError();
+  if (error != ERROR_INSUFFICIENT_BUFFER)
+    return base::win::ScopedHandle();
+
+  // |interface_detail->DevicePath| is variable size.
+  std::vector<uint8_t> raw_buf(required_size);
+  auto* interface_detail =
+      reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(raw_buf.data());
+  interface_detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+  BOOL success = ::SetupDiGetDeviceInterfaceDetail(
+      devices, interface_data, interface_detail, required_size, nullptr,
+      nullptr);
+  if (!success)
+    return base::win::ScopedHandle();
+
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  base::win::ScopedHandle battery(
+      ::CreateFile(interface_detail->DevicePath, GENERIC_READ | GENERIC_WRITE,
+                   FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
+                   FILE_ATTRIBUTE_NORMAL, nullptr));
+  return battery;
+}
+
+// Returns the current tag for |battery| handle, or nullopt if there is no
+// battery at this slot or the request failed. Each battery in a particular slot
+// is assigned a tag, which must be used for all queries for information. For
+// more details, see
+// https://docs.microsoft.com/en-us/windows/win32/power/battery-information
+base::Optional<uint64_t> GetBatteryTag(HANDLE battery) {
+  ULONG battery_tag = 0;
+  ULONG wait = 0;
+  DWORD bytes_returned = 0;
+  BOOL success = ::DeviceIoControl(
+      battery, IOCTL_BATTERY_QUERY_TAG, &wait, sizeof(wait), &battery_tag,
+      sizeof(battery_tag), &bytes_returned, nullptr);
+  if (!success)
+    return base::nullopt;
+  return battery_tag;
+}
+
+// Returns BATTERY_INFORMATION structure containing battery information, given
+// battery handle and tag, or nullopt if the request failed. Battery handle and
+// tag are obtained with GetBatteryHandle() and GetBatteryTag(), respectively.
+base::Optional<BATTERY_INFORMATION> GetBatteryInformation(
+    HANDLE battery,
+    uint64_t battery_tag) {
+  BATTERY_QUERY_INFORMATION query_information = {};
+  query_information.BatteryTag = battery_tag;
+  query_information.InformationLevel = BatteryInformation;
+  BATTERY_INFORMATION battery_information = {};
+  DWORD bytes_returned;
+  BOOL success = ::DeviceIoControl(
+      battery, IOCTL_BATTERY_QUERY_INFORMATION, &query_information,
+      sizeof(query_information), &battery_information,
+      sizeof(battery_information), &bytes_returned, nullptr);
+  if (!success)
+    return base::nullopt;
+  return battery_information;
+}
+
+// Returns BATTERY_STATUS structure containing battery state, given battery
+// handle and tag, or nullopt if the request failed. Battery handle and tag are
+// obtained with GetBatteryHandle() and GetBatteryTag(), respectively.
+base::Optional<BATTERY_STATUS> GetBatteryStatus(HANDLE battery,
+                                                uint64_t battery_tag) {
+  BATTERY_WAIT_STATUS wait_status = {};
+  wait_status.BatteryTag = battery_tag;
+  BATTERY_STATUS battery_status;
+  DWORD bytes_returned;
+  BOOL success = ::DeviceIoControl(
+      battery, IOCTL_BATTERY_QUERY_STATUS, &wait_status, sizeof(wait_status),
+      &battery_status, sizeof(battery_status), &bytes_returned, nullptr);
+  if (!success)
+    return base::nullopt;
+  return battery_status;
+}
+
+}  // namespace
+
+class BatteryLevelProviderWin : public BatteryLevelProvider {
+ public:
+  BatteryLevelProviderWin() = default;
+  ~BatteryLevelProviderWin() override = default;
+
+  base::Optional<BatteryState> GetBatteryState() override;
+};
+
+std::unique_ptr<BatteryLevelProvider> BatteryLevelProvider::Create() {
+  return std::make_unique<BatteryLevelProviderWin>();
+}
+
+base::Optional<BatteryLevelProvider::BatteryState>
+BatteryLevelProviderWin::GetBatteryState() {
+  const base::TimeTicks capture_time = base::TimeTicks::Now();
+
+  // Battery interfaces are enumerated at every sample to detect when a new
+  // interface is added, and avoid holding dangling handles when a battery is
+  // disconnected.
+  base::win::ScopedDevInfo devices(
+      ::SetupDiGetClassDevs(&GUID_DEVICE_BATTERY, nullptr, nullptr,
+                            DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
+  if (!devices.is_valid())
+    return base::nullopt;
+
+  uint64_t total_max_capacity = 0;
+  uint64_t total_current_capacity = 0;
+  bool external_connected = false;
+
+  // The algorithm to enumerate battery devices is taken from
+  // https://docs.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices
+  // Limit search to 8 batteries max. A system may have several battery slots
+  // and each slot may hold an actual battery.
+  for (int device_index = 0; device_index < 8; ++device_index) {
+    SP_DEVICE_INTERFACE_DATA interface_data = {};
+    interface_data.cbSize = sizeof(interface_data);
+
+    BOOL success =
+        ::SetupDiEnumDeviceInterfaces(devices.get(), 0, &GUID_DEVCLASS_BATTERY,
+                                      device_index, &interface_data);
+    if (!success) {
+      // Exit condition.
+      if (ERROR_NO_MORE_ITEMS == ::GetLastError())
+        break;
+      continue;
+    }
+
+    base::win::ScopedHandle battery =
+        GetBatteryHandle(devices.get(), &interface_data);
+    if (!battery.IsValid())
+      continue;
+
+    base::Optional<uint64_t> battery_tag = GetBatteryTag(battery.Get());
+    if (!battery_tag)
+      continue;
+    auto battery_information =
+        GetBatteryInformation(battery.Get(), *battery_tag);
+    auto battery_status = GetBatteryStatus(battery.Get(), *battery_tag);
+    // If any of the values were not available.
+    if (!battery_information.has_value() || !battery_status.has_value())
+      continue;
+
+    // The state is set as connected if any battery has access to AC power.
+    external_connected = external_connected ||
+                         (battery_status->PowerState & BATTERY_POWER_ON_LINE);
+
+    // Total capacity is averaged across multiple batteries.
+    total_max_capacity += battery_information->FullChargedCapacity;
+    total_current_capacity += battery_status->Capacity;
+  }
+
+  // Avoid invalid division.
+  if (total_max_capacity == 0)
+    return base::nullopt;
+
+  DCHECK_LE(total_current_capacity, total_max_capacity);
+  double charge_level = static_cast<double>(total_current_capacity) /
+                        static_cast<double>(total_max_capacity);
+
+  return BatteryState{charge_level, !external_connected, capture_time};
+}
diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc
index f427933..bd1b26f 100644
--- a/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -26,7 +26,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/net/profile_network_context_service_factory.h"
@@ -41,6 +40,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/common/pref_names.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
@@ -1391,7 +1391,7 @@
   ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language));
   EXPECT_EQ(system ? kNoAcceptLanguage : "en-US,en;q=0.9", accept_language);
   ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent));
-  EXPECT_EQ(::GetUserAgent(), user_agent);
+  EXPECT_EQ(embedder_support::GetUserAgent(), user_agent);
 
   // Change AcceptLanguages preferences, and check that headers are updated.
   // First, A single language.
@@ -1402,7 +1402,7 @@
   ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language2));
   EXPECT_EQ(system ? kNoAcceptLanguage : "zu", accept_language2);
   ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent2));
-  EXPECT_EQ(::GetUserAgent(), user_agent2);
+  EXPECT_EQ(embedder_support::GetUserAgent(), user_agent2);
 
   // Second, a single language with locale.
   browser()->profile()->GetPrefs()->SetString(language::prefs::kAcceptLanguages,
@@ -1412,7 +1412,7 @@
   ASSERT_TRUE(FetchHeaderEcho("accept-language", &accept_language3));
   EXPECT_EQ(system ? kNoAcceptLanguage : "zu-ZA,zu;q=0.9", accept_language3);
   ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent3));
-  EXPECT_EQ(::GetUserAgent(), user_agent3);
+  EXPECT_EQ(embedder_support::GetUserAgent(), user_agent3);
 
   // Third, a list with multiple languages. Incognito mode should return only
   // the first.
@@ -1430,7 +1430,7 @@
               accept_language4);
   }
   ASSERT_TRUE(FetchHeaderEcho("user-agent", &user_agent4));
-  EXPECT_EQ(::GetUserAgent(), user_agent4);
+  EXPECT_EQ(embedder_support::GetUserAgent(), user_agent4);
 }
 
 // First part of testing enable referrers. Check that referrers are enabled by
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index 1075f22..b3fad2b 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -21,7 +21,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/component_updater/crl_set_component_installer.h"
 #include "chrome/browser/component_updater/first_party_sets_component_installer.h"
 #include "chrome/browser/component_updater/tls_deprecation_config_component_installer.h"
@@ -35,6 +34,7 @@
 #include "chrome/common/google_url_loader_throttle.h"
 #include "chrome/common/pref_names.h"
 #include "components/certificate_transparency/ct_known_logs.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/net_log/net_export_file_writer.h"
 #include "components/net_log/net_log_proxy_source.h"
 #include "components/network_session_configurator/common/network_features.h"
@@ -584,7 +584,7 @@
 
   network_context_params->enable_brotli = true;
 
-  network_context_params->user_agent = GetUserAgent();
+  network_context_params->user_agent = embedder_support::GetUserAgent();
 
   // Disable referrers by default. Any consumer that enables referrers should
   // respect prefs::kEnableReferrers from the appropriate pref store.
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc
index 0b444d08..14c0d07 100644
--- a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc
+++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc
@@ -101,7 +101,6 @@
   impression.ignore_timeout_duration = ignore_timeout_duration;
   it->second->impressions.emplace_back(std::move(impression));
   it->second->last_shown_ts = clock_->Now();
-  impression_map_.emplace(guid, &it->second->impressions.back());
   SetNeedsUpdate(type, true /*needs_update*/);
   MaybeUpdateDb(type);
 }
@@ -124,9 +123,9 @@
 }
 
 const Impression* ImpressionHistoryTrackerImpl::GetImpression(
-    const std::string& guid) const {
-  auto it = impression_map_.find(guid);
-  return it == impression_map_.end() ? nullptr : it->second;
+    SchedulerClientType type,
+    const std::string& guid) {
+  return GetImpressionInternal(type, guid);
 }
 
 void ImpressionHistoryTrackerImpl::GetImpressionDetail(
@@ -154,13 +153,16 @@
                          : ActionButtonType::kUnknownAction;
   switch (action_data.action_type) {
     case UserActionType::kClick:
-      OnClickInternal(action_data.guid, true /*update_db*/);
+      OnClickInternal(action_data.client_type, action_data.guid,
+                      true /*update_db*/);
       break;
     case UserActionType::kButtonClick:
-      OnButtonClickInternal(action_data.guid, button_type, true /*update_db*/);
+      OnButtonClickInternal(action_data.client_type, action_data.guid,
+                            button_type, true /*update_db*/);
       break;
     case UserActionType::kDismiss:
-      OnDismissInternal(action_data.guid, true /*update_db*/);
+      OnDismissInternal(action_data.client_type, action_data.guid,
+                        true /*update_db*/);
       break;
   }
 }
@@ -191,7 +193,6 @@
         SetNeedsUpdate(type, true);
       } else {
         impressions.emplace_back(impression);
-        impression_map_.emplace(impression.guid, &impressions.back());
       }
     }
     stats::LogImpressionCount(impressions.size(), type);
@@ -267,14 +268,16 @@
         CheckConsecutiveDismiss(client_state, &dismisses);
         break;
       case UserFeedback::kClick:
-        OnClickInternal(impression->guid, false /*update_db*/);
+        OnClickInternal(client_state->type, impression->guid,
+                        false /*update_db*/);
         break;
       case UserFeedback::kHelpful:
-        OnButtonClickInternal(impression->guid, ActionButtonType::kHelpful,
-                              false /*update_db*/);
+        OnButtonClickInternal(client_state->type, impression->guid,
+                              ActionButtonType::kHelpful, false /*update_db*/);
         break;
       case UserFeedback::kNotHelpful:
-        OnButtonClickInternal(impression->guid, ActionButtonType::kUnhelpful,
+        OnButtonClickInternal(client_state->type, impression->guid,
+                              ActionButtonType::kUnhelpful,
                               false /*update_db*/);
         break;
       case UserFeedback::kNoFeedback:
@@ -512,22 +515,36 @@
 }
 
 Impression* ImpressionHistoryTrackerImpl::FindImpressionNeedsUpdate(
+    SchedulerClientType type,
     const std::string& notification_guid) {
-  auto it = impression_map_.find(notification_guid);
-  if (it == impression_map_.end())
-    return nullptr;
-  auto* impression = it->second;
-
-  if (impression->integrated)
+  Impression* impression = GetImpressionInternal(type, notification_guid);
+  if (!impression || impression->integrated)
     return nullptr;
 
-  return it->second;
+  return impression;
+}
+
+Impression* ImpressionHistoryTrackerImpl::GetImpressionInternal(
+    SchedulerClientType type,
+    const std::string& guid) {
+  auto it = client_states_.find(type);
+  if (it == client_states_.end())
+    return nullptr;
+
+  ClientState* client_state = it->second.get();
+  for (auto& impression : client_state->impressions) {
+    if (impression.guid == guid)
+      return &impression;
+  }
+
+  return nullptr;
 }
 
 void ImpressionHistoryTrackerImpl::OnClickInternal(
+    SchedulerClientType type,
     const std::string& notification_guid,
     bool update_db) {
-  auto* impression = FindImpressionNeedsUpdate(notification_guid);
+  auto* impression = FindImpressionNeedsUpdate(type, notification_guid);
   if (!impression)
     return;
 
@@ -545,10 +562,11 @@
 }
 
 void ImpressionHistoryTrackerImpl::OnButtonClickInternal(
+    SchedulerClientType type,
     const std::string& notification_guid,
     ActionButtonType button_type,
     bool update_db) {
-  auto* impression = FindImpressionNeedsUpdate(notification_guid);
+  auto* impression = FindImpressionNeedsUpdate(type, notification_guid);
   if (!impression)
     return;
   auto it = client_states_.find(impression->type);
@@ -577,9 +595,10 @@
 }
 
 void ImpressionHistoryTrackerImpl::OnDismissInternal(
+    SchedulerClientType type,
     const std::string& notification_guid,
     bool update_db) {
-  auto* impression = FindImpressionNeedsUpdate(notification_guid);
+  auto* impression = FindImpressionNeedsUpdate(type, notification_guid);
   if (!impression)
     return;
 
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h
index 1914a83..4e285350 100644
--- a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h
+++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h
@@ -70,7 +70,8 @@
 
   // Queries impression based on guid, returns nullptr if no impression is
   // found.
-  virtual const Impression* GetImpression(const std::string& guid) const = 0;
+  virtual const Impression* GetImpression(SchedulerClientType type,
+                                          const std::string& guid) = 0;
 
   // Queries the impression detail of a given |SchedulerClientType|.
   virtual void GetImpressionDetail(
@@ -108,7 +109,8 @@
   void AnalyzeImpressionHistory() override;
   void GetClientStates(std::map<SchedulerClientType, const ClientState*>*
                            client_states) const override;
-  const Impression* GetImpression(const std::string& guid) const override;
+  const Impression* GetImpression(SchedulerClientType type,
+                                  const std::string& guid) override;
   void GetImpressionDetail(
       SchedulerClientType type,
       ImpressionDetail::ImpressionDetailCallback callback) override;
@@ -165,13 +167,21 @@
   bool NeedsUpdate(SchedulerClientType type) const;
 
   // Finds an impression that needs to update based on notification id.
-  Impression* FindImpressionNeedsUpdate(const std::string& notification_guid);
+  Impression* FindImpressionNeedsUpdate(SchedulerClientType type,
+                                        const std::string& notification_guid);
+  Impression* GetImpressionInternal(SchedulerClientType type,
+                                    const std::string& guid);
 
-  void OnClickInternal(const std::string& notification_guid, bool update_db);
-  void OnButtonClickInternal(const std::string& notification_guid,
+  void OnClickInternal(SchedulerClientType type,
+                       const std::string& notification_guid,
+                       bool update_db);
+  void OnButtonClickInternal(SchedulerClientType type,
+                             const std::string& notification_guid,
                              ActionButtonType button_type,
                              bool update_db);
-  void OnDismissInternal(const std::string& notification_guid, bool update_db);
+  void OnDismissInternal(SchedulerClientType type,
+                         const std::string& notification_guid,
+                         bool update_db);
   void OnCustomNegativeActionCountQueried(
       SchedulerClientType type,
       base::circular_deque<Impression*>* impressions,
@@ -184,10 +194,6 @@
   // clients.
   ClientStates client_states_;
 
-  // Notification guid to Impression map.
-  // TODO(xingliu): Consider to remove this.
-  std::map<std::string, Impression*> impression_map_;
-
   // The storage that persists data.
   std::unique_ptr<CollectionStore<ClientState>> store_;
 
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc b/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc
index 9c9d2aa..315f5185 100644
--- a/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc
@@ -266,7 +266,8 @@
   test_case.expected.back().impressions.emplace_back(expected_impression);
   test_case.expected.back().last_shown_ts = clock()->Now();
   VerifyClientStates(test_case);
-  EXPECT_EQ(*tracker()->GetImpression(kGuid1), expected_impression);
+  EXPECT_EQ(*tracker()->GetImpression(SchedulerClientType::kTest1, kGuid1),
+            expected_impression);
 }
 
 // Verifies that impression loaded from the database can be retrieved correctly.
@@ -276,7 +277,8 @@
   test_case.input.front().impressions.emplace_back(impression);
   CreateTracker(test_case);
   InitTrackerWithData(test_case);
-  EXPECT_EQ(*tracker()->GetImpression(kGuid1), impression);
+  EXPECT_EQ(*tracker()->GetImpression(SchedulerClientType::kTest1, kGuid1),
+            impression);
 }
 
 // If impression has been deleted, click should have no result.
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
index 2c24a36..4b5e3ab 100644
--- a/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
+++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
@@ -360,8 +360,8 @@
     auto client_action_data = action_data;
 
     // Attach custom data if the impression is not expired.
-    const auto* impression =
-        context_->impression_tracker()->GetImpression(action_data.guid);
+    const auto* impression = context_->impression_tracker()->GetImpression(
+        action_data.client_type, action_data.guid);
     if (impression) {
       client_action_data.custom_data = impression->custom_data;
     }
diff --git a/chrome/browser/notifications/scheduler/test/mock_impression_history_tracker.h b/chrome/browser/notifications/scheduler/test/mock_impression_history_tracker.h
index a1cf6df..0396e3e 100644
--- a/chrome/browser/notifications/scheduler/test/mock_impression_history_tracker.h
+++ b/chrome/browser/notifications/scheduler/test/mock_impression_history_tracker.h
@@ -29,7 +29,8 @@
   MOCK_METHOD0(AnalyzeImpressionHistory, void());
   MOCK_CONST_METHOD1(GetClientStates,
                      void(std::map<SchedulerClientType, const ClientState*>*));
-  MOCK_CONST_METHOD1(GetImpression, Impression*(const std::string&));
+  MOCK_METHOD2(GetImpression,
+               const Impression*(SchedulerClientType, const std::string&));
   MOCK_METHOD2(GetImpressionDetail,
                void(SchedulerClientType,
                     ImpressionDetail::ImpressionDetailCallback));
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
index 1a789e0..ac97a28 100644
--- a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
+++ b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
@@ -14,7 +14,6 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/image_fetcher/image_fetcher_service_factory.h"
 #include "chrome/browser/net/system_network_context_manager.h"
@@ -27,6 +26,7 @@
 #include "chrome/browser/transition_manager/full_browser_transition_manager.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_constants.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/feed/feed_feature_list.h"
 #include "components/image_fetcher/core/cached_image_fetcher.h"
 #include "components/image_fetcher/core/image_fetcher_impl.h"
@@ -135,8 +135,8 @@
 
   auto prefetch_network_request_factory =
       std::make_unique<PrefetchNetworkRequestFactoryImpl>(
-          url_loader_factory, chrome::GetChannel(), GetUserAgent(),
-          profile_key->GetPrefs());
+          url_loader_factory, chrome::GetChannel(),
+          embedder_support::GetUserAgent(), profile_key->GetPrefs());
 
   scoped_refptr<base::SequencedTaskRunner> background_task_runner =
       base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
diff --git a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
index 5bfe9d3..8741738 100644
--- a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
@@ -1026,27 +1026,19 @@
   }
 
   base::ElapsedTimer timer;
-  const uint32_t kMaxRetries = 5;
-  uint32_t retries = 0;
   cc::UkmSmoothnessData smoothness_data;
-  base::subtle::Atomic32 version;
-  do {
-    const uint32_t kMaxReadAttempts = 32;
-    version = smoothness->seq_lock.ReadBegin(kMaxReadAttempts);
-    device::OneWriterSeqLock::AtomicReaderMemcpy(
-        &smoothness_data, &smoothness->data, sizeof(cc::UkmSmoothnessData));
-  } while (smoothness->seq_lock.ReadRetry(version) && ++retries < kMaxRetries);
+  bool success = smoothness->Read(smoothness_data);
 
   UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
       "Graphics.Smoothness.Diagnostic.ReadSharedMemoryDuration",
       timer.Elapsed(), base::TimeDelta::FromMicroseconds(1),
       base::TimeDelta::FromMilliseconds(5), 100);
   UMA_HISTOGRAM_BOOLEAN(
-      "Graphics.Smoothness.Diagnostic.ReadSharedMemoryUKMSuccess",
-      retries < kMaxRetries);
+      "Graphics.Smoothness.Diagnostic.ReadSharedMemoryUKMSuccess", success);
 
-  if (retries >= kMaxRetries)
+  if (!success)
     return;
+
   ukm::builders::Graphics_Smoothness_NormalizedPercentDroppedFrames(
       GetDelegate().GetPageUkmSourceId())
       .SetAverage(smoothness_data.avg_smoothness)
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 29fe227f..4da0252 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -320,14 +320,11 @@
         "var visiblePageDimensions ="
         "    viewer.viewport.getPageScreenRect(visiblePage);"
         "var viewportPosition = viewer.viewport.position;"
-        "var screenOffsetX = visiblePageDimensions.x - viewportPosition.x;"
-        "var screenOffsetY = visiblePageDimensions.y - viewportPosition.y;"
-        "if (document.documentElement.hasAttribute("
-        "        'pdf-viewer-update-enabled')) {"
-        "  const scrollParent = viewer.shadowRoot.querySelector('#main');"
-        "  screenOffsetX += scrollParent.offsetLeft;"
-        "  screenOffsetY += scrollParent.offsetTop;"
-        "}"
+        "var scrollParent = viewer.shadowRoot.querySelector('#main');"
+        "var screenOffsetX = visiblePageDimensions.x - viewportPosition.x +"
+        "    scrollParent.offsetLeft;"
+        "var screenOffsetY = visiblePageDimensions.y - viewportPosition.y +"
+        "    scrollParent.offsetTop;"
         "var linkScreenPositionX ="
         "    Math.floor(" +
             base::NumberToString(point->x()) +
diff --git a/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.cc b/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.cc
index c2614cbb..0ff346c 100644
--- a/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.cc
+++ b/chrome/browser/prefetch/search_prefetch/base_search_prefetch_request.cc
@@ -7,10 +7,10 @@
 #include <vector>
 
 #include "build/build_config.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/common/pref_names.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/variations/net/variations_http_headers.h"
@@ -169,7 +169,7 @@
   // https://w3c.github.io/webappsec/specs/upgrade/#feature-detect
   resource_request->headers.SetHeader("Upgrade-Insecure-Requests", "1");
   resource_request->headers.SetHeader(net::HttpRequestHeaders::kUserAgent,
-                                      GetUserAgent());
+                                      embedder_support::GetUserAgent());
   resource_request->headers.SetHeader(content::kCorsExemptPurposeHeaderName,
                                       "prefetch");
   resource_request->headers.SetHeader(
diff --git a/chrome/browser/printing/printing_init.cc b/chrome/browser/printing/printing_init.cc
index f0ae73d..f95496fa 100644
--- a/chrome/browser/printing/printing_init.cc
+++ b/chrome/browser/printing/printing_init.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/printing/printing_init.h"
 
-#include "chrome/browser/chrome_content_browser_client.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/printing/browser/print_manager_utils.h"
 #include "content/public/browser/web_contents.h"
 #include "printing/buildflags/buildflags.h"
@@ -25,7 +25,7 @@
 #else
   printing::PrintViewManagerBasic::CreateForWebContents(web_contents);
 #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
-  CreateCompositeClientIfNeeded(web_contents, GetUserAgent());
+  CreateCompositeClientIfNeeded(web_contents, embedder_support::GetUserAgent());
 }
 
 }  // namespace printing
diff --git a/chrome/browser/profile_resetter/resettable_settings_snapshot.cc b/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
index f3b1c55..46ce94f 100644
--- a/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
+++ b/chrome/browser/profile_resetter/resettable_settings_snapshot.cc
@@ -19,7 +19,6 @@
 #include "base/task_runner_util.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/profile_resetter/profile_reset_report.pb.h"
 #include "chrome/browser/profile_resetter/reset_report_uploader.h"
 #include "chrome/browser/profile_resetter/reset_report_uploader_factory.h"
@@ -29,6 +28,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/strings/grit/components_strings.h"
@@ -237,9 +237,8 @@
   AddPair(list.get(),
           l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE),
           g_browser_process->GetApplicationLocale());
-  AddPair(list.get(),
-          l10n_util::GetStringUTF16(IDS_VERSION_UI_USER_AGENT),
-          GetUserAgent());
+  AddPair(list.get(), l10n_util::GetStringUTF16(IDS_VERSION_UI_USER_AGENT),
+          embedder_support::GetUserAgent());
   std::string version = version_info::GetVersionNumber();
   version += chrome::GetChannelName();
   AddPair(list.get(),
diff --git a/chrome/browser/profiles/reporting_util.cc b/chrome/browser/profiles/reporting_util.cc
index dca115c..083293f7 100644
--- a/chrome/browser/profiles/reporting_util.cc
+++ b/chrome/browser/profiles/reporting_util.cc
@@ -11,12 +11,12 @@
 #include "base/values.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/account_id/account_id.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -112,7 +112,7 @@
 
 base::Value GetContext(Profile* profile) {
   base::Value context(base::Value::Type::DICTIONARY);
-  context.SetStringPath("browser.userAgent", GetUserAgent());
+  context.SetStringPath("browser.userAgent", embedder_support::GetUserAgent());
 
   if (!profile)
     return context;
diff --git a/chrome/browser/resources/chromeos/arc_support/main.html b/chrome/browser/resources/chromeos/arc_support/main.html
index 2d56167..82ba491 100644
--- a/chrome/browser/resources/chromeos/arc_support/main.html
+++ b/chrome/browser/resources/chromeos/arc_support/main.html
@@ -4,7 +4,7 @@
   <title i18n-content="appWindow"></title>
   <link rel="stylesheet" href="chrome://resources/css/roboto.css">
   <link rel="stylesheet" href="chrome://resources/css/bubble.css">
-  <link rel="stylesheet" href="bubble_button.css">
+  <link rel="stylesheet" href="chrome://resources/css/bubble_button.css">
   <link rel="stylesheet" href="chrome://resources/css/overlay.css">
   <link rel="stylesheet" href="controlled_indicator.css">
   <link rel="stylesheet" href="main.css">
@@ -21,7 +21,7 @@
   <script src="chrome://resources/js/event_tracker.js"></script>
   <script src="chrome://resources/js/cr/ui.js"></script>
   <script src="chrome://resources/js/cr/ui/bubble.js"></script>
-  <script src="bubble_button.js"></script>
+  <script src="chrome://resources/js/cr/ui/bubble_button.js"></script>
   <script src="chrome://resources/js/cr/ui/overlay.js"></script>
   <script src="chrome://resources/js/cr/ui/focus_manager.js"></script>
   <script type="module" src="main.js"></script>
diff --git a/chrome/browser/resources/pdf/elements/shared-css.html b/chrome/browser/resources/pdf/elements/shared-css.html
index 365abed..16e61e4 100644
--- a/chrome/browser/resources/pdf/elements/shared-css.html
+++ b/chrome/browser/resources/pdf/elements/shared-css.html
@@ -14,17 +14,17 @@
        ui/webui/resources/cr_elements/shared_vars_css.html. Unfortunately there
        seems to be no great way to share styles with the
        "prefers-color-scheme: dark" @media query selector. */
-    :host-context([pdf-viewer-update-enabled]) cr-action-menu,
-    :host-context([pdf-viewer-update-enabled]) viewer-bookmark {
-        --cr-menu-background-color: var(--google-grey-900);
-        --cr-menu-shadow: rgba(0, 0, 0, .3) 0 1px 2px 0,
-                          rgba(0, 0, 0, .15) 0 3px 6px 2px;
-        --cr-primary-text-color: var(--google-grey-200);
-        --cr-menu-background-focus-color: rgba(var(--google-grey-800-rgb), .6);
-        --cr-menu-background-sheen: rgba(255, 255, 255, .06);
-        --cr-separator-line: var(--cr-separator-height) solid
-            rgba(255, 255, 255, .1);
-      }
+    cr-action-menu,
+    viewer-bookmark {
+      --cr-menu-background-color: var(--google-grey-900);
+      --cr-menu-shadow: rgba(0, 0, 0, .3) 0 1px 2px 0,
+                        rgba(0, 0, 0, .15) 0 3px 6px 2px;
+      --cr-primary-text-color: var(--google-grey-200);
+      --cr-menu-background-focus-color: rgba(var(--google-grey-800-rgb), .6);
+      --cr-menu-background-sheen: rgba(255, 255, 255, .06);
+      --cr-separator-line: var(--cr-separator-height) solid
+          rgba(255, 255, 255, .1);
+    }
 
   </style>
 </template>
diff --git a/chrome/browser/resources/pdf/elements/viewer-bookmark.html b/chrome/browser/resources/pdf/elements/viewer-bookmark.html
index 7d60ee8..df9c4573 100644
--- a/chrome/browser/resources/pdf/elements/viewer-bookmark.html
+++ b/chrome/browser/resources/pdf/elements/viewer-bookmark.html
@@ -1,28 +1,18 @@
     <style>
       #item {
-        align-items: center;
+        align-items: flex-start;
         cursor: pointer;
         display: flex;
-        height: 30px;
+        padding: 5px 0;
         position: relative;
         transition: background-color 100ms ease-out;
       }
 
-      :host-context([pdf-viewer-update-enabled]) #item {
-        align-items: flex-start;
-        height: initial;
-        padding: 5px 0;
-      }
-
       #item:hover {
         background-color: var(--cr-menu-background-focus-color);
       }
 
       #item:active {
-        background-color: rgba(var(--google-grey-900-rgb), 0.25);
-      }
-
-      :host-context([pdf-viewer-update-enabled]) #item:active {
         background-color: rgba(255, 255, 255, 0.25);
       }
 
@@ -30,17 +20,12 @@
         outline: none;
         overflow: hidden;
         text-overflow: ellipsis;
-        white-space: nowrap;
       }
 
       :host-context(.focus-outline-visible) #title:focus {
         outline: auto -webkit-focus-ring-color;
       }
 
-      :host-context([pdf-viewer-update-enabled]) #title {
-        white-space: initial;
-      }
-
       #expand-container {
         --expand-button-size: 28px;
         flex-shrink: 0;
diff --git a/chrome/browser/resources/pdf/index.html b/chrome/browser/resources/pdf/index.html
index 638c16d0..cc61971 100644
--- a/chrome/browser/resources/pdf/index.html
+++ b/chrome/browser/resources/pdf/index.html
@@ -1,6 +1,5 @@
 <!doctype html>
-<html dir="$i18n{textdirection}" lang="$i18n{language}"
-    pdf-viewer-update-enabled>
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
 <head>
   <meta charset="utf-8">
 
@@ -13,7 +12,7 @@
       height: 100%;
     }
 
-    html[pdf-viewer-update-enabled] body {
+    body {
       width: 100%;
     }
   </style>
diff --git a/chrome/browser/resources/pdf/pdf_viewer.html b/chrome/browser/resources/pdf/pdf_viewer.html
index 4deed45..f24b385 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.html
+++ b/chrome/browser/resources/pdf/pdf_viewer.html
@@ -1,6 +1,10 @@
 <style include="pdf-viewer-shared-style cr-hidden-style">
   :host {
     --viewer-pdf-sidenav-width: 300px;
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    width: 100%;
   }
 
   viewer-pdf-sidenav,
@@ -58,26 +62,17 @@
     width: 100%;
   }
 
-  /* Styles only applying when the pdfViewerUpdateEnabled flag is on. */
-
-  :host-context([pdf-viewer-update-enabled]):host {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-    width: 100%;
-  }
-
   #container {
     display: flex;
     flex: 1;
     overflow: hidden;
   }
 
-  :host-context([pdf-viewer-update-enabled]) #plugin {
+  #plugin {
     position: initial;
   }
 
-  :host-context([pdf-viewer-update-enabled]) #content {
+  #content {
     height: 100%;
     left: 0;
     position: sticky;
@@ -85,7 +80,7 @@
     z-index: initial;
   }
 
-  :host-context([pdf-viewer-update-enabled]) #sizer {
+  #sizer {
     top: 0;
     width: 100%;
     z-index: initial;
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index a996c40..aa6e435 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -235,9 +235,6 @@
       },
 
       /** @private */
-      pdfViewerUpdateEnabled_: Boolean,
-
-      /** @private */
       printingEnabled_: {
         type: Boolean,
         value: false,
@@ -287,18 +284,11 @@
   constructor() {
     super();
 
-    // Polymer properties
-
-    this.pdfViewerUpdateEnabled_ =
-        document.documentElement.hasAttribute('pdf-viewer-update-enabled');
-
-    if (this.pdfViewerUpdateEnabled_) {
-      // TODO(dpapad): Add tests after crbug.com/1111459 is fixed.
-      this.sidenavCollapsed_ = Boolean(Number.parseInt(
-          LocalStorageProxyImpl.getInstance().getItem(
-              LOCAL_STORAGE_SIDENAV_COLLAPSED_KEY),
-          10));
-    }
+    // TODO(dpapad): Add tests after crbug.com/1111459 is fixed.
+    this.sidenavCollapsed_ = Boolean(Number.parseInt(
+        LocalStorageProxyImpl.getInstance().getItem(
+            LOCAL_STORAGE_SIDENAV_COLLAPSED_KEY),
+        10));
 
     // Non-Polymer properties
 
@@ -329,21 +319,6 @@
   }
 
   /** @override */
-  getToolbarHeight() {
-    assert(this.paramsParser);
-    this.toolbarEnabled_ =
-        this.paramsParser.shouldShowToolbar(this.originalUrl);
-
-    // The toolbar does not need to be manually accounted in the
-    // PDFViewerUpdate UI.
-    if (this.pdfViewerUpdateEnabled_) {
-      return 0;
-    }
-
-    return this.toolbarEnabled_ ? MATERIAL_TOOLBAR_HEIGHT : 0;
-  }
-
-  /** @override */
   getContent() {
     return /** @type {!HTMLDivElement} */ (this.$$('#content'));
   }
@@ -390,6 +365,9 @@
     this.fileName_ = getFilenameFromURL(this.originalUrl);
     this.title_ = this.fileName_;
 
+    assert(this.paramsParser);
+    this.toolbarEnabled_ =
+        this.paramsParser.shouldShowToolbar(this.originalUrl);
     if (this.toolbarEnabled_) {
       this.getToolbar_().hidden = false;
     }
@@ -416,14 +394,11 @@
    * @private
    */
   handleToolbarKeyEvent_(e) {
-    if (this.pdfViewerUpdateEnabled_) {
-      if (e.key === '\\' && e.ctrlKey) {
-        this.getToolbar_().fitToggle();
-      }
-      // TODO: Add handling for additional relevant hotkeys for the new unified
-      // toolbar.
-      return;
+    if (e.key === '\\' && e.ctrlKey) {
+      this.getToolbar_().fitToggle();
     }
+    // TODO: Add handling for additional relevant hotkeys for the new unified
+    // toolbar.
   }
 
   /**
@@ -544,13 +519,11 @@
       // TODO(dstockwell): set plugin read-only, begin transition
       this.updateProgress(0);
 
-      if (this.pdfViewerUpdateEnabled_) {
-        this.sidenavRestoreState_ = this.sidenavCollapsed_;
-        this.sidenavCollapsed_ = true;
-        if (!this.sidenavRestoreState_) {
-          // Wait for the animation before proceeding.
-          await this.waitForSidenavTransition_();
-        }
+      this.sidenavRestoreState_ = this.sidenavCollapsed_;
+      this.sidenavCollapsed_ = true;
+      if (!this.sidenavRestoreState_) {
+        // Wait for the animation before proceeding.
+        await this.waitForSidenavTransition_();
       }
 
       // TODO(dstockwell): handle save failure
@@ -588,9 +561,7 @@
           await this.inkController_.save(SaveRequestType.ANNOTATION);
       // Data always exists when save is called with requestType = ANNOTATION.
       const result = /** @type {!RequiredSaveResult} */ (saveResult);
-      if (this.pdfViewerUpdateEnabled_) {
-        await this.restoreSidenav_();
-      }
+      await this.restoreSidenav_();
       await this.pluginController_.load(result.fileName, result.dataToSave);
       // Ensure the plugin gets the initial viewport.
       this.pluginController_.afterZoom();
@@ -607,9 +578,7 @@
     }
     this.getToolbar_().toggleAnnotation();
     this.annotationMode_ = false;
-    if (this.pdfViewerUpdateEnabled_) {
-      await this.restoreSidenav_();
-    }
+    await this.restoreSidenav_();
     await this.loaded;
   }
   // </if>
@@ -1115,7 +1084,6 @@
 
   /** @private */
   onSidenavToggleClick_() {
-    assert(this.pdfViewerUpdateEnabled_);
     this.sidenavCollapsed_ = !this.sidenavCollapsed_;
     LocalStorageProxyImpl.getInstance().setItem(
         LOCAL_STORAGE_SIDENAV_COLLAPSED_KEY, this.sidenavCollapsed_ ? 1 : 0);
@@ -1241,13 +1209,6 @@
 }
 
 /**
- * The height of the toolbar along the top of the page. The document will be
- * shifted down by this much in the viewport.
- * @type {number}
- */
-const MATERIAL_TOOLBAR_HEIGHT = 56;
-
-/**
  * Minimum height for the material toolbar to show (px). Should match the media
  * query in index-material.css. If the window is smaller than this at load,
  * leave no space for the toolbar.
diff --git a/chrome/browser/resources/pdf/pdf_viewer_base.js b/chrome/browser/resources/pdf/pdf_viewer_base.js
index 3854fe06..07c2060 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_base.js
+++ b/chrome/browser/resources/pdf/pdf_viewer_base.js
@@ -110,11 +110,6 @@
     this.zoomManager_ = null;
   }
 
-  /** @return {number} The height of the top toolbar */
-  getToolbarHeight() {
-    return 0;
-  }
-
   /**
    * @return {!HTMLDivElement}
    * @protected
@@ -160,11 +155,11 @@
   }
 
   /**
-   * @param {boolean} pdfViewerUpdateEnabled is the feature is enabled.
+   * @param {boolean} isPrintPreview Is the plugin for Print Preview.
    * @return {!HTMLEmbedElement} The plugin
    * @private
    */
-  createPlugin_(pdfViewerUpdateEnabled) {
+  createPlugin_(isPrintPreview) {
     // Create the plugin object dynamically so we can set its src. The plugin
     // element is sized to fill the entire window and is set to be fixed
     // positioning, acting as a viewport. The plugin renders into this viewport
@@ -190,7 +185,7 @@
     plugin.setAttribute('headers', headers);
 
     plugin.setAttribute('background-color', this.getBackgroundColor());
-    plugin.setAttribute('top-toolbar-height', this.getToolbarHeight());
+    plugin.setAttribute('top-toolbar-height', 0);
 
     const javascript = this.browserApi.getStreamInfo().javascript || 'block';
     plugin.setAttribute('javascript', javascript);
@@ -202,7 +197,7 @@
       plugin.toggleAttribute('full-frame', true);
     }
 
-    if (pdfViewerUpdateEnabled) {
+    if (!isPrintPreview) {
       plugin.toggleAttribute('pdf-viewer-update-enabled', true);
     }
 
@@ -229,11 +224,11 @@
     }
 
     // Determine the scrolling container.
-    const pdfViewerUpdateEnabled =
-        document.documentElement.hasAttribute('pdf-viewer-update-enabled');
-    const scrollContainer = pdfViewerUpdateEnabled ?
-        /** @type {!HTMLElement} */ (this.getSizer().offsetParent) :
-        document.documentElement;
+    const isPrintPreview =
+        document.documentElement.hasAttribute('is-print-preview');
+    const scrollContainer = isPrintPreview ?
+        document.documentElement :
+        /** @type {!HTMLElement} */ (this.getSizer().offsetParent);
 
     // Create the viewport.
     const defaultZoom =
@@ -243,7 +238,7 @@
 
     this.viewport_ = new Viewport(
         scrollContainer, this.getSizer(), this.getContent(),
-        getScrollbarWidth(), defaultZoom, this.getToolbarHeight());
+        getScrollbarWidth(), defaultZoom, 0);
     this.viewport_.setViewportChangedCallback(() => this.viewportChanged_());
     this.viewport_.setBeforeZoomCallback(
         () => this.currentController.beforeZoom());
@@ -263,7 +258,7 @@
     }, false);
 
     // Create the plugin.
-    this.plugin_ = this.createPlugin_(pdfViewerUpdateEnabled);
+    this.plugin_ = this.createPlugin_(isPrintPreview);
     this.getContent().appendChild(this.plugin_);
 
     const pluginController = PluginController.getInstance();
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 99894947..92f7b29b 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -312,6 +312,8 @@
     "chromeos/crostini_page/crostini_export_import.m.js",
     "chromeos/crostini_page/crostini_import_confirmation_dialog.m.js",
     "chromeos/crostini_page/crostini_mic_sharing_dialog.m.js",
+    "chromeos/crostini_page/crostini_port_forwarding.m.js",
+    "chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.m.js",
     "chromeos/date_time_page/date_time_page.m.js",
     "chromeos/date_time_page/date_time_types.m.js",
     "chromeos/date_time_page/timezone_browser_proxy.m.js",
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn
index ad7a200f..52648002 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/BUILD.gn
@@ -33,7 +33,11 @@
 }
 
 js_library("crostini_browser_proxy") {
-  deps = [ "//ui/webui/resources/js:cr" ]
+  deps = [
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:load_time_data",
+    "//ui/webui/resources/cr_elements/cr_input:cr_input",
+  ]
 }
 
 js_library("crostini_export_import") {
@@ -122,8 +126,9 @@
     ":crostini_mic_sharing_dialog.m",
 
     #    ":crostini_page.m",
-    #    ":crostini_port_forwarding.m",
-    #    ":crostini_port_forwarding_add_port_dialog.m",
+    ":crostini_port_forwarding.m",
+    ":crostini_port_forwarding_add_port_dialog.m",
+
     #    ":crostini_shared_paths.m",
     #    ":crostini_shared_usb_devices.m",
     #    ":crostini_subpage.m"
@@ -158,7 +163,11 @@
 
 js_library("crostini_browser_proxy.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.m.js" ]
-  deps = [ "//ui/webui/resources/js:cr.m" ]
+  deps = [
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js:load_time_data.m",
+    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
+  ]
   extra_deps = [ ":modulize" ]
 }
 
@@ -239,7 +248,16 @@
 js_library("crostini_port_forwarding.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    ":crostini_browser_proxy.m",
+    ":crostini_port_forwarding_add_port_dialog.m",
+    "../../prefs:prefs_behavior.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu.m",
+    "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m",
+    "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render.m",
+    "//ui/webui/resources/cr_elements/cr_toast:cr_toast.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
   extra_deps = [ ":crostini_port_forwarding_module" ]
 }
@@ -247,7 +265,14 @@
 js_library("crostini_port_forwarding_add_port_dialog.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    ":crostini_browser_proxy.m",
+    "..:metrics_recorder.m",
+    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
+    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
+    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
+    "//ui/webui/resources/js:load_time_data.m",
   ]
   extra_deps = [ ":crostini_port_forwarding_add_port_dialog_module" ]
 }
@@ -354,12 +379,16 @@
   js_file = "crostini_port_forwarding.js"
   html_file = "crostini_port_forwarding.html"
   html_type = "dom-module"
+  auto_imports = os_settings_auto_imports
+  namespace_rewrites = os_settings_namespace_rewrites
 }
 
 polymer_modulizer("crostini_port_forwarding_add_port_dialog") {
   js_file = "crostini_port_forwarding_add_port_dialog.js"
   html_file = "crostini_port_forwarding_add_port_dialog.html"
   html_type = "dom-module"
+  auto_imports = os_settings_auto_imports
+  namespace_rewrites = os_settings_namespace_rewrites
 }
 
 polymer_modulizer("crostini_shared_paths") {
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
index 5713081..a37d1152 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.js
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 // #import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
+// #import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+// #import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
 
 // Identifiers for the default Crostini VM and container.
 /** @type {string} */ /* #export */ const DEFAULT_CROSTINI_VM = 'termina';
@@ -58,6 +60,21 @@
 /* #export */ let CrostiniPortActiveSetting;
 
 /**
+ * @enum {string}
+ */
+/* #export */ const PortState = {
+  VALID: '',
+  INVALID: loadTimeData.getString('crostiniPortForwardingAddError'),
+  DUPLICATE: loadTimeData.getString('crostiniPortForwardingAddExisting'),
+};
+
+/* #export */ const MIN_VALID_PORT_NUMBER =
+    1024;  // Minimum 16-bit integer value.
+/* #export */ const MAX_VALID_PORT_NUMBER =
+    65535;  // Maximum 16-bit integer value.
+
+
+/**
  * @fileoverview A helper object used by the "Linux Apps" (Crostini) section
  * to install and uninstall Crostini.
  */
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.html
index 9c9708e..e46a3e0 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.html
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding.html
@@ -5,13 +5,16 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toast/cr_toast.html">
+<link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/load_time_data.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="crostini_browser_proxy.html">
 <link rel="import" href="crostini_port_forwarding_add_port_dialog.html">
 <link rel="import" href="../../controls/settings_toggle_button.html">
-<link rel="import" href="../../i18n_setup.html">
 <link rel="import" href="../../prefs/prefs_behavior.html">
+<link rel="import" href="../metrics_recorder.html">
 <link rel="import" href="../../settings_shared_css.html">
 
 
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.html
index 15a5157e..551d4d9 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.html
@@ -4,6 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/load_time_data.html">
 <link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="crostini_browser_proxy.html">
 <link rel="import" href="../../settings_shared_css.html">
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js
index d4e98810..96b2b19 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js
@@ -8,18 +8,6 @@
  * fields and clicking add.
  */
 
-/**
- * @enum {string}
- */
-const PortState = {
-  VALID: '',
-  INVALID: loadTimeData.getString('crostiniPortForwardingAddError'),
-  DUPLICATE: loadTimeData.getString('crostiniPortForwardingAddExisting'),
-};
-
-const MIN_VALID_PORT_NUMBER = 1024;   // Minimum 16-bit integer value.
-const MAX_VALID_PORT_NUMBER = 65535;  // Maximum 16-bit integer value.
-
 Polymer({
   is: 'settings-crostini-add-port-dialog',
 
@@ -88,17 +76,17 @@
   },
 
   /**
-   * @return {string} input for the port number.
+   * @return {!CrInputElement} input for the port number.
    */
   get portNumberInput() {
-    return this.$.portNumberInput;
+    return /** @type{!CrInputElement} */ (this.$.portNumberInput);
   },
 
   /**
-   * @return {string} input for the optional port label.
+   * @return {!CrInputElement} input for the optional port label.
    */
   get portLabelInput() {
-    return this.$.portLabelInput;
+    return /** @type{!CrInputElement} */ (this.$.portLabelInput);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index fad9c28..898f5f4 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -96,7 +96,7 @@
                            cr_elements_chromeos_auto_imports + [
                              "chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_browser_proxy.html|AmbientModeBrowserProxy,AmbientModeBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/ambient_mode_page/constants.html|AmbientModeTopicSource,AmbientModeTemperatureUnit,AmbientModeAlbum,AmbientModeSettings,TopicSourceItem",
-                             "chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.html|CrostiniBrowserProxy, CrostiniBrowserProxyImpl",
+                             "chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.html|DEFAULT_CROSTINI_VM, DEFAULT_CROSTINI_CONTAINER, CrostiniPortProtocol, CrostiniSharedPath, CrostiniSharedUsbDevice, CrostiniPortSetting, CrostiniDiskInfo, CrostiniPortActiveSetting, CrostiniBrowserProxy, CrostiniBrowserProxyImpl,PortState,MIN_VALID_PORT_NUMBER, MAX_VALID_PORT_NUMBER",
                              "chrome/browser/resources/settings/chromeos/internet_page/cellular_setup_settings_delegate.html|CellularSetupSettingsDelegate",
                              "chrome/browser/resources/settings/chromeos/internet_page/internet_page_browser_proxy.html|InternetPageBrowserProxy,InternetPageBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/deep_linking_behavior.html|DeepLinkingBehavior",
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js
index b65a894..94f5d8fd 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -15,6 +15,8 @@
 import './crostini_page/crostini_export_import.m.js';
 import './crostini_page/crostini_import_confirmation_dialog.m.js';
 import './crostini_page/crostini_mic_sharing_dialog.m.js';
+import './crostini_page/crostini_port_forwarding.m.js';
+import './crostini_page/crostini_port_forwarding_add_port_dialog.m.js';
 import './internet_page/cellular_setup_dialog.m.js';
 import './internet_page/esim_remove_profile_dialog.m.js';
 import './internet_page/internet_config.m.js';
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_chrome_cleaner_child.html b/chrome/browser/resources/settings/safety_check_page/safety_check_chrome_cleaner_child.html
index 43c43ff..d9f4c2b 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_chrome_cleaner_child.html
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_chrome_cleaner_child.html
@@ -11,6 +11,7 @@
       on-button-click="onButtonClick_"
       managed-icon="[[getManagedIcon_(status_)]]"
       on-click="onRowClick_"
-      row-clickable="[[isRowClickable_(status_)]]">
+      row-clickable="[[isRowClickable_(status_)]]"
+      role="presentation">
   </settings-safety-check-child>
-</template>
\ No newline at end of file
+</template>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_child.html b/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_child.html
index 17bf18817..ebe0b9c 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_child.html
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_extensions_child.html
@@ -10,5 +10,6 @@
     on-click="onRowClick_"
     row-clickable="[[isRowClickable_(status_)]]"
     external
-    managed-icon="[[getManagedIcon_(status_)]]">
+    managed-icon="[[getManagedIcon_(status_)]]"
+    role="presentation">
 </settings-safety-check-child>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_child.html b/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_child.html
index 6e5acd6..e664485 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_child.html
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_passwords_child.html
@@ -8,5 +8,6 @@
     button-class="action-button"
     on-button-click="onButtonClick_"
     on-click="onRowClick_"
-    row-clickable="[[isRowClickable_(status_)]]">
+    row-clickable="[[isRowClickable_(status_)]]"
+    role="presentation">
 </settings-safety-check-child>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_child.html b/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_child.html
index bb01f34..4d337739 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_child.html
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_safe_browsing_child.html
@@ -9,5 +9,6 @@
     on-button-click="onButtonClick_"
     on-click="onRowClick_"
     row-clickable="[[isRowClickable_(status_)]]"
-    managed-icon="[[getManagedIcon_(status_)]]"">
+    managed-icon="[[getManagedIcon_(status_)]]"
+    role="presentation">
 </settings-safety-check-child>
diff --git a/chrome/browser/resources/settings/safety_check_page/safety_check_updates_child.html b/chrome/browser/resources/settings/safety_check_page/safety_check_updates_child.html
index 3bf940bd..8baba523 100644
--- a/chrome/browser/resources/settings/safety_check_page/safety_check_updates_child.html
+++ b/chrome/browser/resources/settings/safety_check_page/safety_check_updates_child.html
@@ -7,5 +7,6 @@
     button-aria-label="$i18n{safetyCheckUpdatesButtonAriaLabel}"
     button-class="action-button"
     on-button-click="onButtonClick_"
-    managed-icon="[[getManagedIcon_(status_)]]">
+    managed-icon="[[getManagedIcon_(status_)]]"
+    role="presentation">
 </settings-safety-check-child>
diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc
index d16e8ff..85e34c5 100644
--- a/chrome/browser/sync/profile_sync_service_android.cc
+++ b/chrome/browser/sync/profile_sync_service_android.cc
@@ -141,14 +141,15 @@
 jboolean ProfileSyncServiceAndroid::IsSyncAllowedByPlatform(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
-  return sync_service_->GetUserSettings()->IsSyncAllowedByPlatform();
+  return !sync_service_->HasDisableReason(
+      syncer::SyncService::DISABLE_REASON_PLATFORM_OVERRIDE);
 }
 
 void ProfileSyncServiceAndroid::SetSyncAllowedByPlatform(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
     jboolean allowed) {
-  sync_service_->GetUserSettings()->SetSyncAllowedByPlatform(allowed);
+  sync_service_->SetSyncAllowedByPlatform(allowed);
 }
 
 jboolean ProfileSyncServiceAndroid::IsSyncFeatureActive(
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 6e6dd2b1..8600e19 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -424,6 +424,7 @@
     "//components/download/content/factory",
     "//components/download/content/public",
     "//components/embedder_support",
+    "//components/embedder_support:browser_util",
     "//components/encrypted_messages:encrypted_message_proto",
     "//components/enterprise",
     "//components/error_page/content/browser",
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index b774b57..ca735a6 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3112,9 +3112,6 @@
       <message name="IDS_READING_LIST_UNREAD" desc="The header for the unread section in the reading list UI.">
         Unread
       </message>
-      <message name="IDS_READING_LIST_READY_FOR_OFFLINE" desc="The string to inform the user that the reading list has offline page ready.">
-        Ready for offline
-      </message>
       <message name="IDS_READING_LIST_SAVED" desc="App-based message shown after user adds a new link to the reading list. [CHAR-LIMIT=32]">
         Added to reading list
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_READING_LIST_READY_FOR_OFFLINE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_READING_LIST_READY_FOR_OFFLINE.png.sha1
deleted file mode 100644
index a833b15..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_READING_LIST_READY_FOR_OFFLINE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e900f21404580852a17a4a152c7f5c8198799897
\ No newline at end of file
diff --git a/chrome/browser/ui/ash/clipboard_util.cc b/chrome/browser/ui/ash/clipboard_util.cc
index d03d9e8..b2dc6e0 100644
--- a/chrome/browser/ui/ash/clipboard_util.cc
+++ b/chrome/browser/ui/ash/clipboard_util.cc
@@ -96,8 +96,10 @@
   // Before modifying the clipboard, remove the old entry in ClipboardHistory.
   // CopyAndMaintainClipboard will write to the clipboard a second time,
   // creating a new entry in clipboard history.
-  ash::ClipboardHistoryController::Get()->DeleteClipboardItemByClipboardData(
-      current_data.get());
+  auto* clipboard_history = ash::ClipboardHistoryController::Get();
+  if (clipboard_history) {
+    clipboard_history->DeleteClipboardItemByClipboardData(current_data.get());
+  }
   CopyAndMaintainClipboard(std::move(current_data), html, png_data,
                            decoded_image);
   std::move(callback).Run(true);
diff --git a/chrome/browser/ui/ash/system_tray_client.cc b/chrome/browser/ui/ash/system_tray_client.cc
index ec885529..9b5cc59 100644
--- a/chrome/browser/ui/ash/system_tray_client.cc
+++ b/chrome/browser/ui/ash/system_tray_client.cc
@@ -123,8 +123,7 @@
   return network_state && network_state->type() == shill::kTypeCellular &&
          network_state->activation_state() ==
              shill::kActivationStateNotActivated &&
-         base::FeatureList::IsEnabled(
-             chromeos::features::kUpdatedCellularActivationUi);
+         chromeos::features::IsCellularActivationUiEnabled();
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index d79ff00..0a136730 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/devtools/devtools_window.h"
 #include "chrome/browser/dom_distiller/tab_utils.h"
 #include "chrome/browser/favicon/favicon_utils.h"
@@ -80,6 +79,7 @@
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/browsing_data/content/browsing_data_helper.h"
 #include "components/dom_distiller/core/url_utils.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/favicon/content/content_favicon_driver.h"
 #include "components/find_in_page/find_tab_helper.h"
 #include "components/find_in_page/find_types.h"
@@ -1489,7 +1489,7 @@
     blink::UserAgentOverride ua_override;
     ua_override.ua_string_override = content::BuildUserAgentFromOSAndProduct(
         kOsOverrideForTabletSite, product);
-    ua_override.ua_metadata_override = GetUserAgentMetadata();
+    ua_override.ua_metadata_override = embedder_support::GetUserAgentMetadata();
     ua_override.ua_metadata_override->mobile = true;
     ua_override.ua_metadata_override->platform =
         kChPlatformOverrideForTabletSite;
diff --git a/chrome/browser/ui/find_bar/find_bar_host_unittest_util.h b/chrome/browser/ui/find_bar/find_bar_host_unittest_util.h
index 479c9c25..abd0e4b 100644
--- a/chrome/browser/ui/find_bar/find_bar_host_unittest_util.h
+++ b/chrome/browser/ui/find_bar/find_bar_host_unittest_util.h
@@ -12,7 +12,7 @@
 #if defined(TOOLKIT_VIEWS)
 void DisableFindBarAnimationsDuringTesting(bool disable);
 #else
-static inline void DisableFindBarAnimationsDuringTesting(bool disable) {}
+inline void DisableFindBarAnimationsDuringTesting(bool disable) {}
 #endif
 
 }  // namespace chrome
diff --git a/chrome/browser/ui/tabs/existing_base_sub_menu_model.cc b/chrome/browser/ui/tabs/existing_base_sub_menu_model.cc
index 11ec428c..1519e12 100644
--- a/chrome/browser/ui/tabs/existing_base_sub_menu_model.cc
+++ b/chrome/browser/ui/tabs/existing_base_sub_menu_model.cc
@@ -77,6 +77,8 @@
     } else {
       AddItem(group_index, item.text);
     }
+
+    SetMayHaveMnemonicsAt(GetItemCount() - 1, item.may_have_mnemonics);
     group_index++;
   }
 }
diff --git a/chrome/browser/ui/tabs/existing_base_sub_menu_model.h b/chrome/browser/ui/tabs/existing_base_sub_menu_model.h
index e0e6708..6df85f6 100644
--- a/chrome/browser/ui/tabs/existing_base_sub_menu_model.h
+++ b/chrome/browser/ui/tabs/existing_base_sub_menu_model.h
@@ -56,6 +56,8 @@
 
     // The optional image for an entry in the sub menu.
     base::Optional<ui::ImageModel> image;
+
+    bool may_have_mnemonics = true;
   };
 
   // Helper method to create consistent submenus.|new_text| is the label to add
diff --git a/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc b/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc
index c07223d..af977842 100644
--- a/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc
+++ b/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc
@@ -48,6 +48,7 @@
     ui::ImageModel image_model = ui::ImageModel::FromVectorIcon(
         kTabGroupIcon, tp.GetColor(color_id), kIconSize);
     menu_item_infos.emplace_back(MenuItemInfo{displayed_title, image_model});
+    menu_item_infos.back().may_have_mnemonics = false;
   }
   Build(IDS_TAB_CXMENU_SUBMENU_NEW_GROUP, menu_item_infos);
 }
diff --git a/chrome/browser/ui/tabs/existing_window_sub_menu_model.cc b/chrome/browser/ui/tabs/existing_window_sub_menu_model.cc
index 66d4c3de6..d1e5ed8 100644
--- a/chrome/browser/ui/tabs/existing_window_sub_menu_model.cc
+++ b/chrome/browser/ui/tabs/existing_window_sub_menu_model.cc
@@ -29,6 +29,7 @@
 
   for (auto& window_title : window_titles) {
     menu_item_infos.emplace_back(MenuItemInfo{window_title});
+    menu_item_infos.back().may_have_mnemonics = false;
   }
   Build(IDS_TAB_CXMENU_MOVETOANOTHERNEWWINDOW, menu_item_infos);
 }
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index 3795fcf..75ffbe5 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -234,8 +234,7 @@
 // Each permission selector row is like this: [icon] [label] [selector]
 constexpr size_t kViewsPerPermissionRow = 3;
 
-// Test is flaky on all platforms: crbug.com/1152528.
-TEST_F(PageInfoBubbleViewTest, DISABLED_NotificationPermissionRevokeUkm) {
+TEST_F(PageInfoBubbleViewTest, NotificationPermissionRevokeUkm) {
   GURL origin_url = GURL(kUrl).GetOrigin();
   TestingProfile* profile =
       static_cast<TestingProfile*>(web_contents_helper_.profile());
diff --git a/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc b/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
index 022fb5a..9c779330 100644
--- a/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
@@ -439,7 +439,7 @@
   EXPECT_FALSE(controller()->current_autosignin_prompt());
 }
 
-IN_PROC_BROWSER_TEST_F(PasswordDialogViewTest, PopupAutoSigninPrompt) {
+IN_PROC_BROWSER_TEST_F(PasswordDialogViewTest, EscCancelsAutoSigninPrompt) {
   EXPECT_TRUE(
       password_bubble_experiment::ShouldShowAutoSignInPromptFirstRunExperience(
           browser()->profile()->GetPrefs()));
@@ -456,7 +456,7 @@
   content::RunAllPendingInMessageLoop();
   base::RunLoop().RunUntilIdle();
   testing::Mock::VerifyAndClearExpectations(controller());
-  EXPECT_TRUE(
+  EXPECT_FALSE(
       password_bubble_experiment::ShouldShowAutoSignInPromptFirstRunExperience(
           browser()->profile()->GetPrefs()));
 }
diff --git a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc
index f9a16fc..e617165 100644
--- a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc
+++ b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc
@@ -8,10 +8,10 @@
 
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/channel_info.h"
 #include "components/autofill/core/browser/logging/log_router.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/grit/dev_ui_components_resources.h"
 #include "components/version_info/version_info.h"
 #include "components/version_ui/version_handler_helper.h"
@@ -41,7 +41,7 @@
                                                : "Developer build");
   source->AddString(version_ui::kVersionModifier, chrome::GetChannelName());
   source->AddString(version_ui::kCL, version_info::GetLastChange());
-  source->AddString(version_ui::kUserAgent, GetUserAgent());
+  source->AddString(version_ui::kUserAgent, embedder_support::GetUserAgent());
   source->AddString("app_locale", g_browser_process->GetApplicationLocale());
   return source;
 }
diff --git a/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_dialog_launcher.cc b/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_dialog_launcher.cc
index e38820d..53bfd91 100644
--- a/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_dialog_launcher.cc
+++ b/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_dialog_launcher.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_dialog_launcher.h"
 
-#include "base/feature_list.h"
 #include "chrome/browser/ui/ash/system_tray_client.h"
 #include "chrome/browser/ui/webui/chromeos/cellular_setup/mobile_setup_dialog.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -14,12 +13,10 @@
 namespace cellular_setup {
 
 void OpenCellularSetupDialog(const std::string& cellular_network_guid) {
-  if (base::FeatureList::IsEnabled(
-          chromeos::features::kUpdatedCellularActivationUi)) {
+  if (chromeos::features::IsCellularActivationUiEnabled())
     SystemTrayClient::Get()->ShowSettingsCellularSetupPsimFlow();
-  } else {
+  else
     MobileSetupDialog::ShowByNetworkId(cellular_network_guid);
-  }
 }
 
 }  // namespace cellular_setup
diff --git a/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc b/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc
index 2a008371..f7448c6f 100644
--- a/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/chromeos/cellular_setup/cellular_setup_localized_strings_provider.cc
@@ -95,8 +95,7 @@
 const std::vector<const NamedBoolean>& GetBooleanValues() {
   static const base::NoDestructor<std::vector<const NamedBoolean>> named_bools({
       {"updatedCellularActivationUi",
-       base::FeatureList::IsEnabled(
-           chromeos::features::kUpdatedCellularActivationUi)},
+       chromeos::features::IsCellularActivationUiEnabled()},
   });
   return *named_bools;
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_section.cc b/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
index 437b3c29..b8d2f4c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
@@ -731,8 +731,7 @@
   network_element::AddConfigLocalizedStrings(html_source);
   network_element::AddErrorLocalizedStrings(html_source);
   cellular_setup::AddNonStringLoadTimeData(html_source);
-  if (base::FeatureList::IsEnabled(
-          chromeos::features::kUpdatedCellularActivationUi)) {
+  if (features::IsCellularActivationUiEnabled()) {
     cellular_setup::AddLocalizedStrings(html_source);
   }
 
@@ -1049,10 +1048,8 @@
         if (base::FeatureList::IsEnabled(::features::kMeteredShowToggle))
           updater.AddSearchTags(GetCellularMeteredSearchConcepts());
 
-        if (base::FeatureList::IsEnabled(
-                chromeos::features::kUpdatedCellularActivationUi)) {
+        if (features::IsCellularActivationUiEnabled())
           updater.AddSearchTags(GetCellularSetupAndDetailMenuSearchConcepts());
-        }
         break;
 
       case NetworkType::kTether:
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index 5da199f..ae21a216 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -922,9 +922,10 @@
                          GetSyncErrorAction(status_labels.action_type));
 
   sync_status->SetBoolean("managed", disallowed_by_policy);
-  sync_status->SetBoolean(
-      "disabled", !service || disallowed_by_policy ||
-                      !service->GetUserSettings()->IsSyncAllowedByPlatform());
+  // TODO(crbug.com/1171279): audit js usages of |disabled| and |signedIn|
+  // fields, update it to use the right field, comments around and conditions
+  // here. Perhaps removal of one of these to fields is possible.
+  sync_status->SetBoolean("disabled", !service || disallowed_by_policy);
   // NOTE: This means signed-in for *sync*. It can be false when the user is
   // signed-in to the content area or to the browser.
   sync_status->SetBoolean("signedIn", identity_manager->HasPrimaryAccount());
diff --git a/chrome/browser/ui/webui/version/version_ui.cc b/chrome/browser/ui/webui/version/version_ui.cc
index 8068565..ab478da 100644
--- a/chrome/browser/ui/webui/version/version_ui.cc
+++ b/chrome/browser/ui/webui/version/version_ui.cc
@@ -11,7 +11,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/version/version_handler.h"
 #include "chrome/browser/ui/webui/version/version_util_win.h"
@@ -20,6 +19,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/grit/components_resources.h"
 #include "components/strings/grit/components_chromium_strings.h"
 #include "components/strings/grit/components_strings.h"
@@ -171,7 +171,8 @@
           l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT),
           base::Time::Now()));
   html_source->AddString(version_ui::kCL, version_info::GetLastChange());
-  html_source->AddString(version_ui::kUserAgent, GetUserAgent());
+  html_source->AddString(version_ui::kUserAgent,
+                         embedder_support::GetUserAgent());
   // Note that the executable path and profile path are retrieved asynchronously
   // and returned in VersionHandler::OnGotFilePaths. The area is initially
   // blank.
diff --git a/chrome/browser/video_tutorials/android/java/src/org/chromium/chrome/browser/video_tutorials/VideoTutorialService.java b/chrome/browser/video_tutorials/android/java/src/org/chromium/chrome/browser/video_tutorials/VideoTutorialService.java
index 07b72eb..34b35a9 100644
--- a/chrome/browser/video_tutorials/android/java/src/org/chromium/chrome/browser/video_tutorials/VideoTutorialService.java
+++ b/chrome/browser/video_tutorials/android/java/src/org/chromium/chrome/browser/video_tutorials/VideoTutorialService.java
@@ -25,10 +25,17 @@
 
     /**
      * Called to get the list of supported languages.
+     * Deprecated in favor of getAvailableLanguagesForVideo(@FeatureType).
      */
     List<String> getSupportedLanguages();
 
     /**
+     * Called to get the list of supported languages in which a tutorial is available.
+     * @param feature The associated tutorial.
+     */
+    List<String> getAvailableLanguagesForTutorial(@FeatureType int feature);
+
+    /**
      * @return The user's language of choice for watching the video tutorials, or null user hasn't
      *         set it yet.
      */
diff --git a/chrome/browser/video_tutorials/android/java/src/org/chromium/chrome/browser/video_tutorials/test/TestVideoTutorialService.java b/chrome/browser/video_tutorials/android/java/src/org/chromium/chrome/browser/video_tutorials/test/TestVideoTutorialService.java
index 96aa9977..8962c0e1 100644
--- a/chrome/browser/video_tutorials/android/java/src/org/chromium/chrome/browser/video_tutorials/test/TestVideoTutorialService.java
+++ b/chrome/browser/video_tutorials/android/java/src/org/chromium/chrome/browser/video_tutorials/test/TestVideoTutorialService.java
@@ -59,6 +59,11 @@
     }
 
     @Override
+    public List<String> getAvailableLanguagesForTutorial(int feature) {
+        return mLanguages;
+    }
+
+    @Override
     public String getPreferredLocale() {
         return mPreferredLocale;
     }
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/bridges/VideoTutorialServiceBridge.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/bridges/VideoTutorialServiceBridge.java
index 7cd5d62..485a6f5 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/bridges/VideoTutorialServiceBridge.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/bridges/VideoTutorialServiceBridge.java
@@ -54,6 +54,14 @@
     }
 
     @Override
+    public List<String> getAvailableLanguagesForTutorial(@FeatureType int feature) {
+        if (mNativeVideoTutorialServiceBridge == 0) return null;
+        String[] languages = VideoTutorialServiceBridgeJni.get().getAvailableLanguagesForTutorial(
+                mNativeVideoTutorialServiceBridge, this, feature);
+        return Arrays.asList(languages);
+    }
+
+    @Override
     public String getPreferredLocale() {
         if (mNativeVideoTutorialServiceBridge == 0) return null;
         return VideoTutorialServiceBridgeJni.get().getPreferredLocale(
@@ -80,6 +88,8 @@
                 int feature, Callback<Tutorial> callback);
         String[] getSupportedLanguages(
                 long nativeVideoTutorialServiceBridge, VideoTutorialServiceBridge caller);
+        String[] getAvailableLanguagesForTutorial(long nativeVideoTutorialServiceBridge,
+                VideoTutorialServiceBridge caller, int feature);
         String getPreferredLocale(
                 long nativeVideoTutorialServiceBridge, VideoTutorialServiceBridge caller);
         void setPreferredLocale(long nativeVideoTutorialServiceBridge,
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerCoordinator.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerCoordinator.java
index 5f2aad2f..ac7abd6 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerCoordinator.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerCoordinator.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.view.View;
 
+import org.chromium.chrome.browser.video_tutorials.FeatureType;
 import org.chromium.chrome.browser.video_tutorials.LanguageInfoProvider;
 import org.chromium.chrome.browser.video_tutorials.VideoTutorialService;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
@@ -41,11 +42,13 @@
 
     /**
      * Called to open the language picker UI.
+     * @param feature The tutorial for which the language options will be shown.
      * @param doneCallback The callback to be invoked when the watch button is clicked.
      * @param closeCallback The callback to be invoked when the close button is clicked.
      */
-    public void showLanguagePicker(Runnable doneCallback, Runnable closeCallback) {
-        mMediator.showLanguagePicker(doneCallback, closeCallback);
+    public void showLanguagePicker(
+            @FeatureType int feature, Runnable doneCallback, Runnable closeCallback) {
+        mMediator.showLanguagePicker(feature, doneCallback, closeCallback);
     }
 
     /** @return A {@link View} representing this coordinator. */
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediator.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediator.java
index 55d76a9a..09c2d1ae 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediator.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediator.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.text.TextUtils;
 
+import org.chromium.chrome.browser.video_tutorials.FeatureType;
 import org.chromium.chrome.browser.video_tutorials.Language;
 import org.chromium.chrome.browser.video_tutorials.LanguageInfoProvider;
 import org.chromium.chrome.browser.video_tutorials.VideoTutorialService;
@@ -29,6 +30,7 @@
     private final PropertyModel mModel;
     private final ModelList mListModel;
     private String mSelectedLocale;
+    private @FeatureType int mFeature;
 
     /**
      * Constructor.
@@ -47,7 +49,9 @@
     /**
      * See {@link LanguagePickerCoordinator#showLanguagePicker(Runnable, Runnable)}.
      */
-    public void showLanguagePicker(Runnable doneCallback, Runnable closeCallback) {
+    public void showLanguagePicker(
+            @FeatureType int feature, Runnable doneCallback, Runnable closeCallback) {
+        mFeature = feature;
         mModel.set(LanguagePickerProperties.CLOSE_CALLBACK, () -> {
             mSelectedLocale = mVideoTutorialService.getPreferredLocale();
             VideoTutorialMetrics.recordLanguagePickerAction(LanguagePickerAction.CLOSE);
@@ -57,12 +61,12 @@
             onLanguageSelectionFinalized();
             doneCallback.run();
         });
-        populateList(mVideoTutorialService.getSupportedLanguages());
+        populateList(mVideoTutorialService.getAvailableLanguagesForTutorial(mFeature));
     }
 
     private void onLanguageSelected(String locale) {
         mSelectedLocale = locale;
-        populateList(mVideoTutorialService.getSupportedLanguages());
+        populateList(mVideoTutorialService.getAvailableLanguagesForTutorial(mFeature));
     }
 
     private void populateList(List<String> supportedLanguages) {
@@ -95,7 +99,8 @@
     private void onLanguageSelectionFinalized() {
         VideoTutorialMetrics.recordLanguagePickerAction(LanguagePickerAction.WATCH);
         mVideoTutorialService.setPreferredLocale(mSelectedLocale);
-        List<String> supportedLanguages = mVideoTutorialService.getSupportedLanguages();
+        List<String> supportedLanguages =
+                mVideoTutorialService.getAvailableLanguagesForTutorial(mFeature);
         for (int i = 0; i < supportedLanguages.size(); i++) {
             if (TextUtils.equals(supportedLanguages.get(i), mSelectedLocale)) {
                 VideoTutorialMetrics.recordLanguageSelected(i);
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediatorUnitTest.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediatorUnitTest.java
index d130a775..9412ea9 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediatorUnitTest.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediatorUnitTest.java
@@ -19,6 +19,7 @@
 
 import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.video_tutorials.FeatureType;
 import org.chromium.chrome.browser.video_tutorials.LanguageInfoProvider;
 import org.chromium.chrome.browser.video_tutorials.test.TestVideoTutorialService;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
@@ -59,7 +60,7 @@
 
     @Test
     public void checkCallbacks() {
-        mMediator.showLanguagePicker(() -> {}, () -> {});
+        mMediator.showLanguagePicker(FeatureType.CHROME_INTRO, () -> {}, () -> {});
         verify(mPropertyObserver)
                 .onPropertyChanged(mModel, LanguagePickerProperties.CLOSE_CALLBACK);
         verify(mPropertyObserver)
@@ -75,7 +76,7 @@
         Mockito.when(mLanguageProvider.getLanguageInfo("en"))
                 .thenReturn(TestVideoTutorialService.ENGLISH);
 
-        mMediator.showLanguagePicker(() -> {}, () -> {});
+        mMediator.showLanguagePicker(FeatureType.CHROME_INTRO, () -> {}, () -> {});
 
         assertThat(mListModel.size(), equalTo(mTestVideoTutorialService.getTestLanguages().size()));
         ListItem listItem = mListModel.get(0);
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerTest.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerTest.java
index 3fbdca4..e818d80 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerTest.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerTest.java
@@ -26,6 +26,7 @@
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.BaseActivityTestRule;
+import org.chromium.chrome.browser.video_tutorials.FeatureType;
 import org.chromium.chrome.browser.video_tutorials.LanguageInfoProvider;
 import org.chromium.chrome.browser.video_tutorials.R;
 import org.chromium.chrome.browser.video_tutorials.VideoTutorialService;
@@ -71,7 +72,8 @@
                     .thenReturn(TestVideoTutorialService.HINDI);
             mCoordinator = new LanguagePickerCoordinator(
                     mContentView, mVideoTutorialService, mLanguageProvider);
-            mCoordinator.showLanguagePicker(mWatchCallback, mCloseCallback);
+            mCoordinator.showLanguagePicker(
+                    FeatureType.CHROME_INTRO, mWatchCallback, mCloseCallback);
         });
     }
 
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediator.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediator.java
index 5067865..13bf2a7 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediator.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediator.java
@@ -94,13 +94,13 @@
      */
     void playVideoTutorial(Tutorial tutorial) {
         mTutorial = tutorial;
-
         boolean shouldShowLanguagePicker =
                 TextUtils.isEmpty(mVideoTutorialService.getPreferredLocale())
-                && mVideoTutorialService.getSupportedLanguages().size() > 1;
+                && areMultipleLanguagesAvailable();
         if (shouldShowLanguagePicker) {
             mModel.set(VideoPlayerProperties.SHOW_LANGUAGE_PICKER, true);
-            mLanguagePicker.showLanguagePicker(this::onLanguageSelected, mCloseCallback);
+            mLanguagePicker.showLanguagePicker(
+                    mTutorial.featureType, this::onLanguageSelected, mCloseCallback);
         } else {
             startVideo(tutorial);
         }
@@ -135,8 +135,7 @@
     public void onEnded() {
         VideoTutorialMetrics.recordWatchStateUpdate(mTutorial.featureType, WatchState.COMPLETED);
         mModel.set(VideoPlayerProperties.SHOW_MEDIA_CONTROLS, true);
-        mModel.set(VideoPlayerProperties.SHOW_CHANGE_LANGUAGE,
-                mVideoTutorialService.getSupportedLanguages().size() > 1);
+        mModel.set(VideoPlayerProperties.SHOW_CHANGE_LANGUAGE, areMultipleLanguagesAvailable());
         maybeShowWatchNextVideoButton();
         mModel.set(VideoPlayerProperties.SHOW_TRY_NOW,
                 VideoTutorialUtils.shouldShowTryNow(mTutorial.featureType));
@@ -150,7 +149,8 @@
 
     private void changeLanguage() {
         mModel.set(VideoPlayerProperties.SHOW_LANGUAGE_PICKER, true);
-        mLanguagePicker.showLanguagePicker(this::onLanguageSelected, this::onLanguagePickerClosed);
+        mLanguagePicker.showLanguagePicker(
+                mTutorial.featureType, this::onLanguageSelected, this::onLanguagePickerClosed);
         VideoTutorialMetrics.recordUserAction(mTutorial.featureType, UserAction.CHANGE_LANGUAGE);
     }
 
@@ -204,4 +204,9 @@
         VideoTutorialMetrics.recordUserAction(mTutorial.featureType, UserAction.WATCH_NEXT_VIDEO);
         VideoTutorialUtils.getNextTutorial(mVideoTutorialService, mTutorial, this::startVideo);
     }
+
+    private boolean areMultipleLanguagesAvailable() {
+        return mVideoTutorialService.getAvailableLanguagesForTutorial(mTutorial.featureType).size()
+                > 1;
+    }
 }
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java
index ceb35cb1..a854c4c 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java
@@ -7,6 +7,8 @@
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -95,7 +97,8 @@
 
         assertThat(mModel.get(VideoPlayerProperties.SHOW_LANGUAGE_PICKER), equalTo(true));
         Mockito.verify(mLanguagePicker, Mockito.times(1))
-                .showLanguagePicker(mLanguagePickerCallback.capture(), any());
+                .showLanguagePicker(
+                        eq(tutorial.featureType), mLanguagePickerCallback.capture(), any());
         ((Runnable) mLanguagePickerCallback.getValue()).run();
         Mockito.verify(mNavigationController).loadUrl(any());
     }
@@ -109,7 +112,7 @@
 
         assertThat(mModel.get(VideoPlayerProperties.SHOW_LANGUAGE_PICKER), equalTo(false));
         Mockito.verify(mLanguagePicker, Mockito.times(0))
-                .showLanguagePicker(mLanguagePickerCallback.capture(), any());
+                .showLanguagePicker(anyInt(), mLanguagePickerCallback.capture(), any());
     }
 
     @Test
@@ -119,7 +122,7 @@
         mMediator.playVideoTutorial(tutorial);
 
         assertThat(mModel.get(VideoPlayerProperties.SHOW_LANGUAGE_PICKER), equalTo(false));
-        Mockito.verify(mLanguagePicker, Mockito.never()).showLanguagePicker(any(), any());
+        Mockito.verify(mLanguagePicker, Mockito.never()).showLanguagePicker(anyInt(), any(), any());
         Mockito.verify(mNavigationController).loadUrl(any());
     }
 
@@ -171,7 +174,8 @@
         Mockito.when(mLanguageProvider.getLanguageInfo("en")).thenReturn(language);
         mModel.get(VideoPlayerProperties.CALLBACK_CHANGE_LANGUAGE).run();
         Mockito.verify(mLanguagePicker, Mockito.times(1))
-                .showLanguagePicker(mLanguagePickerCallback.capture(), any());
+                .showLanguagePicker(
+                        eq(tutorial.featureType), mLanguagePickerCallback.capture(), any());
         mTestVideoTutorialService.setPreferredLocale("en");
         ((Runnable) mLanguagePickerCallback.getValue()).run();
     }
@@ -191,7 +195,7 @@
 
         mModel.get(VideoPlayerProperties.CALLBACK_SHARE).run();
         mModel.get(VideoPlayerProperties.CALLBACK_CHANGE_LANGUAGE).run();
-        Mockito.verify(mLanguagePicker).showLanguagePicker(any(), any());
+        Mockito.verify(mLanguagePicker).showLanguagePicker(eq(tutorial.featureType), any(), any());
 
         WatchStateInfo watchStateInfo = new WatchStateInfo();
         watchStateInfo.videoLength = 10;
diff --git a/chrome/browser/video_tutorials/internal/android/video_tutorial_service_bridge.cc b/chrome/browser/video_tutorials/internal/android/video_tutorial_service_bridge.cc
index 0acb149..ea74999 100644
--- a/chrome/browser/video_tutorials/internal/android/video_tutorial_service_bridge.cc
+++ b/chrome/browser/video_tutorials/internal/android/video_tutorial_service_bridge.cc
@@ -98,6 +98,16 @@
       env, video_tutorial_service_->GetSupportedLanguages());
 }
 
+ScopedJavaLocalRef<jobjectArray>
+VideoTutorialServiceBridge::GetAvailableLanguagesForTutorial(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    jint j_feature) {
+  return base::android::ToJavaArrayOfStrings(
+      env, video_tutorial_service_->GetAvailableLanguagesForTutorial(
+               static_cast<FeatureType>(j_feature)));
+}
+
 ScopedJavaLocalRef<jstring> VideoTutorialServiceBridge::GetPreferredLocale(
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller) {
diff --git a/chrome/browser/video_tutorials/internal/android/video_tutorial_service_bridge.h b/chrome/browser/video_tutorials/internal/android/video_tutorial_service_bridge.h
index f60c890..670bd50b 100644
--- a/chrome/browser/video_tutorials/internal/android/video_tutorial_service_bridge.h
+++ b/chrome/browser/video_tutorials/internal/android/video_tutorial_service_bridge.h
@@ -40,6 +40,10 @@
   ScopedJavaLocalRef<jobjectArray> GetSupportedLanguages(
       JNIEnv* env,
       const JavaParamRef<jobject>& jcaller);
+  ScopedJavaLocalRef<jobjectArray> GetAvailableLanguagesForTutorial(
+      JNIEnv* env,
+      const JavaParamRef<jobject>& jcaller,
+      jint j_feature);
   ScopedJavaLocalRef<jstring> GetPreferredLocale(
       JNIEnv* env,
       const JavaParamRef<jobject>& jcaller);
diff --git a/chrome/browser/video_tutorials/internal/tutorial_manager.h b/chrome/browser/video_tutorials/internal/tutorial_manager.h
index 9b30d2e..da79d77 100644
--- a/chrome/browser/video_tutorials/internal/tutorial_manager.h
+++ b/chrome/browser/video_tutorials/internal/tutorial_manager.h
@@ -29,6 +29,10 @@
   // Returns a list of languages for which video tutorials are available.
   virtual const std::vector<std::string>& GetSupportedLanguages() = 0;
 
+  // Returns a list of languages in which a given tutorial is available.
+  virtual const std::vector<std::string>& GetAvailableLanguagesForTutorial(
+      FeatureType feature_type) = 0;
+
   // Returns the preferred locale for the video tutorials.
   virtual base::Optional<std::string> GetPreferredLocale() = 0;
 
diff --git a/chrome/browser/video_tutorials/internal/tutorial_manager_impl.cc b/chrome/browser/video_tutorials/internal/tutorial_manager_impl.cc
index feb62f2..7128d8a 100644
--- a/chrome/browser/video_tutorials/internal/tutorial_manager_impl.cc
+++ b/chrome/browser/video_tutorials/internal/tutorial_manager_impl.cc
@@ -100,6 +100,12 @@
   return supported_languages_;
 }
 
+const std::vector<std::string>&
+TutorialManagerImpl::GetAvailableLanguagesForTutorial(
+    FeatureType feature_type) {
+  return languages_for_tutorials_[feature_type];
+}
+
 base::Optional<std::string> TutorialManagerImpl::GetPreferredLocale() {
   if (prefs_->HasPrefPath(kPreferredLocaleKey))
     return prefs_->GetString(kPreferredLocaleKey);
@@ -127,6 +133,14 @@
     for (auto& tutorial_group : *all_groups) {
       supported_languages_.emplace_back(tutorial_group.language);
     }
+
+    languages_for_tutorials_.clear();
+    for (auto& tutorial_group : *all_groups) {
+      for (auto& tutorial : tutorial_group.tutorials) {
+        languages_for_tutorials_[tutorial.feature].emplace_back(
+            tutorial_group.language);
+      }
+    }
   }
 
   init_success_ = success;
diff --git a/chrome/browser/video_tutorials/internal/tutorial_manager_impl.h b/chrome/browser/video_tutorials/internal/tutorial_manager_impl.h
index 166a44dc..67cad63 100644
--- a/chrome/browser/video_tutorials/internal/tutorial_manager_impl.h
+++ b/chrome/browser/video_tutorials/internal/tutorial_manager_impl.h
@@ -8,6 +8,7 @@
 #include "chrome/browser/video_tutorials/internal/tutorial_manager.h"
 
 #include <deque>
+#include <map>
 #include <memory>
 #include <string>
 #include <vector>
@@ -32,6 +33,8 @@
   void GetTutorial(FeatureType feature_type,
                    SingleItemCallback callback) override;
   const std::vector<std::string>& GetSupportedLanguages() override;
+  const std::vector<std::string>& GetAvailableLanguagesForTutorial(
+      FeatureType feature_type) override;
   base::Optional<std::string> GetPreferredLocale() override;
   void SetPreferredLocale(const std::string& locale) override;
   void SaveGroups(std::unique_ptr<std::vector<TutorialGroup>> groups) override;
@@ -56,6 +59,9 @@
   // List of languages for which we have tutorials.
   std::vector<std::string> supported_languages_;
 
+  // List of supported languages per tutorial.
+  std::map<FeatureType, std::vector<std::string>> languages_for_tutorials_;
+
   // We only keep the tutorials for the preferred locale.
   base::Optional<TutorialGroup> tutorial_group_;
 
diff --git a/chrome/browser/video_tutorials/internal/tutorial_manager_impl_unittest.cc b/chrome/browser/video_tutorials/internal/tutorial_manager_impl_unittest.cc
index daf8913f..aa6e6e6 100644
--- a/chrome/browser/video_tutorials/internal/tutorial_manager_impl_unittest.cc
+++ b/chrome/browser/video_tutorials/internal/tutorial_manager_impl_unittest.cc
@@ -177,6 +177,8 @@
   std::vector<FeatureType> features(
       {FeatureType::kDownload, FeatureType::kSearch});
   auto groups = CreateSampleGroups({"hi", "kn"}, features);
+  groups[1].tutorials.emplace_back(
+      Tutorial(FeatureType::kVoiceSearch, "", "", "", "", "", "", "", 10));
   auto tutorial_store = std::make_unique<StrictMock<TestStore>>();
   tutorial_store->InitStoreData("hi", groups);
   CreateTutorialManager(std::move(tutorial_store));
@@ -189,6 +191,15 @@
 
   GetTutorial(FeatureType::kSearch);
   EXPECT_EQ(FeatureType::kSearch, last_get_tutorial_result()->feature);
+
+  languages = manager()->GetAvailableLanguagesForTutorial(FeatureType::kSearch);
+  EXPECT_EQ(languages.size(), 2u);
+  languages =
+      manager()->GetAvailableLanguagesForTutorial(FeatureType::kChromeIntro);
+  EXPECT_EQ(languages.size(), 0u);
+  languages =
+      manager()->GetAvailableLanguagesForTutorial(FeatureType::kVoiceSearch);
+  EXPECT_EQ(languages.size(), 1u);
 }
 
 TEST_F(TutorialManagerTest, InitAndGetTutorialsWithSummary) {
@@ -240,6 +251,9 @@
   EXPECT_EQ(languages.size(), 2u);
   GetTutorials();
   EXPECT_EQ(last_results().size(), 2u);
+  languages =
+      manager()->GetAvailableLanguagesForTutorial(FeatureType::kDownload);
+  EXPECT_EQ(languages.size(), 2u);
 
   // New fetch data.
   features = std::vector<FeatureType>(
@@ -252,6 +266,10 @@
   GetTutorials();
   EXPECT_EQ(last_results().size(), 4u);
 
+  languages =
+      manager()->GetAvailableLanguagesForTutorial(FeatureType::kDownload);
+  EXPECT_EQ(languages.size(), 3u);
+
   // New fetch data with summary.
   features = std::vector<FeatureType>(
       {FeatureType::kChromeIntro, FeatureType::kVoiceSearch,
@@ -261,6 +279,10 @@
   manager()->SetPreferredLocale("tl");
   GetTutorials();
   EXPECT_EQ(last_results().size(), 3u);
+
+  languages =
+      manager()->GetAvailableLanguagesForTutorial(FeatureType::kDownload);
+  EXPECT_EQ(languages.size(), 0u);
 }
 
 }  // namespace
diff --git a/chrome/browser/video_tutorials/internal/tutorial_service_impl.cc b/chrome/browser/video_tutorials/internal/tutorial_service_impl.cc
index 1e02bdbc..4241241 100644
--- a/chrome/browser/video_tutorials/internal/tutorial_service_impl.cc
+++ b/chrome/browser/video_tutorials/internal/tutorial_service_impl.cc
@@ -72,6 +72,12 @@
   return tutorial_manager_->GetSupportedLanguages();
 }
 
+const std::vector<std::string>&
+TutorialServiceImpl::GetAvailableLanguagesForTutorial(
+    FeatureType feature_type) {
+  return tutorial_manager_->GetAvailableLanguagesForTutorial(feature_type);
+}
+
 base::Optional<std::string> TutorialServiceImpl::GetPreferredLocale() {
   return tutorial_manager_->GetPreferredLocale();
 }
diff --git a/chrome/browser/video_tutorials/internal/tutorial_service_impl.h b/chrome/browser/video_tutorials/internal/tutorial_service_impl.h
index 8486357..675ba829 100644
--- a/chrome/browser/video_tutorials/internal/tutorial_service_impl.h
+++ b/chrome/browser/video_tutorials/internal/tutorial_service_impl.h
@@ -26,6 +26,8 @@
   void GetTutorial(FeatureType feature_type,
                    SingleItemCallback callback) override;
   const std::vector<std::string>& GetSupportedLanguages() override;
+  const std::vector<std::string>& GetAvailableLanguagesForTutorial(
+      FeatureType feature_type) override;
   base::Optional<std::string> GetPreferredLocale() override;
   void SetPreferredLocale(const std::string& locale) override;
 
diff --git a/chrome/browser/video_tutorials/video_tutorial_service.h b/chrome/browser/video_tutorials/video_tutorial_service.h
index 96bbee0..f3a2a607 100644
--- a/chrome/browser/video_tutorials/video_tutorial_service.h
+++ b/chrome/browser/video_tutorials/video_tutorial_service.h
@@ -35,6 +35,10 @@
   // Called to retrieve all the supported locales.
   virtual const std::vector<std::string>& GetSupportedLanguages() = 0;
 
+  // Returns a list of languages in which a given tutorial is available.
+  virtual const std::vector<std::string>& GetAvailableLanguagesForTutorial(
+      FeatureType feature_type) = 0;
+
   // Called to retrieve the preferred locale, if it is set by the user.
   virtual base::Optional<std::string> GetPreferredLocale() = 0;
 
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index c2833c5..98daaee 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -93,7 +93,8 @@
                           OsHooksResults os_hooks_info);
 
   WebAppRegistrar& GetWebAppRegistrar() const;
-
+  
+  // Used for legacy Bookmark Apps.
   std::unique_ptr<InstallFinalizer> legacy_finalizer_;
 
   Profile* const profile_;
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 78f42fa4..7bf9bae 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1611834165-3de8138b68f3249c9171d047dc9aa8e0feb18681.profdata
+chrome-linux-master-1611856733-a21458dcae1f312e1b9bb9647f9f3b0865709090.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index e70ae8c..be1da09 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1611834165-5b4c2ca0c732830797b1d3211ef326065c3ef2bd.profdata
+chrome-mac-master-1611856733-3e44d52638b87d5eb2c9ef1ab766a80a1b8db797.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 7befdf5..fccfcf8 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1611824033-c2e20d642b54c6039dac96251f5f1f1cd63f9041.profdata
+chrome-win32-master-1611834165-4b335240b4b05daf7ebafae407d3b94205e08a4f.profdata
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 94ffdb9..9021c751 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -565,9 +565,6 @@
 // apps/origins.  This should be used only for testing purpose.
 const char kUnlimitedStorage[]              = "unlimited-storage";
 
-// A string used to override the default user agent with a custom one.
-const char kUserAgent[]                     = "user-agent";
-
 // Specifies the user data directory, which is where the browser will look for
 // all of its state.
 const char kUserDataDir[]                   = "user-data-dir";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 0a71375..10c2ac8 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -175,7 +175,6 @@
 extern const char kTrustedDownloadSources[];
 extern const char kTryChromeAgain[];
 extern const char kUnlimitedStorage[];
-extern const char kUserAgent[];
 extern const char kUserDataDir[];
 extern const char kValidateCrx[];
 extern const char kVersion[];
diff --git a/chrome/test/data/pdf/basic_plugin_test.js b/chrome/test/data/pdf/basic_plugin_test.js
index 538fd3b6..d52e119 100644
--- a/chrome/test/data/pdf/basic_plugin_test.js
+++ b/chrome/test/data/pdf/basic_plugin_test.js
@@ -20,7 +20,7 @@
     viewer.viewport.setZoom(1);
     const sizer = viewer.shadowRoot.querySelector('#sizer');
     chrome.test.assertEq(826, sizer.offsetWidth);
-    chrome.test.assertEq(1066 + viewer.getToolbarHeight(), sizer.offsetHeight);
+    chrome.test.assertEq(1066, sizer.offsetHeight);
     chrome.test.succeed();
   },
 
diff --git a/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js b/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js
index 32e2960..513fcc4 100644
--- a/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js
+++ b/chrome/test/data/webui/chromeos/scanning/scanner_select_test.js
@@ -71,6 +71,7 @@
     assertEquals(3, select.length);
     assertEquals(firstScannerName, select.options[0].textContent.trim());
     assertEquals(secondScannerName, select.options[1].textContent.trim());
+    assertTrue(select.options[2].hidden);
     assertEquals(tokenToString(firstScannerId), select.value);
   });
 
diff --git a/chrome/test/data/webui/chromeos/scanning/source_select_test.js b/chrome/test/data/webui/chromeos/scanning/source_select_test.js
index b8857ef..fced5ff 100644
--- a/chrome/test/data/webui/chromeos/scanning/source_select_test.js
+++ b/chrome/test/data/webui/chromeos/scanning/source_select_test.js
@@ -73,6 +73,7 @@
     assertEquals(
         getSourceTypeString(secondSource.type),
         select.options[1].textContent.trim());
+    assertTrue(select.options[2].hidden);
     assertEquals(secondSource.name, select.value);
   });
 
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 0b61214..01e7774f 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -10,11 +10,6 @@
 // Polymer BrowserTest fixture.
 GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
 
-// Only run in release builds because we frequently see test timeouts in debug.
-// We suspect this is because the settings page loads slowly in debug.
-// https://crbug.com/1003483
-GEN('#if defined(NDEBUG)');
-
 GEN('#include "ash/public/cpp/ash_features.h"');
 GEN('#include "build/branding_buildflags.h"');
 GEN('#include "chrome/browser/nearby_sharing/common/nearby_share_features.h"');
@@ -23,6 +18,15 @@
 GEN('#include "content/public/test/browser_test.h"');
 GEN('#include "ui/display/display_features.h"');
 
+// Most tests only run in release builds because we frequently see test timeouts
+// in debug. We suspect this is because the settings page loads slowly in debug.
+// https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_AllJsTests DISABLED_AllJsTests');
+GEN('#else');
+GEN('#define MAYBE_AllJsTests AllJsTests');
+GEN('#endif');
+
 // Generic test fixture for CrOS Polymer Settings elements to be overridden by
 // individual element tests.
 const OSSettingsBrowserTest = class extends Polymer2DeprecatedTest {
@@ -60,7 +64,7 @@
   }
 };
 
-TEST_F('SettingsLocalizedLinkTest', 'AllJsTests', () => {
+TEST_F('SettingsLocalizedLinkTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -90,12 +94,24 @@
   }
 };
 
-TEST_F('OSSettingsAboutPageTest', 'AllBuilds', () => {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_AllBuilds DISABLED_AllBuilds');
+GEN('#else');
+GEN('#define MAYBE_AllBuilds AllBuilds');
+GEN('#endif');
+TEST_F('OSSettingsAboutPageTest', 'MAYBE_AllBuilds', () => {
   mocha.grep('/^(?!AboutPageTest_OfficialBuild).*$/').run();
 });
 
 GEN('#if BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-TEST_F('OSSettingsAboutPageTest', 'AboutPage_OfficialBuild', () => {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_OfficialBuild DISABLED_OfficialBuild');
+GEN('#else');
+GEN('#define MAYBE_OfficialBuild OfficialBuild');
+GEN('#endif');
+TEST_F('OSSettingsAboutPageTest', 'MAYBE_OfficialBuild', () => {
   mocha.grep('AboutPageTest_OfficialBuild').run();
 });
 GEN('#endif');
@@ -117,7 +133,7 @@
   }
 };
 
-TEST_F('OSSettingsSliderTest', 'AllJsTests', () => {
+TEST_F('OSSettingsSliderTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -137,7 +153,7 @@
   }
 };
 
-TEST_F('OSSettingsTextAreaTest', 'AllJsTests', () => {
+TEST_F('OSSettingsTextAreaTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -157,7 +173,7 @@
   }
 };
 
-TEST_F('OSSettingsToggleButtonTest', 'AllJsTests', () => {
+TEST_F('OSSettingsToggleButtonTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -177,7 +193,7 @@
   }
 };
 
-TEST_F('OSSettingsPrefUtilTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPrefUtilTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -200,7 +216,7 @@
   }
 };
 
-TEST_F('OSSettingsPrefsTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPrefsTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -222,7 +238,7 @@
   }
 };
 
-TEST_F('OSSettingsAddUsersTest', 'AllJsTests', () => {
+TEST_F('OSSettingsAddUsersTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -249,7 +265,7 @@
   }
 };
 
-TEST_F('OSSettingsLockScreenPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsLockScreenPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -264,7 +280,7 @@
 
 TEST_F(
     'OSSettingsLockScreenPageTestWithAccountManagementFlowsV2Enabled',
-    'AllJsTests', () => {
+    'MAYBE_AllJsTests', () => {
       mocha.run();
     });
 
@@ -293,7 +309,7 @@
   }
 };
 
-TEST_F('OSSettingsUserPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsUserPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -307,8 +323,8 @@
 };
 
 TEST_F(
-    'OSSettingsUserPageTestWithAccountManagementFlowsV2Enabled', 'AllJsTests',
-    () => {
+    'OSSettingsUserPageTestWithAccountManagementFlowsV2Enabled',
+    'MAYBE_AllJsTests', () => {
       mocha.run();
     });
 
@@ -330,7 +346,7 @@
   }
 };
 
-TEST_F('OSSettingsAmbientModePageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsAmbientModePageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -353,7 +369,7 @@
   }
 };
 
-TEST_F('OSSettingsAmbientModePhotosPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsAmbientModePhotosPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -369,7 +385,7 @@
   }
 };
 
-TEST_F('OSSettingsPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPageTest', 'MAYBE_AllJsTests', () => {
   // Run all registered tests.
   mocha.run();
 });
@@ -394,7 +410,7 @@
   }
 };
 
-TEST_F('OSSettingsAppsPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsAppsPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -435,7 +451,7 @@
   }
 };
 
-TEST_F('OSSettingsAppManagementDomSwitchTest', 'All', function() {
+TEST_F('OSSettingsAppManagementDomSwitchTest', 'MAYBE_AllJsTests', function() {
   mocha.run();
 });
 
@@ -456,7 +472,7 @@
   }
 };
 
-TEST_F('OSSettingsAppManagementPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsAppManagementPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -477,7 +493,7 @@
   }
 };
 
-TEST_F('OSSettingsAppManagementPwaDetailViewTest', 'AllJsTests', () => {
+TEST_F('OSSettingsAppManagementPwaDetailViewTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -498,7 +514,7 @@
   }
 };
 
-TEST_F('OSSettingsAppManagementArcDetailViewTest', 'AllJsTests', () => {
+TEST_F('OSSettingsAppManagementArcDetailViewTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -521,9 +537,10 @@
   }
 };
 
-TEST_F('OSSettingsAppManagementPluginVmDetailViewTest', 'AllJsTests', () => {
-  mocha.run();
-});
+TEST_F(
+    'OSSettingsAppManagementPluginVmDetailViewTest', 'MAYBE_AllJsTests', () => {
+      mocha.run();
+    });
 
 // Test fixture for the Plugin VM shared paths page.
 // eslint-disable-next-line no-var
@@ -544,9 +561,11 @@
   }
 };
 
-TEST_F('OSSettingsAppManagementPluginVmSharedPathsTest', 'AllJsTests', () => {
-  mocha.run();
-});
+TEST_F(
+    'OSSettingsAppManagementPluginVmSharedPathsTest', 'MAYBE_AllJsTests',
+    () => {
+      mocha.run();
+    });
 
 // Test fixture for the app management managed app view.
 // eslint-disable-next-line no-var
@@ -565,7 +584,7 @@
   }
 };
 
-TEST_F('OSSettingsAppManagementManagedAppTest', 'AllJsTests', () => {
+TEST_F('OSSettingsAppManagementManagedAppTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -582,7 +601,7 @@
   }
 };
 
-TEST_F('OSSettingsAppManagementReducersTest', 'AllJsTests', () => {
+TEST_F('OSSettingsAppManagementReducersTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -608,7 +627,7 @@
 };
 
 // Flaky. https://crbug.com/1035378
-TEST_F('OSSettingsBluetoothPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsBluetoothPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -637,29 +656,66 @@
   }
 };
 
-TEST_F('OSSettingsCrostiniPageTest', 'MainPage', function() {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_MainPage DISABLED_MainPage');
+GEN('#else');
+GEN('#define MAYBE_MainPage MainPage');
+GEN('#endif');
+TEST_F('OSSettingsCrostiniPageTest', 'MAYBE_MainPage', function() {
   mocha.grep('\\bMainPage\\b').run();
 });
 
-TEST_F('OSSettingsCrostiniPageTest', 'SubPageDefault', function() {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_SubPageDefault DISABLED_SubPageDefault');
+GEN('#else');
+GEN('#define MAYBE_SubPageDefault SubPageDefault');
+GEN('#endif');
+TEST_F('OSSettingsCrostiniPageTest', 'MAYBE_SubPageDefault', function() {
   mocha.grep('\\bSubPageDefault\\b').run();
 });
 
-TEST_F('OSSettingsCrostiniPageTest', 'SubPagePortForwarding', function() {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_SubPagePortForwarding DISABLED_SubPagePortForwarding');
+GEN('#else');
+GEN('#define MAYBE_SubPagePortForwarding SubPagePortForwarding');
+GEN('#endif');
+TEST_F('OSSettingsCrostiniPageTest', 'MAYBE_SubPagePortForwarding', function() {
   mocha.grep('\\bSubPagePortForwarding\\b').run();
 });
 
-TEST_F('OSSettingsCrostiniPageTest', 'DiskResize', function() {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_DiskResize DISABLED_DiskResize');
+GEN('#else');
+GEN('#define MAYBE_DiskResize DiskResize');
+GEN('#endif');
+TEST_F('OSSettingsCrostiniPageTest', 'MAYBE_DiskResize', function() {
   mocha.grep('\\bDiskResize\\b').run();
 });
 
-TEST_F('OSSettingsCrostiniPageTest', 'SubPageSharedPaths', function() {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_SubPageSharedPaths DISABLED_SubPageSharedPaths');
+GEN('#else');
+GEN('#define MAYBE_SubPageSharedPaths SubPageSharedPaths');
+GEN('#endif');
+TEST_F('OSSettingsCrostiniPageTest', 'MAYBE_SubPageSharedPaths', function() {
   mocha.grep('\\bSubPageSharedPaths\\b').run();
 });
 
-TEST_F('OSSettingsCrostiniPageTest', 'SubPageSharedUsbDevices', function() {
-  mocha.grep('\\bSubPageSharedUsbDevices\\b').run();
-});
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_SubPageSharedUsbDevices DISABLED_SubPageSharedUsbDevices');
+GEN('#else');
+GEN('#define MAYBE_SubPageSharedUsbDevices SubPageSharedUsbDevices');
+GEN('#endif');
+TEST_F(
+    'OSSettingsCrostiniPageTest', 'MAYBE_SubPageSharedUsbDevices', function() {
+      mocha.grep('\\bSubPageSharedUsbDevices\\b').run();
+    });
 
 // Test fixture for the Guest OS shared USB devices page.
 // eslint-disable-next-line no-var
@@ -679,7 +735,7 @@
   }
 };
 
-TEST_F('OSSettingsGuestOsSharedUsbDevicesTest', 'AllJsTests', () => {
+TEST_F('OSSettingsGuestOsSharedUsbDevicesTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -709,7 +765,7 @@
   }
 };
 
-TEST_F('OSSettingsOnStartupPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsOnStartupPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -731,7 +787,7 @@
   }
 };
 
-TEST_F('OSSettingsDateTimePageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsDateTimePageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -767,7 +823,13 @@
   }
 };
 
-TEST_F('OSSettingsDevicePageTest', 'DevicePageTest', () => {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_DevicePageTest DISABLED_DevicePageTest');
+GEN('#else');
+GEN('#define MAYBE_DevicePageTest DevicePageTest');
+GEN('#endif');
+TEST_F('OSSettingsDevicePageTest', 'MAYBE_DevicePageTest', () => {
   mocha.grep(assert(device_page_tests.TestNames.DevicePage)).run();
 });
 
@@ -776,31 +838,76 @@
   mocha.grep(assert(device_page_tests.TestNames.Display)).run();
 });
 
-TEST_F('OSSettingsDevicePageTest', 'KeyboardTest', () => {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_KeyboardTest DISABLED_KeyboardTest');
+GEN('#else');
+GEN('#define MAYBE_KeyboardTest KeyboardTest');
+GEN('#endif');
+TEST_F('OSSettingsDevicePageTest', 'MAYBE_KeyboardTest', () => {
   mocha.grep(assert(device_page_tests.TestNames.Keyboard)).run();
 });
 
-TEST_F('OSSettingsDevicePageTest', 'NightLightTest', () => {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_NightLightTest DISABLED_NightLightTest');
+GEN('#else');
+GEN('#define MAYBE_NightLightTest NightLightTest');
+GEN('#endif');
+TEST_F('OSSettingsDevicePageTest', 'MAYBE_NightLightTest', () => {
   mocha.grep(assert(device_page_tests.TestNames.NightLight)).run();
 });
 
-TEST_F('OSSettingsDevicePageTest', 'PointersTest', () => {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_PointersTest DISABLED_PointersTest');
+GEN('#else');
+GEN('#define MAYBE_PointersTest PointersTest');
+GEN('#endif');
+TEST_F('OSSettingsDevicePageTest', 'MAYBE_PointersTest', () => {
   mocha.grep(assert(device_page_tests.TestNames.Pointers)).run();
 });
 
-TEST_F('OSSettingsDevicePageTest', 'PointersWithPointingStickTest', () => {
-  mocha.grep(assert(device_page_tests.TestNames.PointingStick)).run();
-});
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_PointersWithPointingStickTest \\');
+GEN('    DISABLED_PointersWithPointingStickTest');
+GEN('#else');
+GEN('#define MAYBE_PointersWithPointingStickTest \\');
+GEN('    PointersWithPointingStickTest');
+GEN('#endif');
+TEST_F(
+    'OSSettingsDevicePageTest', 'MAYBE_PointersWithPointingStickTest', () => {
+      mocha.grep(assert(device_page_tests.TestNames.PointingStick)).run();
+    });
 
-TEST_F('OSSettingsDevicePageTest', 'PowerTest', () => {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_PowerTest DISABLED_PowerTest');
+GEN('#else');
+GEN('#define MAYBE_PowerTest PowerTest');
+GEN('#endif');
+TEST_F('OSSettingsDevicePageTest', 'MAYBE_PowerTest', () => {
   mocha.grep(assert(device_page_tests.TestNames.Power)).run();
 });
 
-TEST_F('OSSettingsDevicePageTest', 'StorageTest', () => {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_StorageTest DISABLED_StorageTest');
+GEN('#else');
+GEN('#define MAYBE_StorageTest StorageTest');
+GEN('#endif');
+TEST_F('OSSettingsDevicePageTest', 'MAYBE_StorageTest', () => {
   mocha.grep(assert(device_page_tests.TestNames.Storage)).run();
 });
 
-TEST_F('OSSettingsDevicePageTest', 'StylusTest', () => {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_StylusTest DISABLED_StylusTest');
+GEN('#else');
+GEN('#define MAYBE_StylusTest StylusTest');
+GEN('#endif');
+TEST_F('OSSettingsDevicePageTest', 'MAYBE_StylusTest', () => {
   mocha.grep(assert(device_page_tests.TestNames.Stylus)).run();
 });
 
@@ -819,8 +926,8 @@
 };
 
 TEST_F(
-    'OSSettingsDevicePageKeyboardArrangementDisabledTest',
-    'KeyboardArrangement', () => {
+    'OSSettingsDevicePageKeyboardArrangementDisabledTest', 'MAYBE_AllJsTests',
+    () => {
       mocha
           .grep(assert(device_page_tests.TestNames.KeyboardArrangementDisabled))
           .run();
@@ -845,7 +952,7 @@
   }
 };
 
-TEST_F('OSSettingsFingerprintListTest', 'AllJsTests', () => {
+TEST_F('OSSettingsFingerprintListTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -877,7 +984,7 @@
   }
 };
 
-TEST_F('OSSettingsGoogleAssistantPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsGoogleAssistantPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -893,7 +1000,7 @@
   }
 };
 
-TEST_F('OSSettingsInternetConfigTest', 'All', () => {
+TEST_F('OSSettingsInternetConfigTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -925,7 +1032,7 @@
   }
 };
 
-TEST_F('OSSettingsInternetDetailPageTest', 'All', () => {
+TEST_F('OSSettingsInternetDetailPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -957,7 +1064,7 @@
   }
 };
 
-TEST_F('OSSettingsInternetDetailMenuTest', 'All', () => {
+TEST_F('OSSettingsInternetDetailMenuTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -995,7 +1102,7 @@
   }
 };
 
-TEST_F('OSSettingsInternetPageTest', 'InternetPage', () => {
+TEST_F('OSSettingsInternetPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1033,7 +1140,7 @@
   }
 };
 
-TEST_F('OSSettingsInternetSubpageTest', 'InternetSubpage', () => {
+TEST_F('OSSettingsInternetSubpageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1066,11 +1173,9 @@
   }
 };
 
-TEST_F(
-    'OSSettingsInternetKnownNetworksPageTest', 'InternetKnownNetworksPage',
-    () => {
-      mocha.run();
-    });
+TEST_F('OSSettingsInternetKnownNetworksPageTest', 'MAYBE_AllJsTests', () => {
+  mocha.run();
+});
 
 // Test fixture for settings-internet-known-cellular-networks-page.
 // eslint-disable-next-line no-var
@@ -1099,7 +1204,7 @@
   }
 };
 
-TEST_F('OSSettingsCellularNetworksListTest', 'CellularNetworkList', () => {
+TEST_F('OSSettingsCellularNetworksListTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1125,7 +1230,7 @@
   }
 };
 
-TEST_F('OSSettingsEsimRenameDialogTest', 'EsimRenameDialog', () => {
+TEST_F('OSSettingsEsimRenameDialogTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1152,10 +1257,9 @@
   }
 };
 
-TEST_F(
-    'OSSettingsEsimRemoveProfileDialogTest', 'EsimRemoveProfileDialog', () => {
-      mocha.run();
-    });
+TEST_F('OSSettingsEsimRemoveProfileDialogTest', 'MAYBE_AllJsTests', () => {
+  mocha.run();
+});
 
 // Test fixture for settings-internet-known-networks-page.
 // eslint-disable-next-line no-var
@@ -1182,7 +1286,7 @@
   }
 };
 
-TEST_F('OSSettingsCellularSetupDialogTest', 'CellularSetupDialog', () => {
+TEST_F('OSSettingsCellularSetupDialogTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1202,7 +1306,7 @@
   }
 };
 
-TEST_F('OSSettingsKerberosPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsKerberosPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1225,7 +1329,7 @@
   }
 };
 
-TEST_F('OSSettingsMainTest', 'AllJsTests', () => {
+TEST_F('OSSettingsMainTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1243,7 +1347,7 @@
   }
 };
 
-TEST_F('OSSettingsSearchBoxBrowserTest', 'AllJsTests', () => {
+TEST_F('OSSettingsSearchBoxBrowserTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1265,7 +1369,7 @@
   }
 };
 
-TEST_F('OSSettingsMenuTest', 'AllJsTests', () => {
+TEST_F('OSSettingsMenuTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1287,7 +1391,7 @@
   }
 };
 
-TEST_F('OSSettingsMultideviceFeatureItemTest', 'AllJsTests', () => {
+TEST_F('OSSettingsMultideviceFeatureItemTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1312,7 +1416,8 @@
 };
 
 TEST_F(
-    'OSSettingsMultideviceNotificationAccessDialogTest', 'AllJsTests', () => {
+    'OSSettingsMultideviceNotificationAccessDialogTest', 'MAYBE_AllJsTests',
+    () => {
       mocha.run();
     });
 
@@ -1334,7 +1439,7 @@
   }
 };
 
-TEST_F('OSSettingsMultideviceFeatureToggleTest', 'AllJsTests', () => {
+TEST_F('OSSettingsMultideviceFeatureToggleTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1358,7 +1463,7 @@
   }
 };
 
-TEST_F('OSSettingsMultidevicePageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsMultidevicePageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1383,7 +1488,7 @@
   }
 };
 
-TEST_F('OSSettingsMultideviceSmartLockSubPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsMultideviceSmartLockSubPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1408,7 +1513,7 @@
   }
 };
 
-TEST_F('OSSettingsMultideviceSubPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsMultideviceSubPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1433,9 +1538,10 @@
   }
 };
 
-TEST_F('OSSettingsMultideviceTaskContinuationItemTest', 'AllJsTests', () => {
-  mocha.run();
-});
+TEST_F(
+    'OSSettingsMultideviceTaskContinuationItemTest', 'MAYBE_AllJsTests', () => {
+      mocha.run();
+    });
 
 // Test fixture for the multidevice task continuation sync disabled link.
 // eslint-disable-next-line no-var
@@ -1457,7 +1563,7 @@
 };
 
 TEST_F(
-    'OSSettingsMultideviceTaskContinuationDisabledLinkTest', 'AllJsTests',
+    'OSSettingsMultideviceTaskContinuationDisabledLinkTest', 'MAYBE_AllJsTests',
     () => {
       mocha.run();
     });
@@ -1481,9 +1587,10 @@
   }
 };
 
-TEST_F('OSSettingsMultideviceWifiSyncDisabledLinkTest', 'AllJsTests', () => {
-  mocha.run();
-});
+TEST_F(
+    'OSSettingsMultideviceWifiSyncDisabledLinkTest', 'MAYBE_AllJsTests', () => {
+      mocha.run();
+    });
 
 // Test fixture for the multidevice wifi sync item.
 // eslint-disable-next-line no-var
@@ -1506,7 +1613,7 @@
   }
 };
 
-TEST_F('OSSettingsMultideviceWifiSyncItemTest', 'AllJsTests', () => {
+TEST_F('OSSettingsMultideviceWifiSyncItemTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1534,7 +1641,7 @@
   }
 };
 
-TEST_F('OSSettingsNearbyShareReceiveDialogTest', 'AllJsTests', () => {
+TEST_F('OSSettingsNearbyShareReceiveDialogTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1567,7 +1674,7 @@
   }
 };
 
-TEST_F('OSSettingsNearbyShareSubPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsNearbyShareSubPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1582,7 +1689,7 @@
   }
 };
 
-TEST_F('OSSettingsNetworkProxySectionTest', 'All', () => {
+TEST_F('OSSettingsNetworkProxySectionTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1597,7 +1704,7 @@
   }
 };
 
-TEST_F('OSSettingsNetworkSummaryItemTest', 'All', () => {
+TEST_F('OSSettingsNetworkSummaryItemTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1611,7 +1718,7 @@
   }
 };
 
-TEST_F('OSSettingsNetworkSummaryTest', 'All', () => {
+TEST_F('OSSettingsNetworkSummaryTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1625,7 +1732,7 @@
   }
 };
 
-TEST_F('OSSettingsTetherConnectionDialogTest', 'All', () => {
+TEST_F('OSSettingsTetherConnectionDialogTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1652,7 +1759,7 @@
   }
 };
 
-TEST_F('OSSettingsPeoplePageAccountManagerTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPeoplePageAccountManagerTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1667,7 +1774,7 @@
 
 TEST_F(
     'OSSettingsPeoplePageAccountManagerTestWithAccountManagementFlowsV2Enabled',
-    'AllJsTests', () => {
+    'MAYBE_AllJsTests', () => {
       mocha.run();
     });
 
@@ -1688,7 +1795,7 @@
   }
 };
 
-TEST_F('OSSettingsPeoplePageChangePictureTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPeoplePageChangePictureTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1711,7 +1818,7 @@
   }
 };
 
-TEST_F('OSSettingsPeoplePageKerberosAccountsTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPeoplePageKerberosAccountsTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1740,7 +1847,7 @@
   }
 };
 
-TEST_F('OSSettingsPeoplePageLockScreenTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPeoplePageLockScreenTest', 'MAYBE_AllJsTests', () => {
   settings_people_page_quick_unlock.registerLockScreenTests();
   mocha.run();
 });
@@ -1764,10 +1871,12 @@
   }
 };
 
-TEST_F('OSSettingsPeoplePageQuickUnlockAuthenticateTest', 'AllJsTests', () => {
-  settings_people_page_quick_unlock.registerAuthenticateTests();
-  mocha.run();
-});
+TEST_F(
+    'OSSettingsPeoplePageQuickUnlockAuthenticateTest', 'MAYBE_AllJsTests',
+    () => {
+      settings_people_page_quick_unlock.registerAuthenticateTests();
+      mocha.run();
+    });
 
 // eslint-disable-next-line no-var
 var OSSettingsPeoplePageSetupPinDialogTest =
@@ -1789,7 +1898,7 @@
   }
 };
 
-TEST_F('OSSettingsPeoplePageSetupPinDialogTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPeoplePageSetupPinDialogTest', 'MAYBE_AllJsTests', () => {
   settings_people_page_quick_unlock.registerSetupPinDialogTests();
   mocha.run();
 });
@@ -1820,10 +1929,11 @@
   }
 };
 
-TEST_F('OSSettingsPeoplePagePinAutosubmitDialogTest', 'AllJsTests', () => {
-  settings_people_page_quick_unlock.registerAutosubmitDialogTests();
-  mocha.run();
-});
+TEST_F(
+    'OSSettingsPeoplePagePinAutosubmitDialogTest', 'MAYBE_AllJsTests', () => {
+      settings_people_page_quick_unlock.registerAutosubmitDialogTests();
+      mocha.run();
+    });
 
 // eslint-disable-next-line no-var
 var OSSettingsPeoplePageSyncControlsTest = class extends OSSettingsBrowserTest {
@@ -1847,7 +1957,7 @@
   }
 };
 
-TEST_F('OSSettingsPeoplePageSyncControlsTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPeoplePageSyncControlsTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1880,7 +1990,7 @@
   }
 };
 
-TEST_F('OSSettingsPeoplePageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPeoplePageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1895,8 +2005,8 @@
 };
 
 TEST_F(
-    'OSSettingsPeoplePageTestWithAccountManagementFlowsV2Enabled', 'AllJsTests',
-    () => {
+    'OSSettingsPeoplePageTestWithAccountManagementFlowsV2Enabled',
+    'MAYBE_AllJsTests', () => {
       mocha.run();
     });
 
@@ -1921,12 +2031,14 @@
   }
 };
 
-TEST_F('OSSettingsPrivacyPageTest', 'AllBuilds', () => {
+// Flaky in debug. See MAYBE_AllBuilds definition above.
+TEST_F('OSSettingsPrivacyPageTest', 'MAYBE_AllBuilds', () => {
   mocha.grep('/^(?!PrivacePageTest_OfficialBuild).*$/').run();
 });
 
 GEN('#if BUILDFLAG(GOOGLE_CHROME_BRANDING)');
-TEST_F('OSSettingsPrivacyPageTest', 'PrivacePage_OfficialBuild', () => {
+// Flaky in debug. See MAYBE_OfficialBuild definition above.
+TEST_F('OSSettingsPrivacyPageTest', 'MAYBE_OfficialBuild', () => {
   mocha.grep('PrivacePageTest_OfficialBuild').run();
 });
 GEN('#endif');
@@ -1941,16 +2053,18 @@
   }
 };
 
+// Flaky in debug. See MAYBE_AllBuilds definition above.
 TEST_F(
-    'OSSettingsPrivacyPageTestWithAccountManagementFlowsV2Enabled', 'AllBuilds',
-    () => {
+    'OSSettingsPrivacyPageTestWithAccountManagementFlowsV2Enabled',
+    'MAYBE_AllBuilds', () => {
       mocha.grep('/^(?!PrivacePageTest_OfficialBuild).*$/').run();
     });
 
 GEN('#if BUILDFLAG(GOOGLE_CHROME_BRANDING)');
+// Flaky in debug. See MAYBE_OfficialBuild definition above.
 TEST_F(
     'OSSettingsPrivacyPageTestWithAccountManagementFlowsV2Enabled',
-    'PrivacePage_OfficialBuild', () => {
+    'MAYBE_OfficialBuild', () => {
       mocha.grep('PrivacePageTest_OfficialBuild').run();
     });
 GEN('#endif');
@@ -1972,7 +2086,7 @@
   }
 };
 
-TEST_F('OSSettingsFilesPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsFilesPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -1993,7 +2107,7 @@
   }
 };
 
-TEST_F('OSSettingsParentalControlsPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsParentalControlsPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2023,7 +2137,8 @@
   }
 };
 
-TEST_F('OSSettingsPersonalizationPageTest', 'AllBuilds', () => {
+// Flaky in debug. See MAYBE_AllBuilds definition above.
+TEST_F('OSSettingsPersonalizationPageTest', 'MAYBE_AllBuilds', () => {
   mocha.grep('/^(?!PersonalizationTest_ReleaseOnly).*$/').run();
 });
 
@@ -2056,7 +2171,7 @@
   }
 };
 
-TEST_F('OSSettingsPrintingPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsPrintingPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2080,7 +2195,7 @@
   }
 };
 
-TEST_F('OSSettingsCupsPrinterEntryTest', 'AllJsTests', () => {
+TEST_F('OSSettingsCupsPrinterEntryTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2108,7 +2223,7 @@
   }
 };
 
-TEST_F('OSSettingsCupsPrinterLandingPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsCupsPrinterLandingPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2135,7 +2250,7 @@
   }
 };
 
-TEST_F('OSSettingsCupsPrinterPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsCupsPrinterPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2169,19 +2284,43 @@
   }
 };
 
-TEST_F('OSSettingsLanguagesPageTest', 'LanguageMenu', function() {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_LanguageMenu DISABLED_LanguageMenu');
+GEN('#else');
+GEN('#define MAYBE_LanguageMenu LanguageMenu');
+GEN('#endif');
+TEST_F('OSSettingsLanguagesPageTest', 'MAYBE_LanguageMenu', function() {
   mocha.grep(assert(os_languages_page_tests.TestNames.LanguageMenu)).run();
 });
 
-TEST_F('OSSettingsLanguagesPageTest', 'InputMethods', function() {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_InputMethods DISABLED_InputMethods');
+GEN('#else');
+GEN('#define MAYBE_InputMethods InputMethods');
+GEN('#endif');
+TEST_F('OSSettingsLanguagesPageTest', 'MAYBE_InputMethods', function() {
   mocha.grep(assert(os_languages_page_tests.TestNames.InputMethods)).run();
 });
 
-TEST_F('OSSettingsLanguagesPageTest', 'RecordMetrics', function() {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_RecordMetrics DISABLED_RecordMetrics');
+GEN('#else');
+GEN('#define MAYBE_RecordMetrics RecordMetrics');
+GEN('#endif');
+TEST_F('OSSettingsLanguagesPageTest', 'MAYBE_RecordMetrics', function() {
   mocha.grep(assert(os_languages_page_tests.TestNames.RecordMetrics)).run();
 });
 
-TEST_F('OSSettingsLanguagesPageTest', 'DetailsPage', function() {
+// Flaky in debug. https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_DetailsPage DISABLED_DetailsPage');
+GEN('#else');
+GEN('#define MAYBE_DetailsPage DetailsPage');
+GEN('#endif');
+TEST_F('OSSettingsLanguagesPageTest', 'MAYBE_DetailsPage', function() {
   mocha.grep(assert(os_languages_page_tests.TestNames.DetailsPage)).run();
 });
 
@@ -2210,7 +2349,7 @@
   }
 };
 
-TEST_F('OSSettingsLanguagesPageV2Test', 'AllJsTests', () => {
+TEST_F('OSSettingsLanguagesPageV2Test', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2230,7 +2369,7 @@
   }
 };
 
-TEST_F('OSSettingsSmartInputsPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsSmartInputsPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2253,7 +2392,7 @@
   }
 };
 
-TEST_F('OSSettingsInputMethodOptionsPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsInputMethodOptionsPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2280,7 +2419,7 @@
   }
 };
 
-TEST_F('OSSettingsInputPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsInputPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2327,11 +2466,11 @@
   }
 };
 
-TEST_F('OSSettingsEditDictionaryPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsEditDictionaryPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
-TEST_F('OSSettingsResetPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsResetPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2354,7 +2493,7 @@
   }
 };
 
-TEST_F('OSSettingsOsSearchPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsOsSearchPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2376,7 +2515,7 @@
   }
 };
 
-TEST_F('OSSettingsSmbPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsSmbPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2397,7 +2536,7 @@
   }
 };
 
-TEST_F('OSSettingsAccessibilityPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsAccessibilityPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2420,7 +2559,7 @@
   }
 };
 
-TEST_F('OSSettingsManageAccessibilityPageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsManageAccessibilityPageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2442,7 +2581,7 @@
   }
 };
 
-TEST_F('OSSettingsTextToSpeechSubpageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsTextToSpeechSubpageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2466,7 +2605,7 @@
   }
 };
 
-TEST_F('OSSettingsSwitchAccessSubpageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsSwitchAccessSubpageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2483,9 +2622,11 @@
   }
 };
 
-TEST_F('OSSettingsSwitchAccessActionAssignmentDialogTest', 'AllJsTests', () => {
-  mocha.run();
-});
+TEST_F(
+    'OSSettingsSwitchAccessActionAssignmentDialogTest', 'MAYBE_AllJsTests',
+    () => {
+      mocha.run();
+    });
 
 // Tests for the Date Time timezone selector
 // eslint-disable-next-line no-var
@@ -2502,7 +2643,7 @@
   }
 };
 
-TEST_F('OSSettingsTimezoneSelectorTest', 'AllJsTests', () => {
+TEST_F('OSSettingsTimezoneSelectorTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2524,7 +2665,7 @@
   }
 };
 
-TEST_F('OSSettingsTimezoneSubpageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsTimezoneSubpageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
 
@@ -2547,8 +2688,6 @@
   }
 };
 
-TEST_F('OSSettingsTtsSubpageTest', 'AllJsTests', () => {
+TEST_F('OSSettingsTtsSubpageTest', 'MAYBE_AllJsTests', () => {
   mocha.run();
 });
-
-GEN('#endif  // defined(NDEBUG)');
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
index 7cefab727..351e58b 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
@@ -10,11 +10,6 @@
 GEN('#include "chromeos/constants/chromeos_features.h"');
 GEN('#include "content/public/test/browser_test.h"');
 
-// Only run in release builds because we frequently see test timeouts in debug.
-// We suspect this is because the settings page loads slowly in debug.
-// https://crbug.com/1003483
-GEN('#if defined(NDEBUG)');
-
 /**
  * Checks whether a given element is visible to the user.
  * @param {!Element} element
@@ -41,7 +36,15 @@
   }
 };
 
-TEST_F('OSSettingsUIBrowserTest', 'AllJsTests', () => {
+// Only run in release builds because we frequently see test timeouts in debug.
+// We suspect this is because the settings page loads slowly in debug.
+// https://crbug.com/1003483
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_AllJsTests DISABLED_AllJsTests');
+GEN('#else');
+GEN('#define MAYBE_AllJsTests AllJsTests');
+GEN('#endif');
+TEST_F('OSSettingsUIBrowserTest', 'MAYBE_AllJsTests', () => {
   suite('os-settings-ui', () => {
     let ui;
     let userActionRecorder;
@@ -250,5 +253,3 @@
 
   mocha.run();
 });
-
-GEN('#endif  // defined(NDEBUG)');
diff --git a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
index f1c2599c..bc1c317 100644
--- a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
@@ -270,6 +270,11 @@
   get browsePreload() {
     return 'chrome://settings/test_loader.html?module=settings/password_check_test.js';
   }
+
+  /** @override */
+  get featureList() {
+    return {enabled: ['features::kSafetyCheckWeakPasswords']};
+  }
 };
 
 // Flaky on Mac builds https://crbug.com/1143801
diff --git a/chrome/test/data/webui/settings/password_check_test.js b/chrome/test/data/webui/settings/password_check_test.js
index 74bd93d..76358de 100644
--- a/chrome/test/data/webui/settings/password_check_test.js
+++ b/chrome/test/data/webui/settings/password_check_test.js
@@ -800,8 +800,10 @@
     const subtitle = section.$.subtitle;
     assertTrue(isElementVisible(subtitle));
 
-    const count = await PluralStringProxyImpl.getInstance().getPluralString(
-        'insecurePasswords', 2);
+    const count =
+        await PluralStringProxyImpl.getInstance().getPluralStringTupleWithComma(
+            'safetyCheckPasswordsCompromised', 1, 'safetyCheckPasswordsWeak',
+            1);
     expectEquals(count, subtitle.textContent.trim());
   });
 
@@ -823,7 +825,7 @@
     assertTrue(isElementVisible(subtitle));
 
     const count = await PluralStringProxyImpl.getInstance().getPluralString(
-        'insecurePasswords', 2);
+        'safetyCheckPasswordsWeak', 2);
     expectEquals(count, subtitle.textContent.trim());
   });
 
@@ -1064,8 +1066,10 @@
     assertTrue(isElementVisible(title));
     expectEquals(section.i18n('checkedPasswords'), title.innerText);
     assertTrue(isElementVisible(subtitle));
-    const count = await PluralStringProxyImpl.getInstance().getPluralString(
-        'insecurePasswords', 2);
+    const count =
+        await PluralStringProxyImpl.getInstance().getPluralStringTupleWithComma(
+            'safetyCheckPasswordsCompromised', 1, 'safetyCheckPasswordsWeak',
+            1);
     expectEquals(count, subtitle.textContent.trim());
 
     expectTrue(
diff --git a/chromecast/external_mojo/public/cpp/BUILD.gn b/chromecast/external_mojo/public/cpp/BUILD.gn
index a8f1316e..bfff23d 100644
--- a/chromecast/external_mojo/public/cpp/BUILD.gn
+++ b/chromecast/external_mojo/public/cpp/BUILD.gn
@@ -18,6 +18,7 @@
   deps = [
     ":common",
     "//base",
+    "//chromecast:chromecast_buildflags",
     "//chromecast/external_mojo/public/mojom",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/platform",
diff --git a/chromecast/external_mojo/public/cpp/external_mojo_broker.cc b/chromecast/external_mojo/public/cpp/external_mojo_broker.cc
index 31c6205..49cb95a 100644
--- a/chromecast/external_mojo/public/cpp/external_mojo_broker.cc
+++ b/chromecast/external_mojo/public/cpp/external_mojo_broker.cc
@@ -22,6 +22,7 @@
 #include "base/task/current_thread.h"
 #include "base/token.h"
 #include "base/trace_event/trace_event.h"
+#include "chromecast/chromecast_buildflags.h"
 #include "chromecast/external_mojo/public/cpp/common.h"
 #include "chromecast/external_mojo/public/mojom/connector.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -409,6 +410,10 @@
 ExternalMojoBroker::ExternalMojoBroker(const std::string& broker_path) {
   connector_ = std::make_unique<ConnectorImpl>();
 
+  // For external service support, we expose a channel endpoint on the
+  // |broker_path|. Otherwise, only services in the same process network can
+  // make use of the broker.
+#if BUILDFLAG(ENABLE_EXTERNAL_MOJO_SERVICES)
   LOG(INFO) << "Initializing external mojo broker at: " << broker_path;
 
   mojo::NamedPlatformChannel::Options channel_options;
@@ -425,6 +430,7 @@
 
   read_watcher_ = std::make_unique<ReadWatcher>(
       connector_.get(), server_endpoint.TakePlatformHandle());
+#endif  // BUILDFLAG(ENABLE_EXTERNAL_MOJO_SERVICES)
 }
 
 void ExternalMojoBroker::InitializeChromium(
diff --git a/chromecast/media/cma/BUILD.gn b/chromecast/media/cma/BUILD.gn
index 907c72e..d177387 100644
--- a/chromecast/media/cma/BUILD.gn
+++ b/chromecast/media/cma/BUILD.gn
@@ -80,6 +80,7 @@
   # Android because both protobuf_full and protobuf_lite get included.
   if (!is_android) {
     sources += [
+      "backend/proxy/audio_decoder_pipeline_node_unittest.cc",
       "backend/proxy/cma_backend_proxy_unittest.cc",
       "backend/proxy/proxy_call_translator_unittest.cc",
       "backend/proxy/push_buffer_queue_unittest.cc",
diff --git a/chromecast/media/cma/backend/proxy/BUILD.gn b/chromecast/media/cma/backend/proxy/BUILD.gn
index f7903e0..a21a29c 100644
--- a/chromecast/media/cma/backend/proxy/BUILD.gn
+++ b/chromecast/media/cma/backend/proxy/BUILD.gn
@@ -34,6 +34,8 @@
 
 cast_source_set("proxy") {
   sources = [
+    "audio_decoder_pipeline_node.cc",
+    "audio_decoder_pipeline_node.h",
     "cast_runtime_audio_channel_broker.cc",
     "cast_runtime_audio_channel_broker.h",
     "cma_backend_proxy.cc",
diff --git a/chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node.cc b/chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node.cc
new file mode 100644
index 0000000..3cb12ce
--- /dev/null
+++ b/chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node.cc
@@ -0,0 +1,123 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node.h"
+
+#include "chromecast/media/api/decoder_buffer_base.h"
+#include "chromecast/public/media/decoder_config.h"
+#include "chromecast/public/media/media_pipeline_device_params.h"
+
+namespace chromecast {
+namespace media {
+
+AudioDecoderPipelineNode::AudioDecoderPipelineNode(
+    CmaBackend::AudioDecoder* delegated_decoder)
+    : delegated_decoder_(delegated_decoder) {
+  DCHECK(delegated_decoder_);
+}
+
+AudioDecoderPipelineNode::~AudioDecoderPipelineNode() = default;
+
+void AudioDecoderPipelineNode::SetDelegate(
+    CmaBackend::Decoder::Delegate* delegate) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  delegated_decoder_delegate_ = delegate;
+  delegated_decoder_->SetDelegate(this);
+}
+
+CmaBackend::Decoder::BufferStatus AudioDecoderPipelineNode::PushBuffer(
+    scoped_refptr<DecoderBufferBase> buffer) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  return delegated_decoder_->PushBuffer(std::move(buffer));
+}
+
+bool AudioDecoderPipelineNode::SetConfig(const AudioConfig& config) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  return delegated_decoder_->SetConfig(config);
+}
+
+bool AudioDecoderPipelineNode::SetVolume(float multiplier) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  return delegated_decoder_->SetVolume(multiplier);
+}
+
+CmaBackend::AudioDecoder::RenderingDelay
+AudioDecoderPipelineNode::GetRenderingDelay() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  return delegated_decoder_->GetRenderingDelay();
+}
+
+void AudioDecoderPipelineNode::GetStatistics(
+    CmaBackend::AudioDecoder::Statistics* statistics) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  return delegated_decoder_->GetStatistics(statistics);
+}
+
+bool AudioDecoderPipelineNode::RequiresDecryption() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  return delegated_decoder_->RequiresDecryption();
+}
+
+void AudioDecoderPipelineNode::SetObserver(
+    CmaBackend::AudioDecoder::Observer* observer) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  delegated_decoder_->SetObserver(observer);
+}
+
+void AudioDecoderPipelineNode::SetOwnedDecoder(
+    std::unique_ptr<AudioDecoderPipelineNode> delegated_decoder) {
+  DCHECK_EQ(delegated_decoder.get(), delegated_decoder_);
+
+  owned_delegated_decoder_ = std::move(delegated_decoder);
+}
+
+void AudioDecoderPipelineNode::OnPushBufferComplete(
+    CmaBackend::Decoder::BufferStatus status) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(delegated_decoder_delegate_);
+
+  delegated_decoder_delegate_->OnPushBufferComplete(status);
+}
+
+void AudioDecoderPipelineNode::OnEndOfStream() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(delegated_decoder_delegate_);
+
+  delegated_decoder_delegate_->OnEndOfStream();
+}
+
+void AudioDecoderPipelineNode::OnDecoderError() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(delegated_decoder_delegate_);
+
+  delegated_decoder_delegate_->OnDecoderError();
+}
+
+void AudioDecoderPipelineNode::OnKeyStatusChanged(const std::string& key_id,
+                                                  CastKeyStatus key_status,
+                                                  uint32_t system_code) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(delegated_decoder_delegate_);
+
+  delegated_decoder_delegate_->OnKeyStatusChanged(key_id, key_status,
+                                                  system_code);
+}
+
+void AudioDecoderPipelineNode::OnVideoResolutionChanged(const Size& size) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(delegated_decoder_delegate_);
+
+  delegated_decoder_delegate_->OnVideoResolutionChanged(size);
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node.h b/chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node.h
new file mode 100644
index 0000000..e48c6c6
--- /dev/null
+++ b/chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node.h
@@ -0,0 +1,90 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_MEDIA_CMA_BACKEND_PROXY_AUDIO_DECODER_PIPELINE_NODE_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_PROXY_AUDIO_DECODER_PIPELINE_NODE_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/threading/thread_checker.h"
+#include "chromecast/media/api/cma_backend.h"
+#include "chromecast/public/media/cast_key_status.h"
+
+namespace chromecast {
+
+struct Size;
+
+namespace media {
+
+// This class acts as a single node in a series of Audio Decoders which form an
+// audio processing pipeline. This can be thought of as a tree with the
+// following characteristics:
+// - Each node has at most one child.
+// - The single leaf node is a CmaBackend::AudioDecoder.
+// - All other nodes are instances of this class.
+// By default, all calls to this class's CmaBackend::AudioDecoder nodes are
+// passed back to this node's child. Additionally, if SetDelegate() is called,
+// any calls to this class's CmaBackendDecoder::Delegate methods are passed to
+// the set delegate, forming a pipeline in the opposite order. All methods are
+// expected to be called on the same thread.
+class AudioDecoderPipelineNode : public CmaBackend::Decoder::Delegate,
+                                 public CmaBackend::AudioDecoder {
+ public:
+  explicit AudioDecoderPipelineNode(
+      CmaBackend::AudioDecoder* delegated_decoder);
+  ~AudioDecoderPipelineNode() override;
+
+  // CmaBackend::AudioDecoder overrides.
+  void SetDelegate(CmaBackend::Decoder::Delegate* delegate) override;
+  CmaBackend::Decoder::BufferStatus PushBuffer(
+      scoped_refptr<DecoderBufferBase> buffer) override;
+  bool SetConfig(const AudioConfig& config) override;
+  bool SetVolume(float multiplier) override;
+  CmaBackend::AudioDecoder::RenderingDelay GetRenderingDelay() override;
+  void GetStatistics(CmaBackend::AudioDecoder::Statistics* statistics) override;
+  bool RequiresDecryption() override;
+  void SetObserver(CmaBackend::AudioDecoder::Observer* observer) override;
+
+ protected:
+  inline void CheckCalledOnCorrectThread() const {
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  }
+
+  // Sets the decoder to be owned by this node of the pipeline. Must match the
+  // instance provided as |delegated_decoder_| in the ctor.
+  void SetOwnedDecoder(
+      std::unique_ptr<AudioDecoderPipelineNode> delegated_decoder);
+
+  // CmaBackend::Decoder::Delegate overrides.
+  void OnPushBufferComplete(CmaBackend::Decoder::BufferStatus status) override;
+  void OnEndOfStream() override;
+  void OnDecoderError() override;
+  void OnKeyStatusChanged(const std::string& key_id,
+                          CastKeyStatus key_status,
+                          uint32_t system_code) override;
+  void OnVideoResolutionChanged(const Size& size) override;
+
+ private:
+  friend class AudioDecoderPipelineNodeTests;
+
+  // Local storage for the object backing |delegated_decoder_|, when it is
+  // owned by this class. Empty unless set by SetOwnedDecoder().
+  std::unique_ptr<AudioDecoderPipelineNode> owned_delegated_decoder_;
+
+  // Decoder::Delegate to which all calls to CmaBackend::Decoder::Delegate
+  // methods should be forwarded.
+  CmaBackend::Decoder::Delegate* delegated_decoder_delegate_;
+
+  // Decoder to which all calls to CmaBackend::AudioDecoder methods will be
+  // forwarded.
+  CmaBackend::AudioDecoder* const delegated_decoder_;
+
+  THREAD_CHECKER(thread_checker_);
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_CMA_BACKEND_PROXY_AUDIO_DECODER_PIPELINE_NODE_H_
diff --git a/chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node_unittest.cc b/chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node_unittest.cc
new file mode 100644
index 0000000..6aaf4c5
--- /dev/null
+++ b/chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node_unittest.cc
@@ -0,0 +1,149 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node.h"
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/test/test_simple_task_runner.h"
+#include "chromecast/base/task_runner_impl.h"
+#include "chromecast/media/api/test/mock_cma_backend.h"
+#include "chromecast/media/base/cast_decoder_buffer_impl.h"
+#include "chromecast/media/cma/backend/proxy/cast_runtime_audio_channel_broker.h"
+#include "chromecast/media/cma/backend/proxy/cma_proxy_handler.h"
+#include "chromecast/public/media/cast_key_status.h"
+#include "chromecast/public/media/decoder_config.h"
+#include "chromecast/public/media/stream_id.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+
+class AudioDecoderPipelineNodeTests : public testing::Test {
+ public:
+  AudioDecoderPipelineNodeTests()
+      : parent_delegate_(
+            std::make_unique<
+                testing::StrictMock<MockCmaBackend::DecoderDelegate>>()),
+        child_node_(std::make_unique<
+                    testing::StrictMock<MockCmaBackend::AudioDecoder>>()) {}
+
+  ~AudioDecoderPipelineNodeTests() override = default;
+
+ protected:
+  std::unique_ptr<MockCmaBackend::DecoderDelegate> parent_delegate_;
+  std::unique_ptr<MockCmaBackend::AudioDecoder> child_node_;
+};
+
+TEST_F(AudioDecoderPipelineNodeTests, TestAudioDecoderCalls) {
+  AudioDecoderPipelineNode test_node(child_node_.get());
+
+  // SetDelegate.
+  EXPECT_CALL(*child_node_, SetDelegate(&test_node));
+  test_node.SetDelegate(parent_delegate_.get());
+  testing::Mock::VerifyAndClearExpectations(child_node_.get());
+
+  // PushBuffer.
+  scoped_refptr<CastDecoderBufferImpl> buffer(
+      new CastDecoderBufferImpl(3, StreamId::kPrimary));
+  buffer->writable_data()[0] = 1;
+  buffer->writable_data()[1] = 2;
+  buffer->writable_data()[2] = 3;
+  EXPECT_CALL(*child_node_, PushBuffer(testing::_))
+      .WillOnce(testing::Invoke([ptr = buffer.get()](auto result)
+                                    -> CmaBackend::Decoder::BufferStatus {
+        EXPECT_EQ(ptr, result.get());
+        return CmaBackend::Decoder::BufferStatus::kBufferSuccess;
+      }));
+  EXPECT_EQ(test_node.PushBuffer(buffer),
+            CmaBackend::Decoder::BufferStatus::kBufferSuccess);
+  testing::Mock::VerifyAndClearExpectations(child_node_.get());
+
+  // SetConfig.
+  AudioConfig config;
+  EXPECT_CALL(*child_node_, SetConfig(testing::_))
+      .WillOnce(testing::Return(true))
+      .WillOnce(testing::Return(false));
+  EXPECT_EQ(test_node.SetConfig(config), true);
+  EXPECT_EQ(test_node.SetConfig(config), false);
+  testing::Mock::VerifyAndClearExpectations(child_node_.get());
+
+  // SetVolume.
+  EXPECT_CALL(*child_node_, SetVolume(42)).WillOnce(testing::Return(true));
+  EXPECT_EQ(test_node.SetVolume(42), true);
+  EXPECT_CALL(*child_node_, SetVolume(23)).WillOnce(testing::Return(false));
+  EXPECT_EQ(test_node.SetVolume(23), false);
+  testing::Mock::VerifyAndClearExpectations(child_node_.get());
+
+  // GetStatistics
+  CmaBackend::AudioDecoder::Statistics stats;
+  EXPECT_CALL(*child_node_, GetStatistics(&stats));
+  test_node.GetStatistics(&stats);
+  testing::Mock::VerifyAndClearExpectations(child_node_.get());
+
+  // RequiresDecryption.
+  EXPECT_CALL(*child_node_, RequiresDecryption())
+      .WillOnce(testing::Return(true))
+      .WillOnce(testing::Return(false));
+  EXPECT_EQ(test_node.RequiresDecryption(), true);
+  EXPECT_EQ(test_node.RequiresDecryption(), false);
+  testing::Mock::VerifyAndClearExpectations(child_node_.get());
+
+  // SetDelegate.
+  EXPECT_CALL(*child_node_, SetObserver(nullptr));
+  test_node.SetObserver(nullptr);
+  testing::Mock::VerifyAndClearExpectations(child_node_.get());
+}
+
+TEST_F(AudioDecoderPipelineNodeTests, TestDecoderDelegateCalls) {
+  AudioDecoderPipelineNode test_node(child_node_.get());
+  auto* node_as_delegate =
+      static_cast<CmaBackend::Decoder::Delegate*>(&test_node);
+  EXPECT_CALL(*child_node_, SetDelegate(&test_node));
+  test_node.SetDelegate(parent_delegate_.get());
+  testing::Mock::VerifyAndClearExpectations(child_node_.get());
+
+  // OnPushBufferComplete.
+  EXPECT_CALL(
+      *parent_delegate_,
+      OnPushBufferComplete(CmaBackend::Decoder::BufferStatus::kBufferSuccess));
+  node_as_delegate->OnPushBufferComplete(
+      CmaBackend::Decoder::BufferStatus::kBufferSuccess);
+  testing::Mock::VerifyAndClearExpectations(parent_delegate_.get());
+  EXPECT_CALL(
+      *parent_delegate_,
+      OnPushBufferComplete(CmaBackend::Decoder::BufferStatus::kBufferFailed));
+  node_as_delegate->OnPushBufferComplete(
+      CmaBackend::Decoder::BufferStatus::kBufferFailed);
+
+  // OnEndOfStream.
+  EXPECT_CALL(*parent_delegate_, OnEndOfStream());
+  node_as_delegate->OnEndOfStream();
+  testing::Mock::VerifyAndClearExpectations(parent_delegate_.get());
+
+  // OnDecoderError.
+  EXPECT_CALL(*parent_delegate_, OnDecoderError());
+  node_as_delegate->OnDecoderError();
+  testing::Mock::VerifyAndClearExpectations(parent_delegate_.get());
+
+  // OnKeyStatusChanged.
+  std::string key = "foobar";
+  CastKeyStatus key_status = CastKeyStatus::KEY_STATUS_EXPIRED;
+  uint32_t system_code = 42;
+  EXPECT_CALL(*parent_delegate_,
+              OnKeyStatusChanged(key, key_status, system_code));
+  node_as_delegate->OnKeyStatusChanged(key, key_status, system_code);
+  testing::Mock::VerifyAndClearExpectations(parent_delegate_.get());
+
+  // OnVideoResolutionChanged.
+  Size size(12, 27);
+  EXPECT_CALL(*parent_delegate_, OnVideoResolutionChanged(testing::_));
+  node_as_delegate->OnVideoResolutionChanged(size);
+  testing::Mock::VerifyAndClearExpectations(parent_delegate_.get());
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/proxy/cma_backend_proxy_unittest.cc b/chromecast/media/cma/backend/proxy/cma_backend_proxy_unittest.cc
index f56d8eb..ff9f88c 100644
--- a/chromecast/media/cma/backend/proxy/cma_backend_proxy_unittest.cc
+++ b/chromecast/media/cma/backend/proxy/cma_backend_proxy_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/memory/ref_counted.h"
 #include "chromecast/media/api/cma_backend.h"
 #include "chromecast/media/api/decoder_buffer_base.h"
+#include "chromecast/media/api/test/mock_cma_backend.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -18,23 +19,12 @@
 namespace media {
 namespace {
 
-class MockCmaBackend : public CmaBackend {
- public:
-  MOCK_METHOD0(CreateAudioDecoder, CmaBackend::AudioDecoder*());
-  MOCK_METHOD0(CreateVideoDecoder, CmaBackend::VideoDecoder*());
-  MOCK_METHOD0(Initialize, bool());
-  MOCK_METHOD1(Start, bool(int64_t));
-  MOCK_METHOD0(Stop, void());
-  MOCK_METHOD0(Pause, bool());
-  MOCK_METHOD0(Resume, bool());
-  MOCK_METHOD0(GetCurrentPts, int64_t());
-  MOCK_METHOD1(SetPlaybackRate, bool(float rate));
-  MOCK_METHOD0(LogicalPause, void());
-  MOCK_METHOD0(LogicalResume, void());
-};
-
 class MockMultizoneAudioDecoderProxy : public MultizoneAudioDecoderProxy {
  public:
+  MockMultizoneAudioDecoderProxy()
+      : MultizoneAudioDecoderProxy(&audio_decoder_) {}
+  ~MockMultizoneAudioDecoderProxy() override = default;
+
   MOCK_METHOD0(Initialize, void());
   MOCK_METHOD1(Start, void(int64_t));
   MOCK_METHOD0(Stop, void());
@@ -52,6 +42,10 @@
   MOCK_METHOD1(GetStatistics, void(Statistics*));
   MOCK_METHOD0(RequiresDecryption, bool());
   MOCK_METHOD1(SetObserver, void(Observer*));
+
+ private:
+  // Used only for the ctor parameter.
+  MockCmaBackend::AudioDecoder audio_decoder_;
 };
 
 }  // namespace
diff --git a/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy.h b/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy.h
index 5938c56..bf938a9 100644
--- a/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy.h
+++ b/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy.h
@@ -11,20 +11,17 @@
 #include "base/memory/ref_counted.h"
 #include "chromecast/media/api/cma_backend.h"
 #include "chromecast/media/api/decoder_buffer_base.h"
+#include "chromecast/media/cma/backend/proxy/audio_decoder_pipeline_node.h"
 
 namespace chromecast {
 namespace media {
 
 // This class is used to decrypt then proxy audio data to an external
 // CmaBackend::AudioDecoder over gRPC.
-class MultizoneAudioDecoderProxy : public CmaBackend::AudioDecoder {
+class MultizoneAudioDecoderProxy : public AudioDecoderPipelineNode {
  public:
-  using BufferStatus = CmaBackend::Decoder::BufferStatus;
-  using Delegate = CmaBackend::Decoder::Delegate;
-  using Observer = CmaBackend::AudioDecoder::Observer;
-  using RenderingDelay = CmaBackend::AudioDecoder::RenderingDelay;
-  using Statistics = CmaBackend::AudioDecoder::Statistics;
-
+  MultizoneAudioDecoderProxy(CmaBackend::AudioDecoder* decoder)
+      : AudioDecoderPipelineNode(decoder) {}
   ~MultizoneAudioDecoderProxy() override = default;
 
   virtual void Initialize() = 0;
diff --git a/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy_impl.cc b/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy_impl.cc
index 8272521..50e4aa3c 100644
--- a/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy_impl.cc
+++ b/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy_impl.cc
@@ -14,134 +14,104 @@
 MultizoneAudioDecoderProxyImpl::MultizoneAudioDecoderProxyImpl(
     const MediaPipelineDeviceParams& params,
     CmaBackend::AudioDecoder* downstream_decoder)
-    : cast_session_id_(params.session_id),
+    : MultizoneAudioDecoderProxy(downstream_decoder),
+      cast_session_id_(params.session_id),
       decoder_mode_(CmaProxyHandler::AudioDecoderOperationMode::kMultiroomOnly),
-      downstream_decoder_(downstream_decoder),
       proxy_handler_(CmaProxyHandler::Create(params.task_runner, this)) {
-  DCHECK(downstream_decoder_);
   DCHECK(proxy_handler_);
+}
 
-  DETACH_FROM_SEQUENCE(sequence_checker_);
+MultizoneAudioDecoderProxyImpl::MultizoneAudioDecoderProxyImpl(
+    const MediaPipelineDeviceParams& params,
+    std::unique_ptr<AudioDecoderPipelineNode> downstream_decoder)
+    : MultizoneAudioDecoderProxyImpl(params, downstream_decoder.get()) {
+  SetOwnedDecoder(std::move(downstream_decoder));
 }
 
 MultizoneAudioDecoderProxyImpl::~MultizoneAudioDecoderProxyImpl() = default;
 
 void MultizoneAudioDecoderProxyImpl::Initialize() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   proxy_handler_->Initialize(cast_session_id_, decoder_mode_);
 }
 
 void MultizoneAudioDecoderProxyImpl::Start(int64_t start_pts) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   proxy_handler_->Start(start_pts);
 }
 
 void MultizoneAudioDecoderProxyImpl::Stop() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   proxy_handler_->Stop();
 }
 
 void MultizoneAudioDecoderProxyImpl::Pause() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   proxy_handler_->Pause();
 }
 
 void MultizoneAudioDecoderProxyImpl::Resume() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   proxy_handler_->Resume();
 }
 
 void MultizoneAudioDecoderProxyImpl::SetPlaybackRate(float rate) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   proxy_handler_->SetPlaybackRate(rate);
 }
 
 void MultizoneAudioDecoderProxyImpl::LogicalPause() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   // There is intentionally no proxy implementation of this method.
 }
 
 void MultizoneAudioDecoderProxyImpl::LogicalResume() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   // There is intentionally no proxy implementation of this method.
 }
 
 int64_t MultizoneAudioDecoderProxyImpl::GetCurrentPts() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
 
   // This will be implemented as part of audio-audio sync.
   NOTIMPLEMENTED();
   return pts_offset_;
 }
 
-void MultizoneAudioDecoderProxyImpl::SetDelegate(Delegate* delegate) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  downstream_decoder_->SetDelegate(delegate);
-}
-
 MultizoneAudioDecoderProxy::BufferStatus
 MultizoneAudioDecoderProxyImpl::PushBuffer(
     scoped_refptr<DecoderBufferBase> buffer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
   if (!proxy_handler_->PushBuffer(buffer)) {
     return BufferStatus::kBufferFailed;
   }
 
-  return downstream_decoder_->PushBuffer(std::move(buffer));
+  return MultizoneAudioDecoderProxy::PushBuffer(std::move(buffer));
 }
 
 bool MultizoneAudioDecoderProxyImpl::SetConfig(const AudioConfig& config) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return proxy_handler_->SetConfig(config) &&
-         downstream_decoder_->SetConfig(config);
-}
-
-bool MultizoneAudioDecoderProxyImpl::SetVolume(float multiplier) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // The proxy implementation of this method is INTENTIONALLY not called here.
-  return true;
-}
-
-MultizoneAudioDecoderProxyImpl::RenderingDelay
-MultizoneAudioDecoderProxyImpl::GetRenderingDelay() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // This will be implemented as part of audio-audio sync.
-  NOTIMPLEMENTED();
-  return RenderingDelay{};
+         MultizoneAudioDecoderProxy::SetConfig(config);
 }
 
 void MultizoneAudioDecoderProxyImpl::GetStatistics(Statistics* statistics) {
   DCHECK(statistics);
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   statistics->decoded_bytes = bytes_decoded_;
 }
 
-bool MultizoneAudioDecoderProxyImpl::RequiresDecryption() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return downstream_decoder_->RequiresDecryption();
-}
-
-void MultizoneAudioDecoderProxyImpl::SetObserver(Observer* observer) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  downstream_decoder_->SetObserver(observer);
-}
-
 void MultizoneAudioDecoderProxyImpl::OnError() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   NOTREACHED();
 }
 
 void MultizoneAudioDecoderProxyImpl::OnPipelineStateChange(
     CmaProxyHandler::PipelineState state) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
 }
 
 void MultizoneAudioDecoderProxyImpl::OnBytesDecoded(
     int64_t decoded_byte_count) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CheckCalledOnCorrectThread();
   bytes_decoded_ = decoded_byte_count;
 }
 
diff --git a/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy_impl.h b/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy_impl.h
index 8e28e5ff..25edf63a 100644
--- a/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy_impl.h
+++ b/chromecast/media/cma/backend/proxy/multizone_audio_decoder_proxy_impl.h
@@ -8,7 +8,6 @@
 #include <limits>
 
 #include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
 #include "chromecast/media/api/cma_backend.h"
 #include "chromecast/media/api/decoder_buffer_base.h"
 #include "chromecast/media/cma/backend/proxy/cma_proxy_handler.h"
@@ -30,6 +29,11 @@
   // call, this instance will be in an undefined state.
   MultizoneAudioDecoderProxyImpl(const MediaPipelineDeviceParams& params,
                                  CmaBackend::AudioDecoder* downstream_decoder);
+
+  MultizoneAudioDecoderProxyImpl(
+      const MediaPipelineDeviceParams& params,
+      std::unique_ptr<AudioDecoderPipelineNode> downstream_decoder);
+
   ~MultizoneAudioDecoderProxyImpl() override;
 
   // MultizoneAudioDecoderProxy implementation:
@@ -48,14 +52,10 @@
   void SetPlaybackRate(float rate) override;
   void LogicalPause() override;
   void LogicalResume() override;
-  void SetDelegate(Delegate* delegate) override;
-  BufferStatus PushBuffer(scoped_refptr<DecoderBufferBase> buffer) override;
+  CmaBackend::Decoder::BufferStatus PushBuffer(
+      scoped_refptr<DecoderBufferBase> buffer) override;
   bool SetConfig(const AudioConfig& config) override;
-  bool SetVolume(float multiplier) override;
-  RenderingDelay GetRenderingDelay() override;
-  void GetStatistics(Statistics* statistics) override;
-  bool RequiresDecryption() override;
-  void SetObserver(Observer* observer) override;
+  void GetStatistics(CmaBackend::AudioDecoder::Statistics* statistics) override;
 
  private:
   // CmaProxyHandler::Client overrides:
@@ -75,18 +75,10 @@
   const std::string cast_session_id_;
   const CmaProxyHandler::AudioDecoderOperationMode decoder_mode_;
 
-  // Decoder to which the CmaBackend::AudioDecoder calls should be duplicated
-  // (when appropriate). It is expected to be the AudioDecoder associated with
-  // the "real" CmaBackend, which plays out audio data using the physical
-  // device's hardware. By design, this decoder is always assumed to exist.
-  CmaBackend::AudioDecoder* const downstream_decoder_;
-
   // This is the local instance representing the "remote" backend. All above
   // public method calls should call into this instance to proxy the call to
   // the remote backend.
   std::unique_ptr<CmaProxyHandler> proxy_handler_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
 };
 
 }  // namespace media
diff --git a/chromeos/components/account_manager/account_manager_ash.cc b/chromeos/components/account_manager/account_manager_ash.cc
index 39f8d53..b256e71b 100644
--- a/chromeos/components/account_manager/account_manager_ash.cc
+++ b/chromeos/components/account_manager/account_manager_ash.cc
@@ -59,6 +59,16 @@
       base::BindOnce(&MarshalAccounts, std::move(callback)));
 }
 
+void AccountManagerAsh::ShowAddAccountDialog(
+    ShowAddAccountDialogCallback callback) {
+  // TODO(crbug.com/1140469): implement this.
+}
+
+void AccountManagerAsh::ShowReauthAccountDialog(const std::string& email,
+                                                base::OnceClosure closure) {
+  // TODO(crbug.com/1140469): implement this.
+}
+
 void AccountManagerAsh::OnTokenUpserted(
     const account_manager::Account& account) {
   for (auto& observer : observers_)
diff --git a/chromeos/components/account_manager/account_manager_ash.h b/chromeos/components/account_manager/account_manager_ash.h
index 93ef434..601e1b80 100644
--- a/chromeos/components/account_manager/account_manager_ash.h
+++ b/chromeos/components/account_manager/account_manager_ash.h
@@ -33,6 +33,9 @@
   void IsInitialized(IsInitializedCallback callback) override;
   void AddObserver(AddObserverCallback callback) override;
   void GetAccounts(GetAccountsCallback callback) override;
+  void ShowAddAccountDialog(ShowAddAccountDialogCallback callback) override;
+  void ShowReauthAccountDialog(const std::string& email,
+                               base::OnceClosure closure) override;
 
   // chromeos::AccountManager::Observer:
   void OnTokenUpserted(const account_manager::Account& account) override;
diff --git a/chromeos/components/account_manager/account_manager_ui.h b/chromeos/components/account_manager/account_manager_ui.h
index 0031347..2adb8f3 100644
--- a/chromeos/components/account_manager/account_manager_ui.h
+++ b/chromeos/components/account_manager/account_manager_ui.h
@@ -6,6 +6,7 @@
 #define CHROMEOS_COMPONENTS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_UI_H_
 
 #include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/component_export.h"
 
 namespace chromeos {
@@ -25,7 +26,10 @@
 
   // Show system dialog for account reauthentication.
   // `email` is the email of account that will be reauthenticated.
-  virtual void ShowReauthAccountDialog(const std::string& email) = 0;
+  // `close_dialog_closure` callback will be called when dialog is closed.
+  virtual void ShowReauthAccountDialog(
+      const std::string& email,
+      base::OnceClosure close_dialog_closure) = 0;
 
   virtual bool IsDialogShown() = 0;
 };
diff --git a/chromeos/components/camera_app_ui/BUILD.gn b/chromeos/components/camera_app_ui/BUILD.gn
index 5aab63b3..5149c5b 100644
--- a/chromeos/components/camera_app_ui/BUILD.gn
+++ b/chromeos/components/camera_app_ui/BUILD.gn
@@ -23,14 +23,17 @@
     "url_constants.h",
   ]
 
+  public_deps = [
+    "resources/strings",
+    "//chromeos/resources:camera_app_resources",
+  ]
+
   deps = [
     ":mojo_bindings",
     ":mojo_bindings_js",
-    "resources/strings",
     "//ash/public/cpp",
     "//chromeos/components/web_applications",
     "//chromeos/constants",
-    "//chromeos/resources:camera_app_resources",
     "//chromeos/strings",
     "//chromeos/system",
     "//components/arc",
diff --git a/chromeos/components/camera_app_ui/camera_app_ui.cc b/chromeos/components/camera_app_ui/camera_app_ui.cc
index 10efcde..310d67f4 100644
--- a/chromeos/components/camera_app_ui/camera_app_ui.cc
+++ b/chromeos/components/camera_app_ui/camera_app_ui.cc
@@ -10,6 +10,7 @@
 #include "chromeos/components/camera_app_ui/camera_app_helper_impl.h"
 #include "chromeos/components/camera_app_ui/resources.h"
 #include "chromeos/components/camera_app_ui/url_constants.h"
+#include "chromeos/grit/chromeos_camera_app_resources_map.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/browser/browser_context.h"
@@ -25,6 +26,7 @@
 #include "media/capture/video/chromeos/mojom/camera_app.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "mojo/public/js/grit/mojo_bindings_resources.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "ui/aura/window.h"
 #include "ui/webui/webui_allowlist.h"
@@ -46,9 +48,8 @@
                             kChromeosCameraAppResources[i].value);
   }
 
-  for (const auto& res : kGritResourceMap) {
-    source->AddResourcePath(res.path, res.id);
-  }
+  source->AddResourcePath("js/mojo/mojo_bindings_lite.js",
+                          IDR_MOJO_MOJO_BINDINGS_LITE_JS);
 
   delegate->PopulateLoadTimeData(source);
 
diff --git a/chromeos/components/camera_app_ui/resources.h b/chromeos/components/camera_app_ui/resources.h
index b580580..4ac54500 100644
--- a/chromeos/components/camera_app_ui/resources.h
+++ b/chromeos/components/camera_app_ui/resources.h
@@ -6,9 +6,6 @@
 #define CHROMEOS_COMPONENTS_CAMERA_APP_UI_RESOURCES_H_
 
 #include "chromeos/components/camera_app_ui/resources/strings/grit/chromeos_camera_app_strings.h"
-#include "chromeos/grit/chromeos_camera_app_resources.h"
-#include "chromeos/grit/chromeos_camera_app_resources_map.h"
-#include "mojo/public/js/grit/mojo_bindings_resources.h"
 
 namespace chromeos {
 
@@ -116,36 +113,6 @@
     {"error_msg_camera_being_used", IDS_ERROR_MSG_CAMERA_BEING_USED},
 };
 
-const struct {
-  const char* path;
-  int id;
-} kGritResourceMap[] = {
-    {"js/browser_proxy/browser_proxy.js", IDR_CAMERA_WEBUI_BROWSER_PROXY_JS},
-    {"js/preload_images.js", IDR_CAMERA_PRELOAD_IMAGES_JS},
-    {"js/window_controller/window_controller.js",
-     IDR_CAMERA_MOJO_WINDOW_CONTROLLER_JS},
-    {"js/mojo/camera_intent.mojom-lite.js",
-     IDR_CAMERA_CAMERA_INTENT_MOJOM_LITE_JS},
-    {"js/mojo/image_capture.mojom-lite.js",
-     IDR_CAMERA_IMAGE_CAPTURE_MOJOM_LITE_JS},
-    {"js/mojo/camera_common.mojom-lite.js",
-     IDR_CAMERA_CAMERA_COMMON_MOJOM_LITE_JS},
-    {"js/mojo/camera_metadata.mojom-lite.js",
-     IDR_CAMERA_CAMERA_METADATA_MOJOM_LITE_JS},
-    {"js/mojo/camera_metadata_tags.mojom-lite.js",
-     IDR_CAMERA_CAMERA_METADATA_TAGS_MOJOM_LITE_JS},
-    {"js/mojo/camera_app.mojom-lite.js", IDR_CAMERA_CAMERA_APP_MOJOM_LITE_JS},
-    {"js/mojo/mojo_bindings_lite.js", IDR_MOJO_MOJO_BINDINGS_LITE_JS},
-    {"js/mojo/camera_app_helper.mojom-lite.js",
-     IDR_CAMERA_CAMERA_APP_HELPER_MOJOM_LITE_JS},
-    {"js/mojo/time.mojom-lite.js", IDR_CAMERA_TIME_MOJOM_LITE_JS},
-    {"js/mojo/idle_manager.mojom-lite.js",
-     IDR_CAMERA_IDLE_MANAGER_MOJOM_LITE_JS},
-    {"js/mojo/camera_app.mojom-lite.js", IDR_CAMERA_CAMERA_APP_MOJOM_LITE_JS},
-    {"js/mojo/geometry.mojom-lite.js", IDR_CAMERA_GEOMETRY_MOJOM_LITE_JS},
-    {"js/mojo/range.mojom-lite.js", IDR_CAMERA_RANGE_MOJOM_LITE_JS},
-};
-
 }  // namespace chromeos
 
 #endif  // CHROMEOS_COMPONENTS_CAMERA_APP_UI_RESOURCES_H_
diff --git a/chromeos/components/camera_app_ui/resources/camera_app_resources.grd b/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
index b133c5bf..89f351d 100644
--- a/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
+++ b/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
@@ -62,7 +62,6 @@
       <structure name="IDR_CAMERA_MODE_RECORD_TIME_JS" file="js/views/camera/mode/record_time.js" type="chrome_html" />
       <structure name="IDR_CAMERA_MODE_SQUARE_JS" file="js/views/camera/mode/square.js" type="chrome_html" />
       <structure name="IDR_CAMERA_MODE_VIDEO_JS" file="js/views/camera/mode/video.js" type="chrome_html" />
-      <structure name="IDR_CAMERA_MOJO_WINDOW_CONTROLLER_JS" file="js/window_controller/mojo_window_controller.js" type="chrome_html" />
       <structure name="IDR_CAMERA_MP4_VIDEO_PROCESSOR_JS" file="js/models/mp4_video_processor.js" type="chrome_html" />
       <structure name="IDR_CAMERA_NATIVE_FILE_SYSTEM_ENTRY_JS" file="js/models/native_file_system_entry.js" type="chrome_html" />
       <structure name="IDR_CAMERA_NAV_JS" file="js/nav.js" type="chrome_html" />
@@ -93,59 +92,78 @@
       <structure name="IDR_CAMERA_VIEW_JS" file="js/views/view.js" type="chrome_html" />
       <structure name="IDR_CAMERA_WAITABLE_EVENT_JS" file="js/waitable_event.js" type="chrome_html" />
       <structure name="IDR_CAMERA_WARNING_JS" file="js/views/warning.js" type="chrome_html" />
-      <structure name="IDR_CAMERA_WEBUI_BROWSER_PROXY_JS" file="js/browser_proxy/webui_browser_proxy.js" type="chrome_html" />
       <structure name="IDR_CAMERA_WINDOW_CONTROLLER_INTERFACE_JS" file="js/window_controller/window_controller_interface.js" type="chrome_html" />
     </structures>
     <includes>
+      <include name="IDR_CAMERA_MOJO_WINDOW_CONTROLLER_JS"
+          file="js/window_controller/mojo_window_controller.js"
+          resource_path="js/window_controller/window_controller.js"
+          type="chrome_html" />
+      <include name="IDR_CAMERA_WEBUI_BROWSER_PROXY_JS"
+          file="js/browser_proxy/webui_browser_proxy.js"
+          resource_path="js/browser_proxy/browser_proxy.js"
+          type="chrome_html" />
       <!-- Mojo Lite Bindings -->
       <include name="IDR_CAMERA_CAMERA_APP_HELPER_MOJOM_LITE_JS"
           file="${root_gen_dir}/chromeos/components/camera_app_ui/camera_app_helper.mojom-lite.js"
+          resource_path="js/mojo/camera_app_helper.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
       <include name="IDR_CAMERA_CAMERA_APP_MOJOM_LITE_JS"
           file="${root_gen_dir}/media/capture/video/chromeos/mojom/camera_app.mojom-lite.js"
+          resource_path="js/mojo/camera_app.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
       <include name="IDR_CAMERA_CAMERA_COMMON_MOJOM_LITE_JS"
           file="${root_gen_dir}/media/capture/video/chromeos/mojom/camera_common.mojom-lite.js"
+          resource_path="js/mojo/camera_common.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
       <include name="IDR_CAMERA_CAMERA_INTENT_MOJOM_LITE_JS"
           file="${root_gen_dir}/components/arc/mojom/camera_intent.mojom-lite.js"
+          resource_path="js/mojo/camera_intent.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
       <include name="IDR_CAMERA_CAMERA_METADATA_MOJOM_LITE_JS"
           file="${root_gen_dir}/media/capture/video/chromeos/mojom/camera_metadata.mojom-lite.js"
+          resource_path="js/mojo/camera_metadata.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
       <include name="IDR_CAMERA_CAMERA_METADATA_TAGS_MOJOM_LITE_JS"
           file="${root_gen_dir}/media/capture/video/chromeos/mojom/camera_metadata_tags.mojom-lite.js"
+          resource_path="js/mojo/camera_metadata_tags.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
       <include name="IDR_CAMERA_GEOMETRY_MOJOM_LITE_JS"
           file="${root_gen_dir}/ui/gfx/geometry/mojom/geometry.mojom-lite.js"
+          resource_path="js/mojo/geometry.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
       <include name="IDR_CAMERA_IDLE_MANAGER_MOJOM_LITE_JS"
           file="${root_gen_dir}/third_party/blink/public/mojom/idle/idle_manager.mojom-lite.js"
+          resource_path="js/mojo/idle_manager.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
       <include name="IDR_CAMERA_IMAGE_CAPTURE_MOJOM_LITE_JS"
           file="${root_gen_dir}/media/capture/mojom/image_capture.mojom-lite.js"
+          resource_path="js/mojo/image_capture.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
       <include name="IDR_CAMERA_RANGE_MOJOM_LITE_JS"
           file="${root_gen_dir}/ui/gfx/range/mojom/range.mojom-lite.js"
+          resource_path="js/mojo/range.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
       <include name="IDR_CAMERA_TIME_MOJOM_LITE_JS"
           file="${root_gen_dir}/mojo/public/mojom/base/time.mojom-lite.js"
+          resource_path="js/mojo/time.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"/>
 
       <!-- Dynamic generated -->
       <include name="IDR_CAMERA_PRELOAD_IMAGES_JS"
           file="${root_gen_dir}/chromeos/components/camera_app_ui/resources/js/preload_images.js"
+          resource_path="js/preload_images.js"
           use_base_dir="false"
           type="chrome_html"/>
 
diff --git a/chromeos/components/diagnostics_ui/diagnostics_ui.cc b/chromeos/components/diagnostics_ui/diagnostics_ui.cc
index 31cab95..924e5db 100644
--- a/chromeos/components/diagnostics_ui/diagnostics_ui.cc
+++ b/chromeos/components/diagnostics_ui/diagnostics_ui.cc
@@ -29,9 +29,6 @@
 
 namespace {
 
-constexpr char kGeneratedPath[] =
-    "@out_folder@/gen/chromeos/components/diagnostics_ui/resources/";
-
 void AddDiagnosticsStrings(content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
       {"batteryCalculatingText", IDS_DIAGNOSTICS_BATTERY_CALCULATING_TEXT},
@@ -112,18 +109,12 @@
 // longer requires a dependency on //chrome/browser.
 void SetUpWebUIDataSource(content::WebUIDataSource* source,
                           base::span<const GritResourceMap> resources,
-                          const std::string& generated_path,
                           int default_resource) {
   for (const auto& resource : resources) {
-    std::string path = resource.name;
-    if (path.rfind(generated_path, 0) == 0)
-      path = path.substr(generated_path.size());
-
-    source->AddResourcePath(path, resource.value);
+    source->AddResourcePath(resource.name, resource.value);
   }
 
   source->SetDefaultResource(default_resource);
-  source->AddResourcePath("d3.min.js", IDR_D3_SRC_D3_MIN_JS);
   source->AddResourcePath("test_loader.html", IDR_WEBUI_HTML_TEST_LOADER_HTML);
   source->AddResourcePath("test_loader.js", IDR_WEBUI_JS_TEST_LOADER_JS);
 }
@@ -145,16 +136,8 @@
 
   const auto resources = base::make_span(kChromeosDiagnosticsAppResources,
                                          kChromeosDiagnosticsAppResourcesSize);
-  SetUpWebUIDataSource(html_source.get(), resources, kGeneratedPath,
+  SetUpWebUIDataSource(html_source.get(), resources,
                        IDR_DIAGNOSTICS_APP_INDEX_HTML);
-
-  html_source->AddResourcePath(
-      "system_data_provider.mojom-lite.js",
-      IDR_DIAGNOSTICS_SYSTEM_DATA_PROVIDER_MOJO_LITE_JS);
-  html_source->AddResourcePath(
-      "system_routine_controller.mojom-lite.js",
-      IDR_DIAGNOSTICS_SYSTEM_ROUTINE_CONTROLLER_MOJO_LITE_JS);
-
   AddDiagnosticsStrings(html_source.get());
   content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
                                 html_source.release());
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_app_resources.grd b/chromeos/components/diagnostics_ui/resources/diagnostics_app_resources.grd
index bebf05ea..83e4f98b 100644
--- a/chromeos/components/diagnostics_ui/resources/diagnostics_app_resources.grd
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_app_resources.grd
@@ -15,35 +15,35 @@
       <!-- Privileged app host contents. -->
       <include name="IDR_DIAGNOSTICS_APP_ICON" file="app_icon_192.png" type="BINDATA" />
       <include name="IDR_DIAGNOSTICS_APP_INDEX_HTML" file="index.html" type="BINDATA" />
-      <include name="IDR_DIAGNOSTICS_APP_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/diagnostics_app.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_BATTERY_STATUS_CARD_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/battery_status_card.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_CARD_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/diagnostics_card.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_CPU_CARD_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/cpu_card.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_DATA_POINT_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/data_point.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_APP_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/diagnostics_app.js" resource_path="diagnostics_app.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_BATTERY_STATUS_CARD_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/battery_status_card.js" resource_path="battery_status_card.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_CARD_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/diagnostics_card.js" resource_path="diagnostics_card.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_CPU_CARD_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/cpu_card.js" resource_path="cpu_card.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_DATA_POINT_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/data_point.js" resource_path="data_point.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_FAKE_DATA_JS" file="fake_data.js" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_FAKE_METHOD_RESOLVER_JS" file="fake_method_resolver.js" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_FAKE_OBSERVABLES_JS" file="fake_observables.js" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_FAKE_SYSTEM_DATA_PROVIDER_JS" file="fake_system_data_provider.js" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_FAKE_SYSTEM_ROUTINE_CONTROLLER_JS" file="fake_system_routine_controller.js" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_FONTS_CSS_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/diagnostics_fonts_css.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_ICONS_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/icons.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_MEMORY_CARD_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/memory_card.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_FONTS_CSS_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/diagnostics_fonts_css.js" resource_path="diagnostics_fonts_css.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_ICONS_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/icons.js" resource_path="icons.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_MEMORY_CARD_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/memory_card.js" resource_path="memory_card.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_MOJO_INTERFACE_PROVIDER_JS" file="mojo_interface_provider.js" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_MOJO_UTILS_JS" file="mojo_utils.js" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_OVERVIEW_CARD_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/overview_card.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_PERCENT_BAR_CHART_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/percent_bar_chart.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_REALTIME_CPU_CHART_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/realtime_cpu_chart.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_OVERVIEW_CARD_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/overview_card.js" resource_path="overview_card.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_PERCENT_BAR_CHART_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/percent_bar_chart.js" resource_path="percent_bar_chart.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_REALTIME_CPU_CHART_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/realtime_cpu_chart.js" resource_path="realtime_cpu_chart.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_ROUTINE_LIST_EXECUTOR_JS" file="routine_list_executor.js" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_ROUTINE_RESULT_ENTRY_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/routine_result_entry.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_ROUTINE_RESULT_LIST_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/routine_result_list.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_ROUTINE_SECTION_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/routine_section.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_DIAGNOSTICS_SHARED_CSS_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/diagnostics_shared_css.js" use_base_dir="false" type="BINDDATA"/>
-      <include name="IDR_DIAGNOSTICS_SYSTEM_DATA_PROVIDER_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/mojom/system_data_provider.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-      <include name="IDR_DIAGNOSTICS_SYSTEM_ROUTINE_CONTROLLER_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/mojom/system_routine_controller.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-      <include name="IDR_DIAGNOSTICS_TEXT_BADGE_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/text_badge.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_ROUTINE_RESULT_ENTRY_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/routine_result_entry.js" resource_path="routine_result_entry.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_ROUTINE_RESULT_LIST_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/routine_result_list.js" resource_path="routine_result_list.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_ROUTINE_SECTION_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/routine_section.js" resource_path="routine_section.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_DIAGNOSTICS_SHARED_CSS_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/diagnostics_shared_css.js" resource_path="diagnostics_shared_css.js" use_base_dir="false" type="BINDDATA"/>
+      <include name="IDR_DIAGNOSTICS_SYSTEM_DATA_PROVIDER_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/mojom/system_data_provider.mojom-lite.js" resource_path="system_data_provider.mojom-lite.js" use_base_dir="false" type="BINDATA" />
+      <include name="IDR_DIAGNOSTICS_SYSTEM_ROUTINE_CONTROLLER_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/mojom/system_routine_controller.mojom-lite.js" resource_path="system_routine_controller.mojom-lite.js" use_base_dir="false" type="BINDATA" />
+      <include name="IDR_DIAGNOSTICS_TEXT_BADGE_JS" file="${root_gen_dir}/chromeos/components/diagnostics_ui/resources/text_badge.js" resource_path="text_badge.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_TYPES_JS" file="diagnostics_types.js" type="BINDATA"/>
       <include name="IDR_DIAGNOSTICS_UTILS_JS" file="diagnostics_utils.js" type="BINDATA"/>
-      <include name="IDR_D3_SRC_D3_MIN_JS" file="../../../../third_party/d3/src/d3.min.js" type="BINDATA" />
+      <include name="IDR_D3_SRC_D3_MIN_JS" file="../../../../third_party/d3/src/d3.min.js" resource_path="d3.min.js" type="BINDATA" />
     </includes>
   </release>
 </grit>
diff --git a/chromeos/components/help_app_ui/resources/help_app_resources.grd b/chromeos/components/help_app_ui/resources/help_app_resources.grd
index e09160c..8f01213 100644
--- a/chromeos/components/help_app_ui/resources/help_app_resources.grd
+++ b/chromeos/components/help_app_ui/resources/help_app_resources.grd
@@ -21,13 +21,13 @@
           type="BINDATA" />
       <include name="IDR_HELP_APP_HELP_APP_MOJOM_JS"
           file="${root_gen_dir}/chromeos/components/help_app_ui/help_app_ui.mojom-lite.js"
-          use_base_dir="false" type="BINDATA" />
+          resource_path="help_app_ui.mojom-lite.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_HELP_APP_LOCAL_SEARCH_SERVICE_TYPES_MOJOM_JS"
-        file="${root_gen_dir}/chromeos/components/local_search_service/public/mojom/types.mojom-lite.js"
-          use_base_dir="false" type="BINDATA" />
+          file="${root_gen_dir}/chromeos/components/local_search_service/public/mojom/types.mojom-lite.js"
+          resource_path="local_search_service_types.mojom-lite.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_HELP_APP_LOCAL_SEARCH_SERVICE_INDEX_MOJOM_JS"
-        file="${root_gen_dir}/chromeos/components/local_search_service/public/mojom/index.mojom-lite.js"
-          use_base_dir="false" type="BINDATA" />
+          file="${root_gen_dir}/chromeos/components/local_search_service/public/mojom/index.mojom-lite.js"
+          resource_path="local_search_service_index.mojom-lite.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_HELP_APP_INDEX_SCRIPTS_JS" file="help_app_index_scripts.js" flattenhtml="true" type="BINDATA" />
 
       <!-- Unprivileged guest contents. -->
diff --git a/chromeos/components/print_management/print_management_ui.cc b/chromeos/components/print_management/print_management_ui.cc
index cefa6e01..0376fbf 100644
--- a/chromeos/components/print_management/print_management_ui.cc
+++ b/chromeos/components/print_management/print_management_ui.cc
@@ -23,19 +23,11 @@
 namespace printing_manager {
 namespace {
 
-constexpr char kGeneratedPath[] =
-    "@out_folder@/gen/chromeos/components/print_management/resources/";
-
 void SetUpWebUIDataSource(content::WebUIDataSource* source,
                           base::span<const GritResourceMap> resources,
-                          const std::string& generated_path,
                           int default_resource) {
   for (const auto& resource : resources) {
-    std::string path = resource.name;
-    if (path.rfind(generated_path, 0) == 0) {
-      path = path.substr(generated_path.size());
-    }
-    source->AddResourcePath(path, resource.value);
+    source->AddResourcePath(resource.name, resource.value);
   }
   source->SetDefaultResource(default_resource);
   source->AddResourcePath("test_loader.html", IDR_WEBUI_HTML_TEST_LOADER_HTML);
@@ -127,7 +119,7 @@
 
   const auto resources = base::make_span(kChromeosPrintManagementResources,
                                          kChromeosPrintManagementResourcesSize);
-  SetUpWebUIDataSource(html_source.get(), resources, kGeneratedPath,
+  SetUpWebUIDataSource(html_source.get(), resources,
                        IDR_PRINT_MANAGEMENT_INDEX_HTML);
 
   html_source->AddResourcePath("printing_manager.mojom-lite.js",
diff --git a/chromeos/components/print_management/resources/print_management_resources.grd b/chromeos/components/print_management/resources/print_management_resources.grd
index cc1a097..f750947 100644
--- a/chromeos/components/print_management/resources/print_management_resources.grd
+++ b/chromeos/components/print_management/resources/print_management_resources.grd
@@ -13,15 +13,15 @@
   <release seq="1">
     <includes>
       <!-- Privileged app host contents. -->
-      <include name="IDR_PRINT_MANAGEMENT_ICONS_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/icons.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_PRINT_MANAGEMENT_ICONS_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/icons.js" resource_path="icons.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_PRINT_MANAGEMENT_INDEX_HTML" file="index.html" type="BINDATA"/>
-      <include name="IDR_PRINT_MANAGEMENT_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/print_management.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_PRINT_MANAGEMENT_PRINT_JOB_ENTRY_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/print_job_entry.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_PRINT_MANAGEMENT_FONTS_CSS_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/print_management_fonts_css.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_PRINT_MANAGEMENT_SHARED_CSS_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/print_management_shared_css.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_PRINT_MANAGEMENT_PRINT_JOB_CLEAR_HISTORY_DIALOG_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/print_job_clear_history_dialog.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_PRINT_MANAGEMENT_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/print_management.js" resource_path="print_management.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_PRINT_MANAGEMENT_PRINT_JOB_ENTRY_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/print_job_entry.js" resource_path="print_job_entry.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_PRINT_MANAGEMENT_FONTS_CSS_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/print_management_fonts_css.js" resource_path="print_management_fonts_css.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_PRINT_MANAGEMENT_SHARED_CSS_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/print_management_shared_css.js" resource_path="print_management_shared_css.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_PRINT_MANAGEMENT_PRINT_JOB_CLEAR_HISTORY_DIALOG_JS" file="${root_gen_dir}/chromeos/components/print_management/resources/print_job_clear_history_dialog.js" resource_path="print_job_clear_history_dialog.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_PRINT_MANAGEMENT_ICON" file="print_management_192.png" type="BINDATA"/>
-      <include name="IDR_PRINTING_MANAGER_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/print_management/mojom/printing_manager.mojom-lite.js" use_base_dir="false" type="BINDATA" />
+      <include name="IDR_PRINTING_MANAGER_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/print_management/mojom/printing_manager.mojom-lite.js" resource_path="printing_manager.mojom-lite.js" use_base_dir="false" type="BINDATA" />
     </includes>
 
     <structures>
diff --git a/chromeos/components/scanning/resources/scanning_app_resources.grd b/chromeos/components/scanning/resources/scanning_app_resources.grd
index 6a8c21f..2c55c21 100644
--- a/chromeos/components/scanning/resources/scanning_app_resources.grd
+++ b/chromeos/components/scanning/resources/scanning_app_resources.grd
@@ -14,31 +14,31 @@
     <includes>
       <!-- Privileged app host contents. -->
       <include name="IDR_SCANNING_APP_INDEX_HTML" file="index.html" type="BINDATA" />
-      <include name="IDR_SCANNING_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/scanning/mojom/scanning.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-      <include name="IDR_SCANNING_APP_FILE_PATH_MOJO_LITE_JS" file="${root_gen_dir}\mojo\public\mojom\base\file_path.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-      <include name="IDR_SCANNING_APP_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanning_app.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/scanning/mojom/scanning.mojom-lite.js" resource_path="scanning.mojom-lite.js" use_base_dir="false" type="BINDATA" />
+      <include name="IDR_SCANNING_APP_FILE_PATH_MOJO_LITE_JS" file="${root_gen_dir}/mojo/public/mojom/base/file_path.mojom-lite.js" resource_path="file_path.mojom-lite.js" use_base_dir="false" type="BINDATA" />
+      <include name="IDR_SCANNING_APP_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanning_app.js" resource_path="scanning_app.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_SCANNING_APP_SCAN_TO_SELECT_HTML" file="scan_to_select.html" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_SCAN_TO_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scan_to_select.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_SCAN_TO_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scan_to_select.js" resource_path="scan_to_select.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_SCANNING_APP_SCANNER_SELECT_HTML" file="scanner_select.html" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_SCANNER_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanner_select.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_SCANNER_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanner_select.js" resource_path="scanner_select.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_SCANNING_APP_SOURCE_SELECT_HTML" file="source_select.html" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_SOURCE_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/source_select.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_SOURCE_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/source_select.js" resource_path="source_select.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_SCANNING_APP_SCAN_DONE_SECTION_HTML" file="scan_done_section.html" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_SCAN_DONE_SECTION_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scan_done_section.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_SCAN_DONE_SECTION_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scan_done_section.js" resource_path="scan_done_section.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_SCANNING_APP_FILE_TYPE_SELECT_HTML" file="file_type_select.html" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_FILE_TYPE_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/file_type_select.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_FILE_TYPE_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/file_type_select.js" resource_path="file_type_select.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_SCANNING_APP_COLOR_MODE_SELECT_HTML" file="color_mode_select.html" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_COLOR_MODE_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/color_mode_select.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_COLOR_MODE_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/color_mode_select.js" resource_path="color_mode_select.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_SCANNING_APP_PAGE_SIZE_SELECT_HTML" file="page_size_select.html" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_PAGE_SIZE_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/page_size_select.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_PAGE_SIZE_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/page_size_select.js" resource_path="page_size_select.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_SCANNING_APP_RESOLUTION_SELECT_HTML" file="resolution_select.html" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_RESOLUTION_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/resolution_select.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_SCAN_SETTINGS_SECTION_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scan_settings_section.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_THROBBER_CSS_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/throbber_css.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_SCANNING_SHARED_CSS_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanning_shared_css.js" use_base_dir="false" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_SCANNING_FONTS_CSS_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanning_fonts_css.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_RESOLUTION_SELECT_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/resolution_select.js" resource_path="resolution_select.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_SCAN_SETTINGS_SECTION_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scan_settings_section.js" resource_path="scan_settings_section.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_THROBBER_CSS_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/throbber_css.js" resource_path="throbber_css.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_SCANNING_SHARED_CSS_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanning_shared_css.js" resource_path="scanning_shared_css.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_SCANNING_FONTS_CSS_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scanning_fonts_css.js" resource_path="scanning_fonts_css.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_SCANNING_APP_SCAN_PREVIEW_HTML" file="scan_preview.html" type="BINDATA"/>
-      <include name="IDR_SCANNING_APP_SCAN_PREVIEW_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scan_preview.js" use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_SCANNING_APP_SCAN_PREVIEW_JS" file="${root_gen_dir}/chromeos/components/scanning/resources/scan_preview.js" resource_path="scan_preview.js" use_base_dir="false" type="BINDATA"/>
       <include name="IDR_SCANNING_APP_ICON_16" file="scanning_app_icon_16.png" type="BINDATA" />
       <include name="IDR_SCANNING_APP_ICON_32" file="scanning_app_icon_32.png" type="BINDATA" />
       <include name="IDR_SCANNING_APP_ICON_48" file="scanning_app_icon_48.png" type="BINDATA" />
diff --git a/chromeos/components/scanning/resources/source_select.html b/chromeos/components/scanning/resources/source_select.html
index 960cefb..bb97170 100644
--- a/chromeos/components/scanning/resources/source_select.html
+++ b/chromeos/components/scanning/resources/source_select.html
@@ -12,7 +12,7 @@
           [[getSourceTypeString_(source.type)]]
         </option>
       </template>
-      <option value="" hidden="[[sources.length]]">
+      <option value="" hidden="[[options.length]]">
         [[i18n('defaultSourceOptionText')]]
       </option>
     </select>
diff --git a/chromeos/components/scanning/scanning_ui.cc b/chromeos/components/scanning/scanning_ui.cc
index cfd73a9..3121412 100644
--- a/chromeos/components/scanning/scanning_ui.cc
+++ b/chromeos/components/scanning/scanning_ui.cc
@@ -29,21 +29,13 @@
 
 namespace {
 
-constexpr char kGeneratedPath[] =
-    "@out_folder@/gen/chromeos/components/scanning/resources/";
-
 // TODO(jschettler): Replace with webui::SetUpWebUIDataSource() once it no
 // longer requires a dependency on //chrome/browser.
 void SetUpWebUIDataSource(content::WebUIDataSource* source,
                           base::span<const GritResourceMap> resources,
-                          const std::string& generated_path,
                           int default_resource) {
   for (const auto& resource : resources) {
-    std::string path = resource.name;
-    if (path.rfind(generated_path, 0) == 0)
-      path = path.substr(generated_path.size());
-
-    source->AddResourcePath(path, resource.value);
+    source->AddResourcePath(resource.name, resource.value);
   }
 
   source->SetDefaultResource(default_resource);
@@ -136,7 +128,7 @@
 
   const auto resources = base::make_span(kChromeosScanningAppResources,
                                          kChromeosScanningAppResourcesSize);
-  SetUpWebUIDataSource(html_source.get(), resources, kGeneratedPath,
+  SetUpWebUIDataSource(html_source.get(), resources,
                        IDR_SCANNING_APP_INDEX_HTML);
 
   html_source->AddResourcePath("scanning.mojom-lite.js",
diff --git a/chromeos/components/telemetry_extension_ui/resources/telemetry_extension_resources.grd b/chromeos/components/telemetry_extension_ui/resources/telemetry_extension_resources.grd
index 1b76e8a3..205c9b5 100644
--- a/chromeos/components/telemetry_extension_ui/resources/telemetry_extension_resources.grd
+++ b/chromeos/components/telemetry_extension_ui/resources/telemetry_extension_resources.grd
@@ -16,13 +16,13 @@
         <!-- Privileged app host contents. -->
         <include name="IDR_TELEMETRY_EXTENSION_INDEX_HTML" file="index.html" type="BINDATA" />
         <include name="IDR_TELEMETRY_EXTENSION_ICON_96" file="app_icon_96.png" type="BINDATA" />
-        <include name="IDR_TELEMETRY_EXTENSION_TRUSTED_SCRIPTS_JS" file="trusted_scripts.js" flattenhtml="true" type="BINDATA" compress="gzip" />
-        <include name="IDR_TELEMETRY_EXTENSION_DIAGNOSTICS_SERVICE_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/telemetry_extension_ui/mojom/diagnostics_service.mojom-lite.js" compress="gzip" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_TELEMETRY_EXTENSION_PROBE_SERVICE_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/telemetry_extension_ui/mojom/probe_service.mojom-lite.js" compress="gzip" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_TELEMETRY_EXTENSION_SYSTEM_EVENTS_SERVICE_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/telemetry_extension_ui/mojom/system_events_service.mojom-lite.js" compress="gzip" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_TELEMETRY_EXTENSION_TRUSTED_SCRIPTS_JS" file="trusted_scripts.js" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_TELEMETRY_EXTENSION_DIAGNOSTICS_SERVICE_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/telemetry_extension_ui/mojom/diagnostics_service.mojom-lite.js" resource_path="diagnostics_service.mojom-lite.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_TELEMETRY_EXTENSION_PROBE_SERVICE_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/telemetry_extension_ui/mojom/probe_service.mojom-lite.js" resource_path="probe_service.mojom-lite.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_TELEMETRY_EXTENSION_SYSTEM_EVENTS_SERVICE_MOJO_LITE_JS" file="${root_gen_dir}/chromeos/components/telemetry_extension_ui/mojom/system_events_service.mojom-lite.js" resource_path= "system_events_service.mojom-lite.js" use_base_dir="false" type="BINDATA" />
 
         <!-- Untrusted app host contents. -->
-        <include name="IDR_TELEMETRY_EXTENSION_DPSL_JS" file="dpsl.js" flattenhtml="true" type="BINDATA" compress="gzip" />
+        <include name="IDR_TELEMETRY_EXTENSION_DPSL_JS" file="dpsl.js" flattenhtml="true" type="BINDATA" />
       </if>
     </includes>
   </release>
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 8f69df077..2d86e2a 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -745,6 +745,10 @@
   return base::FeatureList::IsEnabled(kBetterUpdateScreen);
 }
 
+bool IsCellularActivationUiEnabled() {
+  return base::FeatureList::IsEnabled(kUpdatedCellularActivationUi);
+}
+
 bool IsChildSpecificSigninEnabled() {
   return base::FeatureList::IsEnabled(kChildSpecificSignin);
 }
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index f9bd026..5eb5e43 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -335,6 +335,7 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsAmbientModeDevUseProdEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsAssistantEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsBetterUpdateEnabled();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsCellularActivationUiEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsChildSpecificSigninEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsDeepLinkingEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsDiagnosticsAppEnabled();
diff --git a/chromeos/crosapi/mojom/account_manager.mojom b/chromeos/crosapi/mojom/account_manager.mojom
index e048f5e..e3311657 100644
--- a/chromeos/crosapi/mojom/account_manager.mojom
+++ b/chromeos/crosapi/mojom/account_manager.mojom
@@ -76,6 +76,26 @@
   InvalidGaiaCredentialsReason invalid_gaia_credentials_reason@3;
 };
 
+[Stable]
+struct AccountAdditionResult {
+  // The result of account addition request.
+  // Next value: 5.
+  [Stable, Extensible]
+  enum Status {
+    kSuccess = 0,
+    kAlreadyInProgress = 1,
+    kCancelledByUser = 2,
+    kNetworkError = 3,
+    kUnexpectedResponse = 4,
+  };
+
+  Status status@0;
+  // The account that was added.
+  Account? account@1;
+  // The error is set only if `status` is set to `kNetworkError`.
+  GoogleServiceAuthError? error@2;
+};
+
 // Interface for observers of Chrome OS Account Manager.
 // This interface is implemented by lacros-chrome, and is used by ash-chrome to
 // send account update notifications.
@@ -105,8 +125,8 @@
 // ARC++ uses components/arc/mojom/auth.mojom to talk to the Chrome OS Account
 // Manager.
 //
-// Next version: 3
-// Next method id: 3
+// Next version: 4
+// Next method id: 5
 [Stable, Uuid="85b9a674-9d8e-497f-98d5-22c8dca6f2b8"]
 interface AccountManager {
   // Returns |true| if Chrome OS Account Manager has been fully initialized, and
@@ -127,4 +147,13 @@
   // after the initialization is done).
   [MinVersion = 2]
   GetAccounts@2() => (array<Account> accounts);
+
+  // Launches account addition dialog and returns `result` with the added
+  // account.
+  [MinVersion = 3]
+  ShowAddAccountDialog@3() => (AccountAdditionResult result);
+
+  // Launches account reauthentication dialog for provided `email`.
+  [MinVersion = 3]
+  ShowReauthAccountDialog@4(string email) => ();
 };
diff --git a/components/account_manager_core/BUILD.gn b/components/account_manager_core/BUILD.gn
index 0d3219b..de97c53b 100644
--- a/components/account_manager_core/BUILD.gn
+++ b/components/account_manager_core/BUILD.gn
@@ -11,6 +11,8 @@
   sources = [
     "account.cc",
     "account.h",
+    "account_addition_result.cc",
+    "account_addition_result.h",
     "account_manager_facade.cc",
     "account_manager_facade.h",
     "account_manager_facade_impl.cc",
diff --git a/components/account_manager_core/account_addition_result.cc b/components/account_manager_core/account_addition_result.cc
new file mode 100644
index 0000000..04790a3
--- /dev/null
+++ b/components/account_manager_core/account_addition_result.cc
@@ -0,0 +1,27 @@
+// Copyright 2021 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/account_manager_core/account_addition_result.h"
+
+namespace account_manager {
+
+AccountAdditionResult::AccountAdditionResult(Status status) : status(status) {}
+
+AccountAdditionResult::AccountAdditionResult(Status status, Account account)
+    : status(status), account(account) {
+  DCHECK_EQ(status, Status::kSuccess);
+}
+
+AccountAdditionResult::AccountAdditionResult(Status status,
+                                             GoogleServiceAuthError error)
+    : status(status), error(error) {
+  DCHECK_NE(status, Status::kSuccess);
+}
+
+AccountAdditionResult::AccountAdditionResult(const AccountAdditionResult&) =
+    default;
+
+AccountAdditionResult::~AccountAdditionResult() = default;
+
+}  // namespace account_manager
diff --git a/components/account_manager_core/account_addition_result.h b/components/account_manager_core/account_addition_result.h
new file mode 100644
index 0000000..43761da
--- /dev/null
+++ b/components/account_manager_core/account_addition_result.h
@@ -0,0 +1,44 @@
+// Copyright 2021 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_ACCOUNT_MANAGER_CORE_ACCOUNT_ADDITION_RESULT_H_
+#define COMPONENTS_ACCOUNT_MANAGER_CORE_ACCOUNT_ADDITION_RESULT_H_
+
+#include "base/optional.h"
+#include "components/account_manager_core/account.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+
+namespace account_manager {
+
+// The result of account addition request.
+struct COMPONENT_EXPORT(ACCOUNT_MANAGER_CORE) AccountAdditionResult {
+  enum class Status {
+    // The account was added successfully.
+    kSuccess = 0,
+    // The dialog is already open.
+    kAlreadyInProgress = 1,
+    // User closed the dialog.
+    kCancelledByUser = 2,
+    // Network error.
+    kNetworkError = 3,
+    // Unexpected response (couldn't parse mojo struct).
+    kUnexpectedResponse = 4,
+  };
+
+  Status status;
+  // The account that was added.
+  base::Optional<Account> account;
+  // The error is set only if `status` is set to `kNetworkError`.
+  base::Optional<GoogleServiceAuthError> error;
+
+  explicit AccountAdditionResult(Status status);
+  AccountAdditionResult(Status status, Account account);
+  AccountAdditionResult(Status status, GoogleServiceAuthError error);
+  AccountAdditionResult(const AccountAdditionResult&);
+  ~AccountAdditionResult();
+};
+
+}  // namespace account_manager
+
+#endif  // COMPONENTS_ACCOUNT_MANAGER_CORE_ACCOUNT_ADDITION_RESULT_H_
diff --git a/components/account_manager_core/account_manager_facade.cc b/components/account_manager_core/account_manager_facade.cc
index bc33195..174855e 100644
--- a/components/account_manager_core/account_manager_facade.cc
+++ b/components/account_manager_core/account_manager_facade.cc
@@ -6,17 +6,6 @@
 
 namespace account_manager {
 
-AccountManagerFacade::AccountAdditionResult::AccountAdditionResult() = default;
-AccountManagerFacade::AccountAdditionResult::AccountAdditionResult(
-    Status status,
-    AccountKey account)
-    : status(status), account(account) {}
-AccountManagerFacade::AccountAdditionResult::AccountAdditionResult(
-    Status status,
-    GoogleServiceAuthError error)
-    : status(status), error(error) {}
-AccountManagerFacade::AccountAdditionResult::~AccountAdditionResult() = default;
-
 AccountManagerFacade::Observer::Observer() = default;
 AccountManagerFacade::Observer::~Observer() = default;
 
diff --git a/components/account_manager_core/account_manager_facade.h b/components/account_manager_core/account_manager_facade.h
index 81f6b4a..c3a44a0 100644
--- a/components/account_manager_core/account_manager_facade.h
+++ b/components/account_manager_core/account_manager_facade.h
@@ -11,7 +11,7 @@
 #include "base/component_export.h"
 #include "base/observer_list_types.h"
 #include "components/account_manager_core/account.h"
-#include "google_apis/gaia/google_service_auth_error.h"
+#include "components/account_manager_core/account_addition_result.h"
 
 namespace account_manager {
 
@@ -62,31 +62,6 @@
     kMaxValue = kOnboarding
   };
 
-  // The result of account addition request.
-  struct AccountAdditionResult {
-    enum class Status : int {
-      // The account was added successfully.
-      kSuccess = 0,
-      // The dialog is already open.
-      kAlreadyInProgress = 1,
-      // User closed the dialog.
-      kCancelledByUser = 2,
-      // Network error.
-      kNetworkError = 3,
-    };
-
-    Status status;
-    // The account that was added.
-    base::Optional<AccountKey> account;
-    // The error is set only if `status` is set to `kNetworkError`.
-    base::Optional<GoogleServiceAuthError> error;
-
-    AccountAdditionResult();
-    AccountAdditionResult(Status status, AccountKey account);
-    AccountAdditionResult(Status status, GoogleServiceAuthError error);
-    ~AccountAdditionResult();
-  };
-
   AccountManagerFacade();
   AccountManagerFacade(const AccountManagerFacade&) = delete;
   AccountManagerFacade& operator=(const AccountManagerFacade&) = delete;
diff --git a/components/account_manager_core/account_manager_facade_impl.cc b/components/account_manager_core/account_manager_facade_impl.cc
index a7e0e827..f941cce 100644
--- a/components/account_manager_core/account_manager_facade_impl.cc
+++ b/components/account_manager_core/account_manager_facade_impl.cc
@@ -8,7 +8,10 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/logging.h"
+#include "base/optional.h"
+#include "components/account_manager_core/account_addition_result.h"
 #include "components/account_manager_core/account_manager_util.h"
 
 namespace {
@@ -16,6 +19,9 @@
 // Interface versions in //chromeos/crosapi/mojom/account_manager.mojom:
 // MinVersion of crosapi::mojom::AccountManager::GetAccounts
 constexpr uint32_t kMinVersionWithGetAccounts = 2;
+// MinVersion of crosapi::mojom::AccountManager::ShowAddAccountDialog and
+// crosapi::mojom::AccountManager::ShowReauthAccountDialog.
+constexpr uint32_t kMinVersionWithShowAddAccountDialog = 3;
 
 void UnmarshalAccounts(
     base::OnceCallback<void(const std::vector<account_manager::Account>&)>
@@ -89,14 +95,36 @@
 
 void AccountManagerFacadeImpl::ShowAddAccountDialog(
     const AccountAdditionSource& source,
-    base::OnceCallback<void(const AccountAdditionResult& result)> callback) {
-  // TODO(crbug.com/1140469): implement this.
+    base::OnceCallback<
+        void(const account_manager::AccountAdditionResult& result)> callback) {
+  if (remote_version_ < kMinVersionWithShowAddAccountDialog) {
+    LOG(WARNING) << "Found remote at: " << remote_version_
+                 << ", expected: " << kMinVersionWithShowAddAccountDialog
+                 << " for ShowAddAccountDialog.";
+    std::move(callback).Run(account_manager::AccountAdditionResult(
+        account_manager::AccountAdditionResult::Status::kUnexpectedResponse));
+    return;
+  }
+
+  // TODO(crbug.com/1140469): send UMA stats.
+
+  account_manager_remote_->ShowAddAccountDialog(
+      base::BindOnce(&AccountManagerFacadeImpl::OnShowAddAccountDialogFinished,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void AccountManagerFacadeImpl::ShowReauthAccountDialog(
     const AccountAdditionSource& source,
     const std::string& email) {
-  // TODO(crbug.com/1140469): implement this.
+  if (remote_version_ < kMinVersionWithShowAddAccountDialog) {
+    LOG(WARNING) << "Found remote at: " << remote_version_
+                 << ", expected: " << kMinVersionWithShowAddAccountDialog
+                 << " for ShowReauthAccountDialog.";
+  }
+
+  // TODO(crbug.com/1140469): send UMA stats.
+
+  account_manager_remote_->ShowReauthAccountDialog(email, base::DoNothing());
 }
 
 void AccountManagerFacadeImpl::OnReceiverReceived(
@@ -117,6 +145,20 @@
   FinishInitSequenceIfNotAlreadyFinished();
 }
 
+void AccountManagerFacadeImpl::OnShowAddAccountDialogFinished(
+    base::OnceCallback<
+        void(const account_manager::AccountAdditionResult& result)> callback,
+    crosapi::mojom::AccountAdditionResultPtr mojo_result) {
+  base::Optional<account_manager::AccountAdditionResult> result =
+      account_manager::FromMojoAccountAdditionResult(mojo_result);
+  if (!result.has_value()) {
+    std::move(callback).Run(account_manager::AccountAdditionResult(
+        account_manager::AccountAdditionResult::Status::kUnexpectedResponse));
+    return;
+  }
+  std::move(callback).Run(result.value());
+}
+
 void AccountManagerFacadeImpl::OnTokenUpserted(
     crosapi::mojom::AccountPtr account) {
   is_remote_initialized_ = true;
@@ -176,3 +218,7 @@
     std::move(closure).Run();
   }
 }
+
+void AccountManagerFacadeImpl::FlushMojoForTesting() {
+  account_manager_remote_.FlushForTesting();
+}
diff --git a/components/account_manager_core/account_manager_facade_impl.h b/components/account_manager_core/account_manager_facade_impl.h
index bd06726..b3d7d45 100644
--- a/components/account_manager_core/account_manager_facade_impl.h
+++ b/components/account_manager_core/account_manager_facade_impl.h
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "chromeos/crosapi/mojom/account_manager.mojom.h"
+#include "components/account_manager_core/account_addition_result.h"
 #include "components/account_manager_core/account_manager_facade.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -43,8 +44,8 @@
           callback) override;
   void ShowAddAccountDialog(
       const AccountAdditionSource& source,
-      base::OnceCallback<void(const AccountAdditionResult& result)> callback)
-      override;
+      base::OnceCallback<void(const account_manager::AccountAdditionResult&
+                                  result)> callback) override;
   void ShowReauthAccountDialog(const AccountAdditionSource& source,
                                const std::string& email) override;
 
@@ -53,9 +54,21 @@
   void OnAccountRemoved(crosapi::mojom::AccountPtr account) override;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(AccountManagerFacadeImplTest,
+                           ShowAddAccountDialogCallsMojo);
+  FRIEND_TEST_ALL_PREFIXES(AccountManagerFacadeImplTest,
+                           ShowReauthAccountDialogCallsMojo);
+
   void OnReceiverReceived(
       mojo::PendingReceiver<AccountManagerObserver> receiver);
   void OnInitialized(bool is_initialized);
+  // Callback for `crosapi::mojom::AccountManager::ShowAddAccountDialog`.
+  void OnShowAddAccountDialogFinished(
+      base::OnceCallback<
+          void(const account_manager::AccountAdditionResult& result)> callback,
+      crosapi::mojom::AccountAdditionResultPtr mojo_result);
+
+  void FlushMojoForTesting();
 
   void GetAccountsInternal(
       base::OnceCallback<void(const std::vector<account_manager::Account>&)>
diff --git a/components/account_manager_core/account_manager_facade_impl_unittest.cc b/components/account_manager_core/account_manager_facade_impl_unittest.cc
index 25ac2e6..3f24813 100644
--- a/components/account_manager_core/account_manager_facade_impl_unittest.cc
+++ b/components/account_manager_core/account_manager_facade_impl_unittest.cc
@@ -12,6 +12,8 @@
 #include "base/test/task_environment.h"
 #include "chromeos/crosapi/mojom/account_manager.mojom.h"
 #include "components/account_manager_core/account.h"
+#include "components/account_manager_core/account_addition_result.h"
+#include "components/account_manager_core/account_manager_facade.h"
 #include "components/account_manager_core/account_manager_test_util.h"
 #include "components/account_manager_core/account_manager_util.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -23,6 +25,8 @@
 
 namespace {
 
+const char kFakeEmail[] = "fake_email@example.com";
+
 class FakeAccountManager : public crosapi::mojom::AccountManager {
  public:
   FakeAccountManager() = default;
@@ -52,6 +56,19 @@
     std::move(callback).Run(std::move(mojo_accounts));
   }
 
+  void ShowAddAccountDialog(ShowAddAccountDialogCallback callback) override {
+    show_add_account_dialog_calls_++;
+    std::move(callback).Run(account_manager::ToMojoAccountAdditionResult(
+        account_manager::AccountAdditionResult(
+            account_manager::AccountAdditionResult::Status::kCancelledByUser)));
+  }
+
+  void ShowReauthAccountDialog(const std::string& email,
+                               base::OnceClosure closure) override {
+    show_reauth_account_dialog_calls_++;
+    std::move(closure).Run();
+  }
+
   mojo::Remote<crosapi::mojom::AccountManager> CreateRemote() {
     mojo::Remote<crosapi::mojom::AccountManager> remote;
     receivers_.Add(this, remote.BindNewPipeAndPassReceiver());
@@ -75,7 +92,15 @@
     accounts_ = accounts;
   }
 
+  int show_add_account_dialog_calls() { return show_add_account_dialog_calls_; }
+
+  int show_reauth_account_dialog_calls() {
+    return show_reauth_account_dialog_calls_;
+  }
+
  private:
+  int show_add_account_dialog_calls_ = 0;
+  int show_reauth_account_dialog_calls_ = 0;
   bool is_initialized_{false};
   std::vector<account_manager::Account> accounts_;
   mojo::ReceiverSet<crosapi::mojom::AccountManager> receivers_;
@@ -272,3 +297,28 @@
   account_manager_facade->GetAccounts(callback.Get());
   run_loop.Run();
 }
+
+TEST_F(AccountManagerFacadeImplTest, ShowAddAccountDialogCallsMojo) {
+  std::unique_ptr<AccountManagerFacadeImpl> account_manager_facade =
+      CreateFacade();
+  EXPECT_EQ(0, account_manager().show_add_account_dialog_calls());
+  account_manager_facade->ShowAddAccountDialog(
+      account_manager::AccountManagerFacade::AccountAdditionSource::
+          kSettingsAddAccountButton,
+      base::BindOnce(
+          [](const account_manager::AccountAdditionResult& result) {}));
+  account_manager_facade->FlushMojoForTesting();
+  EXPECT_EQ(1, account_manager().show_add_account_dialog_calls());
+}
+
+TEST_F(AccountManagerFacadeImplTest, ShowReauthAccountDialogCallsMojo) {
+  std::unique_ptr<AccountManagerFacadeImpl> account_manager_facade =
+      CreateFacade();
+  EXPECT_EQ(0, account_manager().show_reauth_account_dialog_calls());
+  account_manager_facade->ShowReauthAccountDialog(
+      account_manager::AccountManagerFacade::AccountAdditionSource::
+          kSettingsAddAccountButton,
+      kFakeEmail);
+  account_manager_facade->FlushMojoForTesting();
+  EXPECT_EQ(1, account_manager().show_reauth_account_dialog_calls());
+}
diff --git a/components/account_manager_core/account_manager_util.cc b/components/account_manager_core/account_manager_util.cc
index 29b506d..4b477c5 100644
--- a/components/account_manager_core/account_manager_util.cc
+++ b/components/account_manager_core/account_manager_util.cc
@@ -5,6 +5,7 @@
 #include "components/account_manager_core/account_manager_util.h"
 
 #include "components/account_manager_core/account.h"
+#include "components/account_manager_core/account_addition_result.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 
 namespace account_manager {
@@ -90,6 +91,41 @@
   }
 }
 
+base::Optional<account_manager::AccountAdditionResult::Status>
+FromMojoAccountAdditionStatus(
+    crosapi::mojom::AccountAdditionResult::Status mojo_status) {
+  switch (mojo_status) {
+    case cm::AccountAdditionResult::Status::kSuccess:
+      return account_manager::AccountAdditionResult::Status::kSuccess;
+    case cm::AccountAdditionResult::Status::kAlreadyInProgress:
+      return account_manager::AccountAdditionResult::Status::kAlreadyInProgress;
+    case cm::AccountAdditionResult::Status::kCancelledByUser:
+      return account_manager::AccountAdditionResult::Status::kCancelledByUser;
+    case cm::AccountAdditionResult::Status::kNetworkError:
+      return account_manager::AccountAdditionResult::Status::kNetworkError;
+    default:
+      LOG(WARNING) << "Unknown crosapi::mojom::AccountAdditionResult::Status: "
+                   << mojo_status;
+      return base::nullopt;
+  }
+}
+
+crosapi::mojom::AccountAdditionResult::Status ToMojoAccountAdditionStatus(
+    account_manager::AccountAdditionResult::Status status) {
+  switch (status) {
+    case account_manager::AccountAdditionResult::Status::kSuccess:
+      return cm::AccountAdditionResult::Status::kSuccess;
+    case account_manager::AccountAdditionResult::Status::kAlreadyInProgress:
+      return cm::AccountAdditionResult::Status::kAlreadyInProgress;
+    case account_manager::AccountAdditionResult::Status::kCancelledByUser:
+      return cm::AccountAdditionResult::Status::kCancelledByUser;
+    case account_manager::AccountAdditionResult::Status::kNetworkError:
+      return cm::AccountAdditionResult::Status::kNetworkError;
+    case account_manager::AccountAdditionResult::Status::kUnexpectedResponse:
+      return cm::AccountAdditionResult::Status::kUnexpectedResponse;
+  }
+}
+
 }  // namespace
 
 base::Optional<account_manager::Account> FromMojoAccount(
@@ -219,4 +255,38 @@
   return mojo_result;
 }
 
+base::Optional<account_manager::AccountAdditionResult>
+FromMojoAccountAdditionResult(
+    const crosapi::mojom::AccountAdditionResultPtr& mojo_result) {
+  base::Optional<account_manager::AccountAdditionResult::Status> status =
+      FromMojoAccountAdditionStatus(mojo_result->status);
+  if (!status.has_value()) {
+    return base::nullopt;
+  }
+  account_manager::AccountAdditionResult result(status.value());
+  result.status = status.value();
+  if (mojo_result->account) {
+    result.account = FromMojoAccount(mojo_result->account);
+  }
+  if (mojo_result->error) {
+    result.error = FromMojoGoogleServiceAuthError(mojo_result->error);
+  }
+  return result;
+}
+
+crosapi::mojom::AccountAdditionResultPtr ToMojoAccountAdditionResult(
+    account_manager::AccountAdditionResult result) {
+  crosapi::mojom::AccountAdditionResultPtr mojo_result =
+      crosapi::mojom::AccountAdditionResult::New();
+  mojo_result->status = ToMojoAccountAdditionStatus(result.status);
+  if (result.account.has_value()) {
+    mojo_result->account =
+        account_manager::ToMojoAccount(result.account.value());
+  }
+  if (result.error.has_value()) {
+    mojo_result->error = ToMojoGoogleServiceAuthError(result.error.value());
+  }
+  return mojo_result;
+}
+
 }  // namespace account_manager
diff --git a/components/account_manager_core/account_manager_util.h b/components/account_manager_core/account_manager_util.h
index 3e0eb0b..ce20307 100644
--- a/components/account_manager_core/account_manager_util.h
+++ b/components/account_manager_core/account_manager_util.h
@@ -8,6 +8,7 @@
 #include "base/optional.h"
 #include "chromeos/crosapi/mojom/account_manager.mojom.h"
 #include "components/account_manager_core/account.h"
+#include "components/account_manager_core/account_addition_result.h"
 
 class GoogleServiceAuthError;
 
@@ -51,6 +52,14 @@
 crosapi::mojom::GoogleServiceAuthErrorPtr ToMojoGoogleServiceAuthError(
     GoogleServiceAuthError error);
 
+COMPONENT_EXPORT(ACCOUNT_MANAGER_CORE)
+base::Optional<account_manager::AccountAdditionResult>
+FromMojoAccountAdditionResult(
+    const crosapi::mojom::AccountAdditionResultPtr& mojo_result);
+
+COMPONENT_EXPORT(ACCOUNT_MANAGER_CORE)
+crosapi::mojom::AccountAdditionResultPtr ToMojoAccountAdditionResult(
+    account_manager::AccountAdditionResult result);
 }  // namespace account_manager
 
 #endif  // COMPONENTS_ACCOUNT_MANAGER_CORE_ACCOUNT_MANAGER_UTIL_H_
diff --git a/components/embedder_support/BUILD.gn b/components/embedder_support/BUILD.gn
index 3010fbe7..8325391 100644
--- a/components/embedder_support/BUILD.gn
+++ b/components/embedder_support/BUILD.gn
@@ -11,20 +11,42 @@
   ]
 }
 
-source_set("unit_tests") {
-  testonly = true
-  sources = [ "origin_trials/origin_trial_policy_impl_unittest.cc" ]
+# Target for general utilities that are only used in the browser process.
+static_library("browser_util") {
+  sources = [
+    "user_agent_utils.cc",
+    "user_agent_utils.h",
+  ]
 
   deps = [
+    ":embedder_support",
+    "//build:branding_buildflags",
+    "//components/version_info",
+    "//content/public/browser",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "origin_trials/origin_trial_policy_impl_unittest.cc",
+    "user_agent_utils_unittest.cc",
+  ]
+
+  deps = [
+    ":browser_util",
     "//base",
     "//base/test:test_support",
     "//components/embedder_support",
     "//components/embedder_support/origin_trials",
+    "//components/version_info:version_info",
+    "//content/public/common",
     "//mojo/core/embedder:embedder",
     "//net:net",
     "//net/traffic_annotation:test_support",
     "//services/network:test_support",
     "//testing/gtest",
+    "//third_party/blink/public/common:headers",
     "//third_party/blink/public/mojom:mojom_platform_headers",
   ]
 
diff --git a/components/embedder_support/DEPS b/components/embedder_support/DEPS
new file mode 100644
index 0000000..c5dabac
--- /dev/null
+++ b/components/embedder_support/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+components/version_info",
+  "+content/public/browser",
+  "+content/public/common",
+  "+net",
+  "+third_party/blink/public/common",
+]
diff --git a/components/embedder_support/android/BUILD.gn b/components/embedder_support/android/BUILD.gn
index c8428393..1cec740e 100644
--- a/components/embedder_support/android/BUILD.gn
+++ b/components/embedder_support/android/BUILD.gn
@@ -70,8 +70,6 @@
     "util/response_delegate_impl.cc",
     "util/response_delegate_impl.h",
     "util/url_utilities.cc",
-    "util/user_agent_utils.cc",
-    "util/user_agent_utils.h",
     "util/web_resource_response.cc",
     "util/web_resource_response.h",
   ]
diff --git a/components/embedder_support/android/util/DEPS b/components/embedder_support/android/util/DEPS
index 7b81183..839bc00 100644
--- a/components/embedder_support/android/util/DEPS
+++ b/components/embedder_support/android/util/DEPS
@@ -1,11 +1,9 @@
 include_rules = [
   "+components/google/core/common",
-  "+components/version_info",
   "+mojo/public/cpp/bindings",
   "+mojo/public/cpp/system",
   "+net",
   "+services/network/public",
-  "+third_party/blink/public/common/user_agent",
   "+third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h",
 ]
 
diff --git a/components/embedder_support/android/util/user_agent_utils.cc b/components/embedder_support/android/util/user_agent_utils.cc
deleted file mode 100644
index 1a15c692..0000000
--- a/components/embedder_support/android/util/user_agent_utils.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2020 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/embedder_support/android/util/user_agent_utils.h"
-
-#include "components/version_info/version_info.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/user_agent.h"
-#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
-
-namespace embedder_support {
-
-void SetDesktopUserAgentOverride(content::WebContents* web_contents,
-                                 const blink::UserAgentMetadata& metadata) {
-  const char kLinuxInfoStr[] = "X11; Linux x86_64";
-  std::string product = version_info::GetProductNameAndVersionForUserAgent();
-
-  blink::UserAgentOverride spoofed_ua;
-  spoofed_ua.ua_string_override =
-      content::BuildUserAgentFromOSAndProduct(kLinuxInfoStr, product);
-  spoofed_ua.ua_metadata_override = metadata;
-  spoofed_ua.ua_metadata_override->platform = "Linux";
-  spoofed_ua.ua_metadata_override->platform_version =
-      std::string();  // match content::GetOSVersion(false) on Linux
-  spoofed_ua.ua_metadata_override->architecture = "x86";
-  spoofed_ua.ua_metadata_override->model = std::string();
-  spoofed_ua.ua_metadata_override->mobile = false;
-
-  web_contents->SetUserAgentOverride(spoofed_ua, false);
-}
-
-}  // namespace embedder_support
diff --git a/components/embedder_support/android/util/user_agent_utils.h b/components/embedder_support/android/util/user_agent_utils.h
deleted file mode 100644
index f2e9144..0000000
--- a/components/embedder_support/android/util/user_agent_utils.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2020 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_EMBEDDER_SUPPORT_ANDROID_UTIL_USER_AGENT_UTILS_H_
-#define COMPONENTS_EMBEDDER_SUPPORT_ANDROID_UTIL_USER_AGENT_UTILS_H_
-
-namespace blink {
-struct UserAgentMetadata;
-}
-
-namespace content {
-class WebContents;
-}
-
-namespace embedder_support {
-
-void SetDesktopUserAgentOverride(content::WebContents* web_contents,
-                                 const blink::UserAgentMetadata& metadata);
-
-}  // namespace embedder_support
-
-#endif  // COMPONENTS_EMBEDDER_SUPPORT_ANDROID_UTIL_USER_AGENT_UTILS_H_
diff --git a/components/embedder_support/switches.cc b/components/embedder_support/switches.cc
index 32b4e5b..4c712ae 100644
--- a/components/embedder_support/switches.cc
+++ b/components/embedder_support/switches.cc
@@ -21,4 +21,7 @@
 // checking origin trial tokens.
 const char kOriginTrialPublicKey[] = "origin-trial-public-key";
 
+// A string used to override the default user agent with a custom one.
+const char kUserAgent[] = "user-agent";
+
 }  // namespace embedder_support
diff --git a/components/embedder_support/switches.h b/components/embedder_support/switches.h
index c58785d..a2cd1d11 100644
--- a/components/embedder_support/switches.h
+++ b/components/embedder_support/switches.h
@@ -15,6 +15,7 @@
 extern const char kOriginTrialDisabledFeatures[];
 extern const char kOriginTrialDisabledTokens[];
 extern const char kOriginTrialPublicKey[];
+extern const char kUserAgent[];
 
 }  // namespace embedder_support
 
diff --git a/components/embedder_support/user_agent_utils.cc b/components/embedder_support/user_agent_utils.cc
new file mode 100644
index 0000000..c516925
--- /dev/null
+++ b/components/embedder_support/user_agent_utils.cc
@@ -0,0 +1,166 @@
+// Copyright 2021 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/embedder_support/user_agent_utils.h"
+
+#include "base/command_line.h"
+#include "base/strings/strcat.h"
+#include "build/branding_buildflags.h"
+#include "components/embedder_support/switches.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/user_agent.h"
+#include "net/http/http_util.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
+
+namespace embedder_support {
+
+std::string GetProduct() {
+  return version_info::GetProductNameAndVersionForUserAgent();
+}
+
+std::string GetUserAgent() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(kUserAgent)) {
+    std::string ua = command_line->GetSwitchValueASCII(kUserAgent);
+    if (net::HttpUtil::IsValidHeaderValue(ua))
+      return ua;
+    LOG(WARNING) << "Ignored invalid value for flag --" << kUserAgent;
+  }
+
+  if (base::FeatureList::IsEnabled(blink::features::kFreezeUserAgent)) {
+    return content::GetFrozenUserAgent(
+        command_line->HasSwitch(switches::kUseMobileUserAgent),
+        version_info::GetMajorVersionNumber());
+  }
+
+  std::string product = GetProduct();
+#if defined(OS_ANDROID)
+  if (command_line->HasSwitch(switches::kUseMobileUserAgent))
+    product += " Mobile";
+#endif
+  return content::BuildUserAgentFromProduct(product);
+}
+
+// Generate a pseudo-random permutation of the following brand/version pairs:
+//   1. The base project (i.e. Chromium)
+//   2. The browser brand, if available
+//   3. A randomized string containing escaped characters to ensure proper
+//      header parsing, along with an arbitrarily low version to ensure proper
+//      version checking.
+blink::UserAgentBrandList GenerateBrandVersionList(
+    int seed,
+    base::Optional<std::string> brand,
+    std::string major_version,
+    base::Optional<std::string> maybe_greasey_brand) {
+  DCHECK_GE(seed, 0);
+  const int npermutations = 6;  // 3!
+  int permutation = seed % npermutations;
+
+  // Pick a stable permutation seeded by major version number. any values here
+  // and in order should be under three.
+  const std::vector<std::vector<int>> orders{{0, 1, 2}, {0, 2, 1}, {1, 0, 2},
+                                             {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
+  const std::vector<int> order = orders[permutation];
+  DCHECK_EQ(6u, orders.size());
+  DCHECK_EQ(3u, order.size());
+
+  // Previous values for indexes 0 and 1 were '\' and '"', temporarily removed
+  // because of compat issues
+  const std::vector<std::string> escaped_chars = {" ", " ", ";"};
+  std::string greasey_brand =
+      base::StrCat({escaped_chars[order[0]], "Not", escaped_chars[order[1]],
+                    "A", escaped_chars[order[2]], "Brand"});
+
+  blink::UserAgentBrandVersion greasey_bv = {
+      maybe_greasey_brand.value_or(greasey_brand), "99"};
+  blink::UserAgentBrandVersion chromium_bv = {"Chromium", major_version};
+
+  blink::UserAgentBrandList greased_brand_version_list(3);
+
+  if (brand) {
+    blink::UserAgentBrandVersion brand_bv = {brand.value(), major_version};
+
+    greased_brand_version_list[order[0]] = greasey_bv;
+    greased_brand_version_list[order[1]] = chromium_bv;
+    greased_brand_version_list[order[2]] = brand_bv;
+  } else {
+    greased_brand_version_list[seed % 2] = greasey_bv;
+    greased_brand_version_list[(seed + 1) % 2] = chromium_bv;
+
+    // If left, the last element would make a blank "" at the end of the header.
+    greased_brand_version_list.pop_back();
+  }
+
+  return greased_brand_version_list;
+}
+
+const blink::UserAgentBrandList& GetBrandVersionList() {
+  static const base::NoDestructor<blink::UserAgentBrandList>
+      greased_brand_version_list([] {
+        int major_version_number;
+        std::string major_version = version_info::GetMajorVersionNumber();
+        base::StringToInt(major_version, &major_version_number);
+        base::Optional<std::string> brand;
+#if !BUILDFLAG(CHROMIUM_BRANDING)
+        brand = version_info::GetProductName();
+#endif
+        base::Optional<std::string> maybe_param_override =
+            base::GetFieldTrialParamValueByFeature(features::kGreaseUACH,
+                                                   "brand_override");
+        if (maybe_param_override->empty())
+          maybe_param_override = base::nullopt;
+
+        return GenerateBrandVersionList(major_version_number, brand,
+                                        major_version, maybe_param_override);
+      }());
+  return *greased_brand_version_list;
+}
+
+blink::UserAgentMetadata GetUserAgentMetadata() {
+  blink::UserAgentMetadata metadata;
+
+  metadata.brand_version_list = GetBrandVersionList();
+  metadata.full_version = version_info::GetVersionNumber();
+  metadata.platform = version_info::GetOSType();
+  metadata.platform_version =
+      content::GetOSVersion(content::IncludeAndroidBuildNumber::Exclude,
+                            content::IncludeAndroidModel::Exclude);
+  metadata.architecture = content::GetLowEntropyCpuArchitecture();
+  metadata.model = content::BuildModelInfo();
+
+  metadata.mobile = false;
+#if defined(OS_ANDROID)
+  metadata.mobile = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kUseMobileUserAgent);
+#endif
+
+  return metadata;
+}
+
+#if defined(OS_ANDROID)
+void SetDesktopUserAgentOverride(content::WebContents* web_contents,
+                                 const blink::UserAgentMetadata& metadata) {
+  const char kLinuxInfoStr[] = "X11; Linux x86_64";
+  std::string product = version_info::GetProductNameAndVersionForUserAgent();
+
+  blink::UserAgentOverride spoofed_ua;
+  spoofed_ua.ua_string_override =
+      content::BuildUserAgentFromOSAndProduct(kLinuxInfoStr, product);
+  spoofed_ua.ua_metadata_override = metadata;
+  spoofed_ua.ua_metadata_override->platform = "Linux";
+  spoofed_ua.ua_metadata_override->platform_version =
+      std::string();  // match content::GetOSVersion(false) on Linux
+  spoofed_ua.ua_metadata_override->architecture = "x86";
+  spoofed_ua.ua_metadata_override->model = std::string();
+  spoofed_ua.ua_metadata_override->mobile = false;
+
+  web_contents->SetUserAgentOverride(spoofed_ua, false);
+}
+#endif  // OS_ANDROID
+
+}  // namespace embedder_support
diff --git a/components/embedder_support/user_agent_utils.h b/components/embedder_support/user_agent_utils.h
new file mode 100644
index 0000000..a6306c7
--- /dev/null
+++ b/components/embedder_support/user_agent_utils.h
@@ -0,0 +1,45 @@
+// Copyright 2021 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_EMBEDDER_SUPPORT_USER_AGENT_UTILS_H_
+#define COMPONENTS_EMBEDDER_SUPPORT_USER_AGENT_UTILS_H_
+
+#include <string>
+
+#include "build/build_config.h"
+#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
+
+namespace blink {
+struct UserAgentMetadata;
+}
+
+namespace content {
+class WebContents;
+}
+
+namespace embedder_support {
+
+// Returns the product used in building the user-agent.
+std::string GetProduct();
+
+// Returns the user agent string for Chrome.
+std::string GetUserAgent();
+
+blink::UserAgentMetadata GetUserAgentMetadata();
+
+blink::UserAgentBrandList GenerateBrandVersionList(
+    int seed,
+    base::Optional<std::string> brand,
+    std::string major_version,
+    base::Optional<std::string> maybe_greasey_brand);
+
+#if defined(OS_ANDROID)
+// This sets a user agent string to simulate a desktop user agent on mobile.
+void SetDesktopUserAgentOverride(content::WebContents* web_contents,
+                                 const blink::UserAgentMetadata& metadata);
+#endif
+
+}  // namespace embedder_support
+
+#endif  // COMPONENTS_EMBEDDER_SUPPORT_USER_AGENT_UTILS_H_
diff --git a/components/embedder_support/user_agent_utils_unittest.cc b/components/embedder_support/user_agent_utils_unittest.cc
new file mode 100644
index 0000000..d7b3d90
--- /dev/null
+++ b/components/embedder_support/user_agent_utils_unittest.cc
@@ -0,0 +1,326 @@
+// Copyright 2021 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/embedder_support/user_agent_utils.h"
+
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/system/sys_info.h"
+#include "base/test/gtest_util.h"
+#include "base/test/scoped_command_line.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "components/version_info/version_info.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/user_agent.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+
+#if defined(USE_X11) || defined(USE_OZONE)
+#include <sys/utsname.h>
+#endif
+
+namespace embedder_support {
+
+namespace {
+
+void CheckUserAgentStringOrdering(bool mobile_device) {
+  std::vector<std::string> pieces;
+
+  // Check if the pieces of the user agent string come in the correct order.
+  std::string buffer = GetUserAgent();
+
+  pieces = base::SplitStringUsingSubstr(
+      buffer, "Mozilla/5.0 (", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  ASSERT_EQ(2u, pieces.size());
+  buffer = pieces[1];
+  EXPECT_EQ("", pieces[0]);
+
+  pieces = base::SplitStringUsingSubstr(
+      buffer, ") AppleWebKit/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  ASSERT_EQ(2u, pieces.size());
+  buffer = pieces[1];
+  std::string os_str = pieces[0];
+
+  pieces =
+      base::SplitStringUsingSubstr(buffer, " (KHTML, like Gecko) ",
+                                   base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  ASSERT_EQ(2u, pieces.size());
+  buffer = pieces[1];
+  std::string webkit_version_str = pieces[0];
+
+  pieces = base::SplitStringUsingSubstr(
+      buffer, " Safari/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  ASSERT_EQ(2u, pieces.size());
+  std::string product_str = pieces[0];
+  std::string safari_version_str = pieces[1];
+
+  EXPECT_FALSE(os_str.empty());
+
+  pieces = base::SplitStringUsingSubstr(os_str, "; ", base::KEEP_WHITESPACE,
+                                        base::SPLIT_WANT_ALL);
+#if defined(OS_WIN)
+  // Windows NT 10.0; Win64; x64
+  // Windows NT 10.0; WOW64
+  // Windows NT 10.0
+  std::string os_and_version = pieces[0];
+  for (unsigned int i = 1; i < pieces.size(); ++i) {
+    bool equals = ((pieces[i] == "WOW64") || (pieces[i] == "Win64") ||
+                   pieces[i] == "x64");
+    ASSERT_TRUE(equals);
+  }
+  pieces = base::SplitStringUsingSubstr(pieces[0], " ", base::KEEP_WHITESPACE,
+                                        base::SPLIT_WANT_ALL);
+  ASSERT_EQ(3u, pieces.size());
+  ASSERT_EQ("Windows", pieces[0]);
+  ASSERT_EQ("NT", pieces[1]);
+  double version;
+  ASSERT_TRUE(base::StringToDouble(pieces[2], &version));
+  ASSERT_LE(4.0, version);
+  ASSERT_GT(11.0, version);
+#elif defined(OS_MAC)
+  // Macintosh; Intel Mac OS X 10_15_4
+  ASSERT_EQ(2u, pieces.size());
+  ASSERT_EQ("Macintosh", pieces[0]);
+  pieces = base::SplitStringUsingSubstr(pieces[1], " ", base::KEEP_WHITESPACE,
+                                        base::SPLIT_WANT_ALL);
+  ASSERT_EQ(5u, pieces.size());
+  ASSERT_EQ("Intel", pieces[0]);
+  ASSERT_EQ("Mac", pieces[1]);
+  ASSERT_EQ("OS", pieces[2]);
+  ASSERT_EQ("X", pieces[3]);
+  pieces = base::SplitStringUsingSubstr(pieces[4], "_", base::KEEP_WHITESPACE,
+                                        base::SPLIT_WANT_ALL);
+  {
+    int major, minor, patch;
+    base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &patch);
+    ASSERT_EQ(base::StringPrintf("%d", major), pieces[0]);
+  }
+  int value;
+  ASSERT_TRUE(base::StringToInt(pieces[1], &value));
+  ASSERT_LE(0, value);
+  ASSERT_TRUE(base::StringToInt(pieces[2], &value));
+  ASSERT_LE(0, value);
+#elif defined(USE_X11) || defined(USE_OZONE)
+  // X11; Linux x86_64
+  // X11; CrOS armv7l 4537.56.0
+  struct utsname unixinfo;
+  uname(&unixinfo);
+  std::string machine = unixinfo.machine;
+  if (strcmp(unixinfo.machine, "x86_64") == 0 &&
+      sizeof(void*) == sizeof(int32_t)) {
+    machine = "i686 (x86_64)";
+  }
+  ASSERT_EQ(2u, pieces.size());
+  ASSERT_EQ("X11", pieces[0]);
+  pieces = base::SplitStringUsingSubstr(pieces[1], " ", base::KEEP_WHITESPACE,
+                                        base::SPLIT_WANT_ALL);
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  // X11; CrOS armv7l 4537.56.0
+  //      ^^
+  ASSERT_EQ(3u, pieces.size());
+  ASSERT_EQ("CrOS", pieces[0]);
+  ASSERT_EQ(machine, pieces[1]);
+  pieces = base::SplitStringUsingSubstr(pieces[2], ".", base::KEEP_WHITESPACE,
+                                        base::SPLIT_WANT_ALL);
+  for (unsigned int i = 1; i < pieces.size(); ++i) {
+    int value;
+    ASSERT_TRUE(base::StringToInt(pieces[i], &value));
+  }
+#else
+  // X11; Linux x86_64
+  //      ^^
+  ASSERT_EQ(2u, pieces.size());
+  // This may not be Linux in all cases in the wild, but it is on the bots.
+  ASSERT_EQ("Linux", pieces[0]);
+  ASSERT_EQ(machine, pieces[1]);
+#endif
+#elif defined(OS_ANDROID)
+  // Linux; Android 7.1.1; Samsung Chromebook 3
+  ASSERT_GE(3u, pieces.size());
+  ASSERT_EQ("Linux", pieces[0]);
+  std::string model;
+  if (pieces.size() > 2)
+    model = pieces[2];
+
+  pieces = base::SplitStringUsingSubstr(pieces[1], " ", base::KEEP_WHITESPACE,
+                                        base::SPLIT_WANT_ALL);
+  ASSERT_EQ(2u, pieces.size());
+  ASSERT_EQ("Android", pieces[0]);
+  pieces = base::SplitStringUsingSubstr(pieces[1], ".", base::KEEP_WHITESPACE,
+                                        base::SPLIT_WANT_ALL);
+  for (unsigned int i = 1; i < pieces.size(); ++i) {
+    int value;
+    ASSERT_TRUE(base::StringToInt(pieces[i], &value));
+  }
+
+  if (!model.empty()) {
+    if (base::SysInfo::GetAndroidBuildCodename() == "REL")
+      ASSERT_EQ(base::SysInfo::HardwareModelName(), model);
+    else
+      ASSERT_EQ("", model);
+  }
+#elif defined(OS_FUCHSIA)
+  // X11; Fuchsia
+  ASSERT_EQ(2u, pieces.size());
+  ASSERT_EQ("X11", pieces[0]);
+  ASSERT_EQ("Fuchsia", pieces[1]);
+#endif
+
+  // Check that the version numbers match.
+  EXPECT_FALSE(webkit_version_str.empty());
+  EXPECT_FALSE(safari_version_str.empty());
+  EXPECT_EQ(webkit_version_str, safari_version_str);
+
+  EXPECT_TRUE(
+      base::StartsWith(product_str, "Chrome/", base::CompareCase::SENSITIVE));
+  if (mobile_device) {
+    // "Mobile" gets tacked on to the end for mobile devices, like phones.
+    EXPECT_TRUE(
+        base::EndsWith(product_str, " Mobile", base::CompareCase::SENSITIVE));
+  }
+}
+
+}  // namespace
+
+TEST(UserAgentUtilsTest, UserAgentStringOrdering) {
+#if defined(OS_ANDROID)
+  const char* const kArguments[] = {"chrome"};
+  base::test::ScopedCommandLine scoped_command_line;
+  base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
+  command_line->InitFromArgv(1, kArguments);
+
+  // Do it for regular devices.
+  ASSERT_FALSE(command_line->HasSwitch(switches::kUseMobileUserAgent));
+  CheckUserAgentStringOrdering(false);
+
+  // Do it for mobile devices.
+  command_line->AppendSwitch(switches::kUseMobileUserAgent);
+  ASSERT_TRUE(command_line->HasSwitch(switches::kUseMobileUserAgent));
+  CheckUserAgentStringOrdering(true);
+#else
+  CheckUserAgentStringOrdering(false);
+#endif
+}
+
+TEST(UserAgentUtilsTest, UserAgentStringFrozen) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(blink::features::kFreezeUserAgent);
+
+#if defined(OS_ANDROID)
+  // Verify the correct user agent is returned when the UseMobileUserAgent
+  // command line flag is present.
+  const char* const kArguments[] = {"chrome"};
+  base::test::ScopedCommandLine scoped_command_line;
+  base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();
+  command_line->InitFromArgv(1, kArguments);
+
+  // Verify the mobile user agent string is not returned when not using a mobile
+  // user agent.
+  ASSERT_FALSE(command_line->HasSwitch(switches::kUseMobileUserAgent));
+  {
+    std::string buffer = GetUserAgent();
+    EXPECT_EQ(buffer, base::StringPrintf(
+                          content::frozen_user_agent_strings::kAndroid,
+                          version_info::GetMajorVersionNumber().c_str()));
+  }
+
+  // Verify the mobile user agent string is returned when using a mobile user
+  // agent.
+  command_line->AppendSwitch(switches::kUseMobileUserAgent);
+  ASSERT_TRUE(command_line->HasSwitch(switches::kUseMobileUserAgent));
+  {
+    std::string buffer = GetUserAgent();
+    EXPECT_EQ(buffer, base::StringPrintf(
+                          content::frozen_user_agent_strings::kAndroidMobile,
+                          version_info::GetMajorVersionNumber().c_str()));
+  }
+#else
+  {
+    std::string buffer = GetUserAgent();
+    EXPECT_EQ(buffer, base::StringPrintf(
+                          content::frozen_user_agent_strings::kDesktop,
+                          version_info::GetMajorVersionNumber().c_str()));
+  }
+#endif
+}
+
+TEST(UserAgentUtilsTest, UserAgentMetadata) {
+  auto metadata = GetUserAgentMetadata();
+
+  std::string major_version = version_info::GetMajorVersionNumber();
+
+  // According to spec, Sec-CH-UA should contain what project the browser is
+  // based on (i.e. Chromium in this case) as well as the actual product.
+  // In CHROMIUM_BRANDING builds this will check chromium twice. That should be
+  // ok though.
+
+  const blink::UserAgentBrandVersion chromium_brand_version = {"Chromium",
+                                                               major_version};
+  const blink::UserAgentBrandVersion product_brand_version = {
+      version_info::GetProductName(), version_info::GetMajorVersionNumber()};
+  bool contains_chromium_brand_version = false;
+  bool contains_product_brand_version = false;
+
+  for (const auto& brand_version : metadata.brand_version_list) {
+    if (brand_version == chromium_brand_version) {
+      contains_chromium_brand_version = true;
+    }
+    if (brand_version == product_brand_version) {
+      contains_product_brand_version = true;
+    }
+  }
+
+  EXPECT_TRUE(contains_chromium_brand_version);
+  EXPECT_TRUE(contains_product_brand_version);
+
+  EXPECT_EQ(metadata.full_version, version_info::GetVersionNumber());
+  EXPECT_EQ(metadata.platform_version,
+            content::GetOSVersion(content::IncludeAndroidBuildNumber::Exclude,
+                                  content::IncludeAndroidModel::Exclude));
+  // This makes sure no extra information is added to the platform version.
+  EXPECT_EQ(metadata.platform_version.find(";"), std::string::npos);
+  EXPECT_EQ(metadata.platform, version_info::GetOSType());
+  EXPECT_EQ(metadata.architecture, content::GetLowEntropyCpuArchitecture());
+  EXPECT_EQ(metadata.model, content::BuildModelInfo());
+}
+
+TEST(UserAgentUtilsTest, GenerateBrandVersionList) {
+  blink::UserAgentMetadata metadata;
+
+  metadata.brand_version_list =
+      GenerateBrandVersionList(84, base::nullopt, "84", base::nullopt);
+  std::string brand_list = metadata.SerializeBrandVersionList();
+  EXPECT_EQ(R"(" Not A;Brand";v="99", "Chromium";v="84")", brand_list);
+
+  metadata.brand_version_list =
+      GenerateBrandVersionList(85, base::nullopt, "85", base::nullopt);
+  std::string brand_list_diff = metadata.SerializeBrandVersionList();
+  // Make sure the lists are different for different seeds
+  EXPECT_EQ(R"("Chromium";v="85", " Not;A Brand";v="99")", brand_list_diff);
+  EXPECT_NE(brand_list, brand_list_diff);
+
+  metadata.brand_version_list =
+      GenerateBrandVersionList(84, "Totally A Brand", "84", base::nullopt);
+  std::string brand_list_w_brand = metadata.SerializeBrandVersionList();
+  EXPECT_EQ(
+      R"(" Not A;Brand";v="99", "Chromium";v="84", "Totally A Brand";v="84")",
+      brand_list_w_brand);
+
+  metadata.brand_version_list =
+      GenerateBrandVersionList(84, base::nullopt, "84", "Clean GREASE");
+  std::string brand_list_grease_override = metadata.SerializeBrandVersionList();
+  EXPECT_EQ(R"("Clean GREASE";v="99", "Chromium";v="84")",
+            brand_list_grease_override);
+  EXPECT_NE(brand_list, brand_list_grease_override);
+
+  // Should DCHECK on negative numbers
+  EXPECT_DCHECK_DEATH(
+      GenerateBrandVersionList(-1, base::nullopt, "99", base::nullopt));
+}
+
+}  // namespace embedder_support
diff --git a/components/gcm_driver/android/BUILD.gn b/components/gcm_driver/android/BUILD.gn
index 4d25e05..d9663411 100644
--- a/components/gcm_driver/android/BUILD.gn
+++ b/components/gcm_driver/android/BUILD.gn
@@ -15,7 +15,6 @@
     "//content/public/android:content_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
-    "//third_party/android_sdk:android_gcm_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 
diff --git a/components/mirroring/service/session.cc b/components/mirroring/service/session.cc
index f0ec4dc4c..ec556d46 100644
--- a/components/mirroring/service/session.cc
+++ b/components/mirroring/service/session.cc
@@ -401,10 +401,11 @@
       state_(MIRRORING),
       observer_(std::move(observer)),
       resource_provider_(std::move(resource_provider)),
-      message_dispatcher_(std::move(outbound_channel),
-                          std::move(inbound_channel),
-                          base::BindRepeating(&Session::OnResponseParsingError,
-                                              base::Unretained(this))),
+      message_dispatcher_(std::make_unique<MessageDispatcher>(
+          std::move(outbound_channel),
+          std::move(inbound_channel),
+          base::BindRepeating(&Session::OnResponseParsingError,
+                              base::Unretained(this)))),
       gpu_channel_host_(nullptr) {
   DCHECK(resource_provider_);
   mirror_settings_.SetResolutionConstraints(max_resolution.width(),
@@ -492,12 +493,16 @@
   state_ = STOPPED;
   StopStreaming();
 
+  // Notes on order: the media remoter needs to deregister itself from the
+  // message dispatcher, which then needs to deregister from the resource
+  // provider.
+  media_remoter_.reset();
+  message_dispatcher_.reset();
   setup_querier_.reset();
   weak_factory_.InvalidateWeakPtrs();
   audio_encode_thread_ = nullptr;
   video_encode_thread_ = nullptr;
   video_capture_client_.reset();
-  media_remoter_.reset();
   resource_provider_.reset();
   gpu_channel_host_ = nullptr;
   gpu_.reset();
@@ -812,7 +817,7 @@
   std::unique_ptr<WifiStatusMonitor> wifi_status_monitor;
   if (answer.supports_wifi_status_reporting) {
     wifi_status_monitor =
-        std::make_unique<WifiStatusMonitor>(&message_dispatcher_);
+        std::make_unique<WifiStatusMonitor>(message_dispatcher_.get());
     // Nest Hub devices do not support remoting despite having a relatively new
     // build version, so we cannot filter with
     // NeedsWorkaroundForOlder1DotXVersions() here.
@@ -935,7 +940,7 @@
   offer.SetKey("receiverGetStatus", base::Value(true));
   offer.SetKey("supportedStreams", base::Value(stream_list));
 
-  const int32_t sequence_number = message_dispatcher_.GetNextSeqNumber();
+  const int32_t sequence_number = message_dispatcher_->GetNextSeqNumber();
   base::Value offer_message(base::Value::Type::DICTIONARY);
   offer_message.SetKey("type", base::Value("OFFER"));
   offer_message.SetKey("seqNum", base::Value(sequence_number));
@@ -947,7 +952,7 @@
       offer_message, &message_to_receiver->json_format_data);
   DCHECK(did_serialize_offer);
 
-  message_dispatcher_.RequestReply(
+  message_dispatcher_->RequestReply(
       std::move(message_to_receiver), ResponseType::ANSWER, sequence_number,
       kOfferAnswerExchangeTimeout,
       base::BindOnce(&Session::OnAnswer, base::Unretained(this), audio_configs,
@@ -981,7 +986,7 @@
 
 void Session::QueryCapabilitiesForRemoting() {
   DCHECK(!media_remoter_);
-  const int32_t sequence_number = message_dispatcher_.GetNextSeqNumber();
+  const int32_t sequence_number = message_dispatcher_->GetNextSeqNumber();
   base::Value query(base::Value::Type::DICTIONARY);
   query.SetKey("type", base::Value("GET_CAPABILITIES"));
   query.SetKey("seqNum", base::Value(sequence_number));
@@ -991,13 +996,16 @@
   const bool did_serialize_query =
       base::JSONWriter::Write(query, &query_message->json_format_data);
   DCHECK(did_serialize_query);
-  message_dispatcher_.RequestReply(
+  message_dispatcher_->RequestReply(
       std::move(query_message), ResponseType::CAPABILITIES_RESPONSE,
       sequence_number, kGetCapabilitiesTimeout,
       base::BindOnce(&Session::OnCapabilitiesResponse, base::Unretained(this)));
 }
 
 void Session::OnCapabilitiesResponse(const ReceiverResponse& response) {
+  if (state_ == STOPPED)
+    return;
+
   if (!response.valid()) {
     VLOG(1) << "Bad CAPABILITIES_RESPONSE. Remoting disabled.";
     if (response.error()) {
@@ -1035,7 +1043,7 @@
       this,
       ToRemotingSinkMetadata(caps, friendly_name, session_params_,
                              build_version),
-      &message_dispatcher_);
+      message_dispatcher_.get());
 }
 
 }  // namespace mirroring
diff --git a/components/mirroring/service/session.h b/components/mirroring/service/session.h
index b5c501cc..25c03dc 100644
--- a/components/mirroring/service/session.h
+++ b/components/mirroring/service/session.h
@@ -169,7 +169,7 @@
   mojo::Remote<mojom::ResourceProvider> resource_provider_;
   MirrorSettings mirror_settings_;
 
-  MessageDispatcher message_dispatcher_;
+  std::unique_ptr<MessageDispatcher> message_dispatcher_;
 
   mojo::Remote<network::mojom::NetworkContext> network_context_;
 
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 2dab7ef..de6a43d1 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -556,20 +556,6 @@
   return nullptr;
 }
 
-bool AutocompleteResult::TopMatchIsStandaloneVerbatimMatch() const {
-  if (empty() || !match_at(0).IsVerbatimType())
-    return false;
-
-  // Skip any copied matches, under the assumption that they'll be expired and
-  // disappear.  We don't want this disappearance to cause the visibility of the
-  // top match to change.
-  for (auto i(begin() + 1); i != end(); ++i) {
-    if (!i->from_previous)
-      return !i->IsVerbatimType();
-  }
-  return true;
-}
-
 void AutocompleteResult::GroupSuggestionsBySearchVsURL(int first_index,
                                                        int last_index) const {
   const int num_elements = matches_.size();
diff --git a/components/omnibox/browser/autocomplete_result.h b/components/omnibox/browser/autocomplete_result.h
index 646bd4d9..962bbb7 100644
--- a/components/omnibox/browser/autocomplete_result.h
+++ b/components/omnibox/browser/autocomplete_result.h
@@ -131,11 +131,6 @@
   // Returns the default match if it exists, or nullptr otherwise.
   const AutocompleteMatch* default_match() const;
 
-  // Returns true if the top match is a verbatim search or URL match (see
-  // IsVerbatimType() in autocomplete_match.h), and the next match is not also
-  // some kind of verbatim match.
-  bool TopMatchIsStandaloneVerbatimMatch() const;
-
   // Returns the first match in |matches| which might be chosen as default.
   // If the page is not the fake box, the scores are not demoted by type.
   static ACMatches::const_iterator FindTopMatch(const AutocompleteInput& input,
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index 4a770516..5b3eef8e 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -1859,37 +1859,6 @@
   }
 }
 
-TEST_F(AutocompleteResultTest, TopMatchIsStandaloneVerbatimMatch) {
-  ACMatches matches;
-  AutocompleteResult result;
-  result.AppendMatches(AutocompleteInput(), matches);
-
-  // Case 1: Result set is empty.
-  EXPECT_FALSE(result.TopMatchIsStandaloneVerbatimMatch());
-
-  // Case 2: Top match is not a verbatim match.
-  PopulateAutocompleteMatchesFromTestData(kNonVerbatimMatches, 1, &matches);
-  result.AppendMatches(AutocompleteInput(), matches);
-  EXPECT_FALSE(result.TopMatchIsStandaloneVerbatimMatch());
-  result.Reset();
-  matches.clear();
-
-  // Case 3: Top match is a verbatim match.
-  PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches);
-  result.AppendMatches(AutocompleteInput(), matches);
-  EXPECT_TRUE(result.TopMatchIsStandaloneVerbatimMatch());
-  result.Reset();
-  matches.clear();
-
-  // Case 4: Standalone verbatim match found in AutocompleteResult.
-  PopulateAutocompleteMatchesFromTestData(kVerbatimMatches, 1, &matches);
-  PopulateAutocompleteMatchesFromTestData(kNonVerbatimMatches, 1, &matches);
-  result.AppendMatches(AutocompleteInput(), matches);
-  EXPECT_TRUE(result.TopMatchIsStandaloneVerbatimMatch());
-  result.Reset();
-  matches.clear();
-}
-
 namespace {
 
 bool EqualClassifications(const std::vector<ACMatchClassification>& lhs,
diff --git a/components/optimization_guide/proto/common_types.proto b/components/optimization_guide/proto/common_types.proto
index 5ea111f..9ea386c 100644
--- a/components/optimization_guide/proto/common_types.proto
+++ b/components/optimization_guide/proto/common_types.proto
@@ -86,3 +86,11 @@
   // to +999,999,999 inclusive.
   optional int32 nanos = 2;
 }
+
+message Any {
+  // A URL/resource name that uniquely identifies the type of the serialized
+  // protocol buffer message.
+  optional string type_url = 1;
+  // Must be a valid serialized protocol buffer of the above specified type.
+  optional bytes value = 2;
+}
diff --git a/components/optimization_guide/proto/hints.proto b/components/optimization_guide/proto/hints.proto
index 8550588b..149f2f9 100644
--- a/components/optimization_guide/proto/hints.proto
+++ b/components/optimization_guide/proto/hints.proto
@@ -324,11 +324,3 @@
   // The source from which the hint was served from.
   optional HintSource hint_source = 3;
 }
-
-message Any {
-  // A URL/resource name that uniquely identifies the type of the serialized
-  // protocol buffer message.
-  optional string type_url = 1;
-  // Must be a valid serialized protocol buffer of the above specified type.
-  optional bytes value = 2;
-}
diff --git a/components/optimization_guide/proto/models.proto b/components/optimization_guide/proto/models.proto
index 4c795d43..323dfe3b 100644
--- a/components/optimization_guide/proto/models.proto
+++ b/components/optimization_guide/proto/models.proto
@@ -202,13 +202,23 @@
   // If in the request, this represents the set of features that the client
   // understands how to evaluate. If in the response, this represents the set
   // of features referenced by the model.
-  repeated ClientModelFeature supported_model_features = 3;
+  //
+  // New use cases should use the model_metadata field instead.
+  repeated ClientModelFeature supported_model_features = 3 [deprecated = true];
   // The set of model types the requesting client can use to make predictions.
   repeated ModelType supported_model_types = 4;
   // The set of host model features that are referenced by the model.
   //
   // Note that this should only be populated if part of the response.
   repeated string supported_host_model_features = 5;
+  // Mechanism used for model owners to attach metadata to the request or
+  // response.
+  //
+  // In practice, we expect this to be used as a way to negotiate capabilities.
+  // The client can provide the model features they can evaluate if this field
+  // is part of the request, and the server can provide the model features that
+  // are actually present in the model.
+  optional Any model_metadata = 6;
 }
 
 // The scenarios for which the optimization guide has models for.
diff --git a/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc b/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc
index a8b5952..2d732c23 100644
--- a/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc
+++ b/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.cc
@@ -17,20 +17,14 @@
 
 DeviceAccountsSynchronizerImpl::~DeviceAccountsSynchronizerImpl() = default;
 
-#if defined(OS_ANDROID)
 void DeviceAccountsSynchronizerImpl::
     ReloadAllAccountsFromSystemWithPrimaryAccount(
         const base::Optional<CoreAccountId>& primary_account_id) {
   token_service_delegate_->ReloadAllAccountsFromSystemWithPrimaryAccount(
       primary_account_id);
 }
-#endif
 
 #if defined(OS_IOS)
-void DeviceAccountsSynchronizerImpl::ReloadAllAccountsFromSystem() {
-  token_service_delegate_->ReloadAllAccountsFromSystem();
-}
-
 void DeviceAccountsSynchronizerImpl::ReloadAccountFromSystem(
     const CoreAccountId& account_id) {
   token_service_delegate_->ReloadAccountFromSystem(account_id);
diff --git a/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h b/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h
index dd425e37d..0ee9d2fd 100644
--- a/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h
+++ b/components/signin/internal/identity_manager/device_accounts_synchronizer_impl.h
@@ -20,13 +20,10 @@
   ~DeviceAccountsSynchronizerImpl() override;
 
   // DeviceAccountsSynchronizer implementation.
-#if defined(OS_ANDROID)
   void ReloadAllAccountsFromSystemWithPrimaryAccount(
       const base::Optional<CoreAccountId>& primary_account_id) override;
-#endif
 
 #if defined(OS_IOS)
-  void ReloadAllAccountsFromSystem() override;
   void ReloadAccountFromSystem(const CoreAccountId& account_id) override;
 #endif
 
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h
index 6107e14d..999e646 100644
--- a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h
+++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h
@@ -126,11 +126,13 @@
   // and false otherwise.
   virtual bool FixRequestErrorIfPossible();
 
-#if defined(OS_IOS)
-  // Triggers platform specific implementation for iOS to reload all accounts
-  // from system.
-  virtual void ReloadAllAccountsFromSystem() {}
+#if defined(OS_IOS) || defined(OS_ANDROID)
+  // Triggers platform specific implementation to reload accounts from system.
+  virtual void ReloadAllAccountsFromSystemWithPrimaryAccount(
+      const base::Optional<CoreAccountId>& primary_account_id) {}
+#endif
 
+#if defined(OS_IOS)
   // Triggers platform specific implementation for iOS to add a given account
   // to the token service from a system account.
   virtual void ReloadAccountFromSystem(const CoreAccountId& account_id) {}
@@ -139,11 +141,6 @@
 #if defined(OS_ANDROID)
   // Returns a reference to the corresponding Java object.
   virtual base::android::ScopedJavaLocalRef<jobject> GetJavaObject() = 0;
-
-  // Triggers platform specific implementation for Android to reload accounts
-  // from system.
-  virtual void ReloadAllAccountsFromSystemWithPrimaryAccount(
-      const base::Optional<CoreAccountId>& primary_account_id) {}
 #endif
 
   // -----------------------------------------------------------------------
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h
index 25ccdd8..3cac71d 100644
--- a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h
+++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.h
@@ -53,8 +53,8 @@
   // Subsequent calls to |RefreshTokenIsAvailable| will return |false|.
   void RevokeAllCredentials() override;
 
-  void ReloadAllAccountsFromSystem() override;
-
+  void ReloadAllAccountsFromSystemWithPrimaryAccount(
+      const base::Optional<CoreAccountId>& primary_account_id) override;
   void ReloadAccountFromSystem(const CoreAccountId& account_id) override;
 
   // Adds |account_id| to |accounts_| if it does not exist or udpates
@@ -69,16 +69,6 @@
 
  private:
   friend class ProfileOAuth2TokenServiceIOSDelegateTest;
-  FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceIOSDelegateTest,
-                           LoadRevokeCredentialsClearsExcludedAccounts);
-  FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceIOSDelegateTest,
-                           ReloadCredentials);
-  FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceIOSDelegateTest,
-                           ReloadCredentialsWithPrimaryAccountId);
-  FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceIOSDelegateTest,
-                           UpdateAuthErrorAfterRevokeCredentials);
-  FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceIOSDelegateTest,
-                           GetAuthError);
 
   struct AccountStatus {
     GoogleServiceAuthError last_auth_error;
@@ -91,7 +81,7 @@
   // Reloads accounts from the provider. Fires |OnRefreshTokenAvailable| for
   // each new account. Fires |OnRefreshTokenRevoked| for each account that was
   // removed.
-  void ReloadCredentials();
+  void ReloadCredentials(const CoreAccountId& primary_account_id);
 
   // Info about the existing accounts.
   AccountStatusMap accounts_;
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm
index 377a482..2103897 100644
--- a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm
+++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios.mm
@@ -193,14 +193,35 @@
     return;
   }
 
-  ReloadCredentials();
+  ReloadCredentials(primary_account_id);
+  if (RefreshTokenIsAvailable(primary_account_id)) {
+    set_load_credentials_state(
+        signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS);
+  } else {
+    // Account must have been seeded before (when the primary account was set).
+    DCHECK(!account_tracker_service_->GetAccountInfo(primary_account_id)
+                .gaia.empty());
+    DCHECK(!account_tracker_service_->GetAccountInfo(primary_account_id)
+                .email.empty());
 
-  set_load_credentials_state(
-      signin::LoadCredentialsState::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS);
+    // For whatever reason, we failed to load the device account for the primary
+    // account. There must always be an account for the primary account
+    accounts_[primary_account_id].last_auth_error =
+        GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+            GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+                CREDENTIALS_MISSING);
+    FireAuthErrorChanged(primary_account_id,
+                         accounts_[primary_account_id].last_auth_error);
+    FireRefreshTokenAvailable(primary_account_id);
+    set_load_credentials_state(
+        signin::LoadCredentialsState::
+            LOAD_CREDENTIALS_FINISHED_WITH_NO_TOKEN_FOR_PRIMARY_ACCOUNT);
+  }
   FireRefreshTokensLoaded();
 }
 
-void ProfileOAuth2TokenServiceIOSDelegate::ReloadCredentials() {
+void ProfileOAuth2TokenServiceIOSDelegate::ReloadCredentials(
+    const CoreAccountId& primary_account_id) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Get the list of new account ids.
@@ -235,7 +256,14 @@
   // load |new_accounts|.
   ScopedBatchChange batch(this);
   for (const auto& account_to_remove : accounts_to_remove) {
-    RemoveAccount(account_to_remove);
+    if (account_to_remove == primary_account_id) {
+      UpdateAuthError(account_to_remove,
+                      GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+                          GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+                              CREDENTIALS_MISSING));
+    } else {
+      RemoveAccount(account_to_remove);
+    }
   }
 
   // Load all new_accounts.
@@ -263,8 +291,10 @@
   DCHECK_EQ(0u, accounts_.size());
 }
 
-void ProfileOAuth2TokenServiceIOSDelegate::ReloadAllAccountsFromSystem() {
-  ReloadCredentials();
+void ProfileOAuth2TokenServiceIOSDelegate::
+    ReloadAllAccountsFromSystemWithPrimaryAccount(
+        const base::Optional<CoreAccountId>& primary_account_id) {
+  ReloadCredentials(primary_account_id.value_or(CoreAccountId()));
 }
 
 void ProfileOAuth2TokenServiceIOSDelegate::ReloadAccountFromSystem(
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm
index 9516d54..278a7268 100644
--- a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm
+++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_ios_unittest.mm
@@ -163,7 +163,38 @@
       oauth2_delegate_->RefreshTokenIsAvailable(GetAccountId(account3)));
 }
 
-TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, ReloadCredentials) {
+TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest,
+       LoadCredentialsPrimaryAccountMissing) {
+  CoreAccountId primary_account =
+      account_tracker_.SeedAccountInfo("gaia_1", "email_1@x");
+  oauth2_delegate_->LoadCredentials(primary_account);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, token_available_count_);
+  EXPECT_EQ(1, tokens_loaded_count_);
+  EXPECT_EQ(0, token_revoked_count_);
+  EXPECT_EQ(1, auth_error_changed_count_);
+  EXPECT_EQ(1U, oauth2_delegate_->GetAccounts().size());
+  EXPECT_TRUE(oauth2_delegate_->RefreshTokenIsAvailable(primary_account));
+  EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
+            oauth2_delegate_->GetAuthError(primary_account).state());
+  ResetObserverCounts();
+
+  // Reloading the account should not remove the primary account even if it is
+  // missing from the device.
+  oauth2_delegate_->ReloadAllAccountsFromSystemWithPrimaryAccount(
+      primary_account);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, token_available_count_);
+  EXPECT_EQ(0, tokens_loaded_count_);
+  EXPECT_EQ(0, token_revoked_count_);
+  EXPECT_EQ(0, auth_error_changed_count_);
+  EXPECT_EQ(1U, oauth2_delegate_->GetAccounts().size());
+  EXPECT_TRUE(oauth2_delegate_->RefreshTokenIsAvailable(primary_account));
+  EXPECT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
+            oauth2_delegate_->GetAuthError(primary_account).state());
+}
+
+TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, ReloadAllAccountsFromSystem) {
   ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x");
   ProviderAccount account2 = fake_provider_->AddAccount("gaia_2", "email_2@x");
   ProviderAccount account3 = fake_provider_->AddAccount("gaia_3", "email_3@x");
@@ -175,7 +206,8 @@
   fake_provider_->ClearAccounts();
   fake_provider_->AddAccount(account1.gaia, account1.email);
   ProviderAccount account4 = fake_provider_->AddAccount("gaia_4", "email_4@x");
-  oauth2_delegate_->ReloadCredentials();
+  oauth2_delegate_->ReloadAllAccountsFromSystemWithPrimaryAccount(
+      GetAccountId(account1));
 
   EXPECT_EQ(1, token_available_count_);
   EXPECT_EQ(0, tokens_loaded_count_);
@@ -192,10 +224,11 @@
 }
 
 TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest,
-       ReloadCredentialsWithPrimaryAccountId) {
+       ReloadAllAccountsFromSystemWithPrimaryAccountId) {
   ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x");
   ProviderAccount account2 = fake_provider_->AddAccount("gaia_2", "email_2@x");
-  oauth2_delegate_->ReloadCredentials();
+  oauth2_delegate_->ReloadAllAccountsFromSystemWithPrimaryAccount(
+      GetAccountId(account1));
 
   EXPECT_EQ(2, token_available_count_);
   EXPECT_EQ(0, tokens_loaded_count_);
@@ -260,7 +293,7 @@
 TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest,
        UpdateAuthErrorAfterRevokeCredentials) {
   ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x");
-  oauth2_delegate_->ReloadCredentials();
+  oauth2_delegate_->LoadCredentials(GetAccountId(account1));
   base::RunLoop().RunUntilIdle();
 
   ResetObserverCounts();
@@ -278,7 +311,7 @@
 TEST_F(ProfileOAuth2TokenServiceIOSDelegateTest, GetAuthError) {
   // Accounts have no error by default.
   ProviderAccount account1 = fake_provider_->AddAccount("gaia_1", "email_1@x");
-  oauth2_delegate_->ReloadCredentials();
+  oauth2_delegate_->LoadCredentials(GetAccountId(account1));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
             oauth2_delegate_->GetAuthError(GetAccountId(account1)));
diff --git a/components/signin/public/identity_manager/device_accounts_synchronizer.h b/components/signin/public/identity_manager/device_accounts_synchronizer.h
index 8224eaf9..3c441ae8 100644
--- a/components/signin/public/identity_manager/device_accounts_synchronizer.h
+++ b/components/signin/public/identity_manager/device_accounts_synchronizer.h
@@ -18,20 +18,13 @@
   DeviceAccountsSynchronizer() = default;
   virtual ~DeviceAccountsSynchronizer() = default;
 
-#if defined(OS_ANDROID)
   // Reloads the information of all device-level accounts. All device-level
   // accounts will be visible in IdentityManager::GetAccountsWithRefreshTokens()
   // with any persistent errors cleared after this method is called.
   virtual void ReloadAllAccountsFromSystemWithPrimaryAccount(
       const base::Optional<CoreAccountId>& primary_account_id) = 0;
-#endif
 
 #if defined(OS_IOS)
-  // Reloads the information of all device-level accounts. All device-level
-  // accounts will be visible in IdentityManager::GetAccountsWithRefreshTokens()
-  // with any persistent errors cleared after this method is called.
-  virtual void ReloadAllAccountsFromSystem() = 0;
-
   // Reloads the information of the device-level account with |account_id|. The
   // account will be visible in IdentityManager::GetAccountsWithRefreshTokens()
   // with any persistent error cleared after this method is called.
diff --git a/components/sync/android/BUILD.gn b/components/sync/android/BUILD.gn
index 3209f892..7ca181c 100644
--- a/components/sync/android/BUILD.gn
+++ b/components/sync/android/BUILD.gn
@@ -13,7 +13,6 @@
     "//net/android:net_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
-    "//third_party/android_sdk:android_gcm_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   srcjar_deps = [ ":java_enums" ]
diff --git a/components/sync/driver/fake_sync_service.cc b/components/sync/driver/fake_sync_service.cc
index 9ca8ec567..83e0c51 100644
--- a/components/sync/driver/fake_sync_service.cc
+++ b/components/sync/driver/fake_sync_service.cc
@@ -65,6 +65,8 @@
 
 void FakeSyncService::StopAndClear() {}
 
+void FakeSyncService::SetSyncAllowedByPlatform(bool allowed) {}
+
 void FakeSyncService::OnDataTypeRequestsSyncStartup(ModelType type) {}
 
 ModelTypeSet FakeSyncService::GetPreferredDataTypes() const {
diff --git a/components/sync/driver/fake_sync_service.h b/components/sync/driver/fake_sync_service.h
index 2890b0b..b6a2b898 100644
--- a/components/sync/driver/fake_sync_service.h
+++ b/components/sync/driver/fake_sync_service.h
@@ -39,6 +39,7 @@
   bool HasObserver(const SyncServiceObserver* observer) const override;
   void OnDataTypeRequestsSyncStartup(ModelType type) override;
   void StopAndClear() override;
+  void SetSyncAllowedByPlatform(bool allowed) override;
   ModelTypeSet GetPreferredDataTypes() const override;
   std::unique_ptr<SyncSetupInProgressHandle> GetSetupInProgressHandle()
       override;
diff --git a/components/sync/driver/mock_sync_service.h b/components/sync/driver/mock_sync_service.h
index a71456d..be53eb1 100644
--- a/components/sync/driver/mock_sync_service.h
+++ b/components/sync/driver/mock_sync_service.h
@@ -53,6 +53,7 @@
   MOCK_METHOD(ModelTypeSet, GetPreferredDataTypes, (), (const override));
   MOCK_METHOD(ModelTypeSet, GetActiveDataTypes, (), (const override));
   MOCK_METHOD(void, StopAndClear, (), (override));
+  MOCK_METHOD(void, SetSyncAllowedByPlatform, (bool), (override));
   MOCK_METHOD(void,
               OnDataTypeRequestsSyncStartup,
               (ModelType type),
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index 449f8a9..4ea88a8 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -300,9 +300,7 @@
 
   user_settings_ = std::make_unique<SyncUserSettingsImpl>(
       &crypto_, &sync_prefs_, sync_client_->GetPreferenceProvider(),
-      GetRegisteredDataTypes(),
-      base::BindRepeating(&ProfileSyncService::SyncAllowedByPlatformChanged,
-                          base::Unretained(this)));
+      GetRegisteredDataTypes());
 
   sync_prefs_.AddSyncPrefObserver(this);
 
@@ -751,7 +749,7 @@
   // shouldn't even be instantiated.
   DCHECK(switches::IsSyncAllowedByFlag());
   DisableReasonSet result;
-  if (!user_settings_->IsSyncAllowedByPlatform()) {
+  if (!sync_allowed_by_platform_) {
     result.Put(DISABLE_REASON_PLATFORM_OVERRIDE);
   }
 
@@ -1809,6 +1807,23 @@
   startup_controller_->TryStart(/*force_immediate=*/true);
 }
 
+void ProfileSyncService::SetSyncAllowedByPlatform(bool allowed) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (allowed == sync_allowed_by_platform_) {
+    return;
+  }
+
+  sync_allowed_by_platform_ = allowed;
+  if (!sync_allowed_by_platform_) {
+    StopImpl(KEEP_DATA);
+    // Try to start up again (in transport-only mode).
+    // TODO(crbug.com/856179): Evaluate whether we can get away without a full
+    // restart (i.e. just reconfigure). See also similar comment in
+    // OnSyncRequestedPrefChange().
+    startup_controller_->TryStart(/*force_immediate=*/true);
+  }
+}
+
 void ProfileSyncService::ReconfigureDatatypeManager(
     bool bypass_setup_in_progress_check) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/components/sync/driver/profile_sync_service.h b/components/sync/driver/profile_sync_service.h
index 6d70341..f921ece 100644
--- a/components/sync/driver/profile_sync_service.h
+++ b/components/sync/driver/profile_sync_service.h
@@ -123,6 +123,7 @@
   ModelTypeSet GetPreferredDataTypes() const override;
   ModelTypeSet GetActiveDataTypes() const override;
   void StopAndClear() override;
+  void SetSyncAllowedByPlatform(bool allowed) override;
   void OnDataTypeRequestsSyncStartup(ModelType type) override;
   void TriggerRefresh(const ModelTypeSet& types) override;
   void DataTypePreconditionChanged(ModelType type) override;
@@ -435,6 +436,10 @@
   // or user signed out.
   bool sync_disabled_by_admin_;
 
+  // Whether Sync is allowed at the platform level (e.g. Android's "MasterSync"
+  // toggle). Maps to DISABLE_REASON_PLATFORM_OVERRIDE.
+  bool sync_allowed_by_platform_ = true;
+
   // Information describing an unrecoverable error.
   base::Optional<UnrecoverableErrorReason> unrecoverable_error_reason_ =
       base::nullopt;
diff --git a/components/sync/driver/profile_sync_service_unittest.cc b/components/sync/driver/profile_sync_service_unittest.cc
index e4fe201e..ea00e02a 100644
--- a/components/sync/driver/profile_sync_service_unittest.cc
+++ b/components/sync/driver/profile_sync_service_unittest.cc
@@ -558,6 +558,24 @@
             service()->GetTransportState());
 }
 
+TEST_F(ProfileSyncServiceTest,
+       ShouldDisableSyncFeatureWhenSyncDisallowedByPlatform) {
+  SignIn();
+  CreateService(ProfileSyncService::MANUAL_START);
+  InitializeForNthSync();
+
+  ASSERT_EQ(SyncService::DisableReasonSet(), service()->GetDisableReasons());
+  ASSERT_EQ(SyncService::TransportState::ACTIVE,
+            service()->GetTransportState());
+
+  service()->SetSyncAllowedByPlatform(false);
+  EXPECT_FALSE(service()->IsSyncFeatureEnabled());
+  EXPECT_FALSE(service()->IsSyncFeatureActive());
+  // Sync-the-transport should be still active.
+  EXPECT_EQ(SyncService::TransportState::ACTIVE,
+            service()->GetTransportState());
+}
+
 // Exercises the ProfileSyncService's code paths related to getting shut down
 // before the backend initialize call returns.
 TEST_F(ProfileSyncServiceTest, AbortedByShutdown) {
diff --git a/components/sync/driver/sync_service.h b/components/sync/driver/sync_service.h
index b5e7aedf..920e86b 100644
--- a/components/sync/driver/sync_service.h
+++ b/components/sync/driver/sync_service.h
@@ -319,6 +319,10 @@
   // remain active after calling this.
   virtual void StopAndClear() = 0;
 
+  // Controls whether sync is allowed at the platform level. If set to false
+  // sync will be disabled with DISABLE_REASON_PLATFORM_OVERRIDE.
+  virtual void SetSyncAllowedByPlatform(bool allowed) = 0;
+
   // Called when a datatype (SyncableService) has a need for sync to start
   // ASAP, presumably because a local change event has occurred but we're
   // still in deferred start mode, meaning the SyncableService hasn't been
diff --git a/components/sync/driver/sync_user_settings.h b/components/sync/driver/sync_user_settings.h
index eeb041c..ac4ff8a 100644
--- a/components/sync/driver/sync_user_settings.h
+++ b/components/sync/driver/sync_user_settings.h
@@ -42,11 +42,6 @@
   virtual bool IsSyncRequested() const = 0;
   virtual void SetSyncRequested(bool requested) = 0;
 
-  // Whether Sync is allowed at the platform level (e.g. Android's "MasterSync"
-  // toggle). Maps to DISABLE_REASON_PLATFORM_OVERRIDE.
-  virtual bool IsSyncAllowedByPlatform() const = 0;
-  virtual void SetSyncAllowedByPlatform(bool allowed) = 0;
-
   // Whether the initial Sync setup has been completed, meaning the user has
   // consented to Sync.
   // NOTE: On ChromeOS, this gets set automatically, so it doesn't really mean
diff --git a/components/sync/driver/sync_user_settings_impl.cc b/components/sync/driver/sync_user_settings_impl.cc
index adf2bfc4..a9a779a 100644
--- a/components/sync/driver/sync_user_settings_impl.cc
+++ b/components/sync/driver/sync_user_settings_impl.cc
@@ -43,13 +43,11 @@
     SyncServiceCrypto* crypto,
     SyncPrefs* prefs,
     const SyncTypePreferenceProvider* preference_provider,
-    ModelTypeSet registered_model_types,
-    const base::RepeatingCallback<void(bool)>& sync_allowed_by_platform_changed)
+    ModelTypeSet registered_model_types)
     : crypto_(crypto),
       prefs_(prefs),
       preference_provider_(preference_provider),
-      registered_model_types_(registered_model_types),
-      sync_allowed_by_platform_changed_cb_(sync_allowed_by_platform_changed) {
+      registered_model_types_(registered_model_types) {
   DCHECK(crypto_);
   DCHECK(prefs_);
 }
@@ -64,20 +62,6 @@
   prefs_->SetSyncRequested(requested);
 }
 
-bool SyncUserSettingsImpl::IsSyncAllowedByPlatform() const {
-  return sync_allowed_by_platform_;
-}
-
-void SyncUserSettingsImpl::SetSyncAllowedByPlatform(bool allowed) {
-  if (sync_allowed_by_platform_ == allowed) {
-    return;
-  }
-
-  sync_allowed_by_platform_ = allowed;
-
-  sync_allowed_by_platform_changed_cb_.Run(sync_allowed_by_platform_);
-}
-
 bool SyncUserSettingsImpl::IsFirstSetupComplete() const {
   return prefs_->IsFirstSetupComplete();
 }
diff --git a/components/sync/driver/sync_user_settings_impl.h b/components/sync/driver/sync_user_settings_impl.h
index 100e9d58..de10c00 100644
--- a/components/sync/driver/sync_user_settings_impl.h
+++ b/components/sync/driver/sync_user_settings_impl.h
@@ -27,17 +27,12 @@
   SyncUserSettingsImpl(SyncServiceCrypto* crypto,
                        SyncPrefs* prefs,
                        const SyncTypePreferenceProvider* preference_provider,
-                       ModelTypeSet registered_types,
-                       const base::RepeatingCallback<void(bool)>&
-                           sync_allowed_by_platform_changed);
+                       ModelTypeSet registered_types);
   ~SyncUserSettingsImpl() override;
 
   bool IsSyncRequested() const override;
   void SetSyncRequested(bool requested) override;
 
-  bool IsSyncAllowedByPlatform() const override;
-  void SetSyncAllowedByPlatform(bool allowed) override;
-
   bool IsFirstSetupComplete() const override;
   void SetFirstSetupComplete(SyncFirstSetupCompleteSource source) override;
 
@@ -91,9 +86,6 @@
   const SyncTypePreferenceProvider* const preference_provider_;
   const ModelTypeSet registered_model_types_;
   base::RepeatingCallback<void(bool)> sync_allowed_by_platform_changed_cb_;
-
-  // Whether sync is currently allowed on this platform.
-  bool sync_allowed_by_platform_ = true;
 };
 
 }  // namespace syncer
diff --git a/components/sync/driver/sync_user_settings_mock.h b/components/sync/driver/sync_user_settings_mock.h
index 4ae5da6e..eb691e2 100644
--- a/components/sync/driver/sync_user_settings_mock.h
+++ b/components/sync/driver/sync_user_settings_mock.h
@@ -20,8 +20,6 @@
   ~SyncUserSettingsMock() override;
   MOCK_METHOD(bool, IsSyncRequested, (), (const override));
   MOCK_METHOD(void, SetSyncRequested, (bool), (override));
-  MOCK_METHOD(bool, IsSyncAllowedByPlatform, (), (const override));
-  MOCK_METHOD(void, SetSyncAllowedByPlatform, (bool), (override));
   MOCK_METHOD(bool, IsFirstSetupComplete, (), (const override));
   MOCK_METHOD(void,
               SetFirstSetupComplete,
diff --git a/components/sync/driver/sync_user_settings_unittest.cc b/components/sync/driver/sync_user_settings_unittest.cc
index da482cc..46db685 100644
--- a/components/sync/driver/sync_user_settings_unittest.cc
+++ b/components/sync/driver/sync_user_settings_unittest.cc
@@ -69,9 +69,7 @@
       ModelTypeSet registered_types) {
     return std::make_unique<SyncUserSettingsImpl>(
         sync_service_crypto_.get(), sync_prefs_.get(),
-        /*preference_provider=*/nullptr, registered_types,
-        /*sync_allowed_by_platform_changed=*/
-        base::DoNothing());
+        /*preference_provider=*/nullptr, registered_types);
   }
 
   // The order of fields matters because it determines destruction order and
diff --git a/components/sync/driver/test_sync_service.cc b/components/sync/driver/test_sync_service.cc
index b3245af..a0295314 100644
--- a/components/sync/driver/test_sync_service.cc
+++ b/components/sync/driver/test_sync_service.cc
@@ -202,6 +202,8 @@
 
 void TestSyncService::StopAndClear() {}
 
+void TestSyncService::SetSyncAllowedByPlatform(bool allowed) {}
+
 void TestSyncService::OnDataTypeRequestsSyncStartup(ModelType type) {}
 
 void TestSyncService::TriggerRefresh(const ModelTypeSet& types) {}
diff --git a/components/sync/driver/test_sync_service.h b/components/sync/driver/test_sync_service.h
index 1bb26e1f..a27d067 100644
--- a/components/sync/driver/test_sync_service.h
+++ b/components/sync/driver/test_sync_service.h
@@ -74,6 +74,7 @@
   ModelTypeSet GetActiveDataTypes() const override;
 
   void StopAndClear() override;
+  void SetSyncAllowedByPlatform(bool allowed) override;
   void OnDataTypeRequestsSyncStartup(ModelType type) override;
   void TriggerRefresh(const ModelTypeSet& types) override;
   void DataTypePreconditionChanged(syncer::ModelType type) override;
diff --git a/components/sync/driver/test_sync_user_settings.cc b/components/sync/driver/test_sync_user_settings.cc
index d4cce15..b22085b 100644
--- a/components/sync/driver/test_sync_user_settings.cc
+++ b/components/sync/driver/test_sync_user_settings.cc
@@ -33,21 +33,6 @@
   service_->SetDisableReasons(disable_reasons);
 }
 
-bool TestSyncUserSettings::IsSyncAllowedByPlatform() const {
-  return !service_->HasDisableReason(
-      SyncService::DISABLE_REASON_PLATFORM_OVERRIDE);
-}
-
-void TestSyncUserSettings::SetSyncAllowedByPlatform(bool allowed) {
-  SyncService::DisableReasonSet disable_reasons = service_->GetDisableReasons();
-  if (allowed) {
-    disable_reasons.Remove(SyncService::DISABLE_REASON_PLATFORM_OVERRIDE);
-  } else {
-    disable_reasons.Put(SyncService::DISABLE_REASON_PLATFORM_OVERRIDE);
-  }
-  service_->SetDisableReasons(disable_reasons);
-}
-
 bool TestSyncUserSettings::IsFirstSetupComplete() const {
   return first_setup_complete_;
 }
diff --git a/components/sync/driver/test_sync_user_settings.h b/components/sync/driver/test_sync_user_settings.h
index 6834959..f87352d 100644
--- a/components/sync/driver/test_sync_user_settings.h
+++ b/components/sync/driver/test_sync_user_settings.h
@@ -24,9 +24,6 @@
   bool IsSyncRequested() const override;
   void SetSyncRequested(bool requested) override;
 
-  bool IsSyncAllowedByPlatform() const override;
-  void SetSyncAllowedByPlatform(bool allowed) override;
-
   bool IsFirstSetupComplete() const override;
   void SetFirstSetupComplete(SyncFirstSetupCompleteSource source) override;
 
diff --git a/components/sync/engine/sync_engine_switches.cc b/components/sync/engine/sync_engine_switches.cc
index 590f184..9fa87498 100644
--- a/components/sync/engine/sync_engine_switches.cc
+++ b/components/sync/engine/sync_engine_switches.cc
@@ -21,7 +21,7 @@
     "SyncSupportTrustedVaultPassphrase", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kSyncTriggerFullKeystoreMigration{
-    "SyncTriggerFullKeystoreMigration", base::FEATURE_DISABLED_BY_DEFAULT};
+    "SyncTriggerFullKeystoreMigration", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Causes Sync to ignore updates encrypted with keys that have been missing for
 // too long from this client; Sync will proceed normally as if those updates
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 2deeb9aef..4f33afdc 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2062,11 +2062,13 @@
       "accessibility/accessibility_event_recorder_uia_win.cc",
       "accessibility/accessibility_event_recorder_uia_win.h",
       "accessibility/accessibility_event_recorder_win.cc",
+      "accessibility/accessibility_event_recorder_win.h",
       "accessibility/accessibility_tree_formatter_uia_win.cc",
       "accessibility/accessibility_tree_formatter_uia_win.h",
       "accessibility/accessibility_tree_formatter_utils_win.cc",
       "accessibility/accessibility_tree_formatter_utils_win.h",
       "accessibility/accessibility_tree_formatter_win.cc",
+      "accessibility/accessibility_tree_formatter_win.h",
       "accessibility/browser_accessibility_com_win.cc",
       "accessibility/browser_accessibility_com_win.h",
       "accessibility/browser_accessibility_manager_win.cc",
diff --git a/content/browser/accessibility/accessibility_event_recorder_win.cc b/content/browser/accessibility/accessibility_event_recorder_win.cc
index f17d3b9..04803f3 100644
--- a/content/browser/accessibility/accessibility_event_recorder_win.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_win.cc
@@ -2,9 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/accessibility/accessibility_event_recorder.h"
+#include "content/browser/accessibility/accessibility_event_recorder_win.h"
 
-#include <oleacc.h>
 #include <stdint.h>
 #include <wrl/client.h>
 
@@ -76,48 +75,6 @@
 
 }  // namespace
 
-class AccessibilityEventRecorderWin : public AccessibilityEventRecorder {
- public:
-  AccessibilityEventRecorderWin(
-      BrowserAccessibilityManager* manager,
-      base::ProcessId pid,
-      const base::StringPiece& application_name_match_pattern);
-  ~AccessibilityEventRecorderWin() override;
-
-  // Callback registered by SetWinEventHook. Just calls OnWinEventHook.
-  static CALLBACK void WinEventHookThunk(HWINEVENTHOOK handle,
-                                         DWORD event,
-                                         HWND hwnd,
-                                         LONG obj_id,
-                                         LONG child_id,
-                                         DWORD event_thread,
-                                         DWORD event_time);
-
- private:
-  // Called by the thunk registered by SetWinEventHook. Retrieves accessibility
-  // info about the node the event was fired on and appends a string to
-  // the event log.
-  void OnWinEventHook(HWINEVENTHOOK handle,
-                      DWORD event,
-                      HWND hwnd,
-                      LONG obj_id,
-                      LONG child_id,
-                      DWORD event_thread,
-                      DWORD event_time);
-
-  // Wrapper around AccessibleObjectFromWindow because the function call
-  // inexplicably flakes sometimes on build/trybots.
-  HRESULT AccessibleObjectFromWindowWrapper(HWND hwnd,
-                                            DWORD dwId,
-                                            REFIID riid,
-                                            void** ppvObject);
-
-  HWINEVENTHOOK win_event_hook_handle_;
-  static AccessibilityEventRecorderWin* instance_;
-
-  DISALLOW_COPY_AND_ASSIGN(AccessibilityEventRecorderWin);
-};
-
 // static
 AccessibilityEventRecorderWin* AccessibilityEventRecorderWin::instance_ =
     nullptr;
diff --git a/content/browser/accessibility/accessibility_event_recorder_win.h b/content/browser/accessibility/accessibility_event_recorder_win.h
new file mode 100644
index 0000000..aadbef1
--- /dev/null
+++ b/content/browser/accessibility/accessibility_event_recorder_win.h
@@ -0,0 +1,60 @@
+// Copyright 2021 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_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_WIN_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_WIN_H_
+
+#include <oleacc.h>
+
+#include "content/browser/accessibility/accessibility_event_recorder.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class CONTENT_EXPORT AccessibilityEventRecorderWin
+    : public AccessibilityEventRecorder {
+ public:
+  AccessibilityEventRecorderWin(
+      BrowserAccessibilityManager* manager,
+      base::ProcessId pid,
+      const base::StringPiece& application_name_match_pattern);
+  ~AccessibilityEventRecorderWin() override;
+
+  // Callback registered by SetWinEventHook. Just calls OnWinEventHook.
+  static CALLBACK void WinEventHookThunk(HWINEVENTHOOK handle,
+                                         DWORD event,
+                                         HWND hwnd,
+                                         LONG obj_id,
+                                         LONG child_id,
+                                         DWORD event_thread,
+                                         DWORD event_time);
+
+ private:
+  // Called by the thunk registered by SetWinEventHook. Retrieves accessibility
+  // info about the node the event was fired on and appends a string to
+  // the event log.
+  void OnWinEventHook(HWINEVENTHOOK handle,
+                      DWORD event,
+                      HWND hwnd,
+                      LONG obj_id,
+                      LONG child_id,
+                      DWORD event_thread,
+                      DWORD event_time);
+
+  // Wrapper around AccessibleObjectFromWindow because the function call
+  // inexplicably flakes sometimes on build/trybots.
+  HRESULT AccessibleObjectFromWindowWrapper(HWND hwnd,
+                                            DWORD dwId,
+                                            REFIID riid,
+                                            void** ppvObject);
+
+  HWINEVENTHOOK win_event_hook_handle_;
+  static AccessibilityEventRecorderWin* instance_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccessibilityEventRecorderWin);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_WIN_H_
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc
index 18c9f7e..4229c81 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -2,13 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/accessibility/platform/inspect/ax_tree_formatter_base.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_win.h"
 
 #include <math.h>
-#include <oleacc.h>
 #include <stddef.h>
 #include <stdint.h>
-#include <wrl/client.h>
 
 #include <iostream>
 #include <string>
@@ -21,7 +19,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "base/win/com_init_util.h"
 #include "base/win/scoped_bstr.h"
 #include "base/win/scoped_variant.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h"
@@ -36,82 +33,6 @@
 
 namespace content {
 
-class AccessibilityTreeFormatterWin : public ui::AXTreeFormatterBase {
- public:
-  AccessibilityTreeFormatterWin();
-  ~AccessibilityTreeFormatterWin() override;
-
-  base::Value BuildTree(ui::AXPlatformNodeDelegate* start) const override;
-  base::Value BuildTreeForWindow(gfx::AcceleratedWidget hwnd) const override;
-  base::Value BuildTreeForSelector(
-      const AXTreeSelector& selector) const override;
-
- protected:
-  void AddDefaultFilters(
-      std::vector<AXPropertyFilter>* property_filters) override;
-
- private:
-  void RecursiveBuildTree(const Microsoft::WRL::ComPtr<IAccessible> node,
-                          base::DictionaryValue* dict,
-                          LONG root_x,
-                          LONG root_y) const;
-
-  void AddProperties(const Microsoft::WRL::ComPtr<IAccessible>,
-                     base::DictionaryValue* dict,
-                     LONG root_x,
-                     LONG root_y) const;
-  void AddMSAAProperties(const Microsoft::WRL::ComPtr<IAccessible>,
-                         base::DictionaryValue* dict,
-                         LONG root_x,
-                         LONG root_y) const;
-  void AddSimpleDOMNodeProperties(const Microsoft::WRL::ComPtr<IAccessible>,
-                                  base::DictionaryValue* dict) const;
-  bool AddIA2Properties(const Microsoft::WRL::ComPtr<IAccessible>,
-                        base::DictionaryValue* dict) const;
-  void AddIA2ActionProperties(const Microsoft::WRL::ComPtr<IAccessible>,
-                              base::DictionaryValue* dict) const;
-  void AddIA2HypertextProperties(const Microsoft::WRL::ComPtr<IAccessible>,
-                                 base::DictionaryValue* dict) const;
-  void AddIA2TextProperties(const Microsoft::WRL::ComPtr<IAccessible>,
-                            base::DictionaryValue* dict) const;
-  void AddIA2TableProperties(const Microsoft::WRL::ComPtr<IAccessible>,
-                             base::DictionaryValue* dict) const;
-  void AddIA2TableCellProperties(const Microsoft::WRL::ComPtr<IAccessible>,
-                                 base::DictionaryValue* dict) const;
-  void AddIA2ValueProperties(const Microsoft::WRL::ComPtr<IAccessible>,
-                             base::DictionaryValue* dict) const;
-  std::string ProcessTreeForOutput(
-      const base::DictionaryValue& node) const override;
-};
-
-// TODO(crbug.com/1133330): move implementation into
-// content/public/ax_inspect_factory.cc when AccessibilityTreeFormatterWin is
-// relocated under ui/accessibility/platform
-
-// static
-std::unique_ptr<ui::AXTreeFormatter>
-AXInspectFactory::CreatePlatformFormatter() {
-  return CreateFormatter(kWinIA2);
-}
-
-// static
-std::unique_ptr<ui::AXTreeFormatter> AXInspectFactory::CreateFormatter(
-    AXInspectFactory::Type type) {
-  switch (type) {
-    case kBlink:
-      return std::make_unique<AccessibilityTreeFormatterBlink>();
-    case kWinIA2:
-      base::win::AssertComInitialized();
-      return std::make_unique<AccessibilityTreeFormatterWin>();
-    case kWinUIA:
-      base::win::AssertComInitialized();
-      return std::make_unique<AccessibilityTreeFormatterUia>();
-    default:
-      NOTREACHED() << "Unsupported formatter type " << type;
-  }
-  return nullptr;
-}
-
 void AccessibilityTreeFormatterWin::AddDefaultFilters(
     std::vector<AXPropertyFilter>* property_filters) {
   // Too noisy: HOTTRACKED, LINKED, SELECTABLE, IA2_STATE_EDITABLE,
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.h b/content/browser/accessibility/accessibility_tree_formatter_win.h
new file mode 100644
index 0000000..4f5bdcba
--- /dev/null
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.h
@@ -0,0 +1,67 @@
+// Copyright 2021 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_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_WIN_H_
+#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_WIN_H_
+
+#include <oleacc.h>
+#include <wrl/client.h>
+
+#include "content/common/content_export.h"
+#include "ui/accessibility/platform/inspect/ax_tree_formatter_base.h"
+
+namespace content {
+
+class CONTENT_EXPORT AccessibilityTreeFormatterWin
+    : public ui::AXTreeFormatterBase {
+ public:
+  AccessibilityTreeFormatterWin();
+  ~AccessibilityTreeFormatterWin() override;
+
+  base::Value BuildTree(ui::AXPlatformNodeDelegate* start) const override;
+  base::Value BuildTreeForWindow(gfx::AcceleratedWidget hwnd) const override;
+  base::Value BuildTreeForSelector(
+      const AXTreeSelector& selector) const override;
+
+ protected:
+  void AddDefaultFilters(
+      std::vector<AXPropertyFilter>* property_filters) override;
+
+ private:
+  void RecursiveBuildTree(const Microsoft::WRL::ComPtr<IAccessible> node,
+                          base::DictionaryValue* dict,
+                          LONG root_x,
+                          LONG root_y) const;
+
+  void AddProperties(const Microsoft::WRL::ComPtr<IAccessible>,
+                     base::DictionaryValue* dict,
+                     LONG root_x,
+                     LONG root_y) const;
+  void AddMSAAProperties(const Microsoft::WRL::ComPtr<IAccessible>,
+                         base::DictionaryValue* dict,
+                         LONG root_x,
+                         LONG root_y) const;
+  void AddSimpleDOMNodeProperties(const Microsoft::WRL::ComPtr<IAccessible>,
+                                  base::DictionaryValue* dict) const;
+  bool AddIA2Properties(const Microsoft::WRL::ComPtr<IAccessible>,
+                        base::DictionaryValue* dict) const;
+  void AddIA2ActionProperties(const Microsoft::WRL::ComPtr<IAccessible>,
+                              base::DictionaryValue* dict) const;
+  void AddIA2HypertextProperties(const Microsoft::WRL::ComPtr<IAccessible>,
+                                 base::DictionaryValue* dict) const;
+  void AddIA2TextProperties(const Microsoft::WRL::ComPtr<IAccessible>,
+                            base::DictionaryValue* dict) const;
+  void AddIA2TableProperties(const Microsoft::WRL::ComPtr<IAccessible>,
+                             base::DictionaryValue* dict) const;
+  void AddIA2TableCellProperties(const Microsoft::WRL::ComPtr<IAccessible>,
+                                 base::DictionaryValue* dict) const;
+  void AddIA2ValueProperties(const Microsoft::WRL::ComPtr<IAccessible>,
+                             base::DictionaryValue* dict) const;
+  std::string ProcessTreeForOutput(
+      const base::DictionaryValue& node) const override;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_WIN_H_
diff --git a/content/browser/conversions/conversion_storage_sql.cc b/content/browser/conversions/conversion_storage_sql.cc
index 3eebbf8..96e2f1e 100644
--- a/content/browser/conversions/conversion_storage_sql.cc
+++ b/content/browser/conversions/conversion_storage_sql.cc
@@ -76,6 +76,11 @@
 // currently deprecated.
 const int kDeprecatedVersionNumber = 0;
 
+void RecordInitializationStatus(const ConversionStorageSql::InitStatus status) {
+  base::UmaHistogramEnumeration("Conversions.Storage.Sql.InitStatus", status,
+                                ConversionStorageSql::InitStatus::kMaxValue);
+}
+
 }  // namespace
 
 // static
@@ -671,6 +676,13 @@
   return impressions;
 }
 
+void ConversionStorageSql::HandleInitializationFailure(
+    const InitStatus status) {
+  RecordInitializationStatus(status);
+  db_.reset();
+  db_init_status_ = DbStatus::kClosed;
+}
+
 bool ConversionStorageSql::LazyInit(DbCreationPolicy creation_policy) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!db_init_status_) {
@@ -709,25 +721,34 @@
       base::BindRepeating(&ConversionStorageSql::DatabaseErrorCallback,
                           weak_factory_.GetWeakPtr()));
 
-  const base::FilePath& dir = path_to_database_.DirName();
-  bool opened = false;
   if (path_to_database_.value() == kInMemoryPath) {
-    opened = db_->OpenInMemory();
-  } else if (base::DirectoryExists(dir) || base::CreateDirectory(dir)) {
-    opened = db_->Open(path_to_database_);
+    if (!db_->OpenInMemory()) {
+      HandleInitializationFailure(InitStatus::kFailedToOpenDbInMemory);
+      return false;
+    }
   } else {
-    DLOG(ERROR) << "Failed to create directory for Conversion database";
-    opened = false;
+    const base::FilePath& dir = path_to_database_.DirName();
+    const bool dir_exists_or_was_created =
+        base::DirectoryExists(dir) || base::CreateDirectory(dir);
+    if (dir_exists_or_was_created == false) {
+      DLOG(ERROR) << "Failed to create directory for Conversion database";
+      HandleInitializationFailure(InitStatus::kFailedToCreateDir);
+      return false;
+    }
+    if (db_->Open(path_to_database_) == false) {
+      HandleInitializationFailure(InitStatus::kFailedToOpenDbFile);
+      return false;
+    }
   }
 
-  if (!opened ||
-      !InitializeSchema(db_init_status_ == DbStatus::kDeferringCreation)) {
-    db_.reset();
-    db_init_status_ = DbStatus::kClosed;
+  if (InitializeSchema(db_init_status_ == DbStatus::kDeferringCreation) ==
+      false) {
+    HandleInitializationFailure(InitStatus::kFailedToInitializeSchema);
     return false;
   }
 
   db_init_status_ = DbStatus::kOpen;
+  RecordInitializationStatus(InitStatus::kSuccess);
   return true;
 }
 bool ConversionStorageSql::InitializeSchema(bool db_empty) {
diff --git a/content/browser/conversions/conversion_storage_sql.h b/content/browser/conversions/conversion_storage_sql.h
index 1486ec72..5517dbc 100644
--- a/content/browser/conversions/conversion_storage_sql.h
+++ b/content/browser/conversions/conversion_storage_sql.h
@@ -42,6 +42,17 @@
     ignore_errors_for_testing_ = ignore_for_testing;
   }
 
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class InitStatus {
+    kSuccess = 0,
+    kFailedToOpenDbInMemory = 1,
+    kFailedToOpenDbFile = 2,
+    kFailedToCreateDir = 3,
+    kFailedToInitializeSchema = 4,
+    kMaxValue = kFailedToInitializeSchema,
+  };
+
  private:
   friend class ConversionStorageSqlMigrations;
 
@@ -103,6 +114,7 @@
   bool LazyInit(DbCreationPolicy creation_policy);
   bool InitializeSchema(bool db_empty);
   bool CreateSchema();
+  void HandleInitializationFailure(const InitStatus status);
 
   void DatabaseErrorCallback(int extended_error, sql::Statement* stmt);
 
diff --git a/content/browser/conversions/conversion_storage_sql_unittest.cc b/content/browser/conversions/conversion_storage_sql_unittest.cc
index b6296cc..5fb02fb 100644
--- a/content/browser/conversions/conversion_storage_sql_unittest.cc
+++ b/content/browser/conversions/conversion_storage_sql_unittest.cc
@@ -321,4 +321,15 @@
             storage->MaybeCreateAndStoreConversionReports(DefaultConversion()));
 }
 
+TEST_F(ConversionStorageSqlTest, DBinitializationSucceeds_HistogramRecorded) {
+  base::HistogramTester histograms;
+
+  OpenDatabase();
+  storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
+  CloseDatabase();
+
+  histograms.ExpectUniqueSample("Conversions.Storage.Sql.InitStatus",
+                                ConversionStorageSql::InitStatus::kSuccess, 1);
+}
+
 }  // namespace content
diff --git a/content/browser/devtools/protocol/emulation_handler.cc b/content/browser/devtools/protocol/emulation_handler.cc
index 0b2a30a..7c201d0f 100644
--- a/content/browser/devtools/protocol/emulation_handler.cc
+++ b/content/browser/devtools/protocol/emulation_handler.cc
@@ -15,6 +15,7 @@
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/common/content_client.h"
 #include "content/public/common/url_constants.h"
 #include "net/http/http_util.h"
 #include "services/device/public/cpp/geolocation/geoposition.h"
@@ -424,24 +425,35 @@
   std::unique_ptr<Emulation::UserAgentMetadata> ua_metadata =
       ua_metadata_override.takeJust();
   blink::UserAgentMetadata new_ua_metadata;
-  DCHECK(ua_metadata->GetBrands());
+  blink::UserAgentMetadata default_ua_metadata =
+      GetContentClient()->browser()->GetUserAgentMetadata();
 
-  for (const auto& bv : *ua_metadata->GetBrands()) {
-    blink::UserAgentBrandVersion out_bv;
-    if (!ValidateClientHintString(bv->GetBrand()))
-      return Response::InvalidParams("Invalid brand string");
-    out_bv.brand = bv->GetBrand();
+  if (ua_metadata->HasBrands()) {
+    for (const auto& bv : *ua_metadata->GetBrands(nullptr)) {
+      blink::UserAgentBrandVersion out_bv;
+      if (!ValidateClientHintString(bv->GetBrand()))
+        return Response::InvalidParams("Invalid brand string");
+      out_bv.brand = bv->GetBrand();
 
-    if (!ValidateClientHintString(bv->GetVersion()))
-      return Response::InvalidParams("Invalid brand version string");
-    out_bv.major_version = bv->GetVersion();
+      if (!ValidateClientHintString(bv->GetVersion()))
+        return Response::InvalidParams("Invalid brand version string");
+      out_bv.major_version = bv->GetVersion();
 
-    new_ua_metadata.brand_version_list.push_back(std::move(out_bv));
+      new_ua_metadata.brand_version_list.push_back(std::move(out_bv));
+    }
+  } else {
+    new_ua_metadata.brand_version_list =
+        std::move(default_ua_metadata.brand_version_list);
   }
 
-  if (!ValidateClientHintString(ua_metadata->GetFullVersion()))
-    return Response::InvalidParams("Invalid full version string");
-  new_ua_metadata.full_version = ua_metadata->GetFullVersion();
+  if (ua_metadata->HasFullVersion()) {
+    String full_version = ua_metadata->GetFullVersion("");
+    if (!ValidateClientHintString(full_version))
+      return Response::InvalidParams("Invalid full version string");
+    new_ua_metadata.full_version = full_version;
+  } else {
+    new_ua_metadata.full_version = std::move(default_ua_metadata.full_version);
+  }
 
   if (!ValidateClientHintString(ua_metadata->GetPlatform()))
     return Response::InvalidParams("Invalid platform string");
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.cc b/content/browser/file_system_access/file_system_access_manager_impl.cc
index 219ec125..ddffad28 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl.cc
+++ b/content/browser/file_system_access/file_system_access_manager_impl.cc
@@ -282,7 +282,7 @@
 void FileSystemAccessManagerImpl::ChooseEntries(
     blink::mojom::ChooseFileSystemEntryType type,
     std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
-    blink::mojom::CommonDirectory starting_directory,
+    blink::mojom::WellKnownDirectory well_known_starting_directory,
     bool include_accepts_all,
     ChooseEntriesCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -332,12 +332,14 @@
 
   PathInfo path_info;
   if (permission_context_) {
-    if (starting_directory != blink::mojom::CommonDirectory::kDefault) {
+    if (well_known_starting_directory !=
+        blink::mojom::WellKnownDirectory::kDefault) {
       // Priotitize an explicitly stated starting directory over an implicitly
       // remembered LastPicked directory.
-      path_info.path =
-          permission_context_->GetCommonDirectoryPath(starting_directory);
-    } else { /*starting_directory == blink::mojom::CommonDirectory::kDefault*/
+      path_info.path = permission_context_->GetWellKnownDirectoryPath(
+          well_known_starting_directory);
+    } else { /*well_known_starting_directory ==
+                blink::mojom::WellKnownDirectory::kDefault*/
       path_info = permission_context_->GetLastPickedDirectory(context.origin);
     }
   }
@@ -370,8 +372,8 @@
   if (result != base::File::Error::FILE_OK) {
     // |path| does not exist. Resort to the default.
     if (permission_context_)
-      default_directory = permission_context_->GetCommonDirectoryPath(
-          blink::mojom::CommonDirectory::kDefault);
+      default_directory = permission_context_->GetWellKnownDirectoryPath(
+          blink::mojom::WellKnownDirectory::kDefault);
   }
 
   // TODO(https://crbug.com/1019408): Append suggested filename to
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.h b/content/browser/file_system_access/file_system_access_manager_impl.h
index 0725b8a0..ab4618d 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl.h
+++ b/content/browser/file_system_access/file_system_access_manager_impl.h
@@ -104,7 +104,7 @@
   void ChooseEntries(
       blink::mojom::ChooseFileSystemEntryType type,
       std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
-      blink::mojom::CommonDirectory starting_directory,
+      blink::mojom::WellKnownDirectory well_known_starting_directory,
       bool include_accepts_all,
       ChooseEntriesCallback callback) override;
   void GetFileHandleFromToken(
diff --git a/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc b/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
index f67f0ba..c7d944e 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
+++ b/content/browser/file_system_access/file_system_access_manager_impl_unittest.cc
@@ -1123,8 +1123,9 @@
   EXPECT_CALL(permission_context_, CanObtainReadPermission(kTestOrigin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(permission_context_,
-              GetCommonDirectoryPath(blink::mojom::CommonDirectory::kDefault))
+  EXPECT_CALL(
+      permission_context_,
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context_, GetLastPickedDirectory(kTestOrigin))
       .WillOnce(testing::Return(PathInfo()));
@@ -1157,7 +1158,7 @@
   base::RunLoop loop;
   manager_remote->ChooseEntries(
       blink::mojom::ChooseFileSystemEntryType::kOpenFile, /*accepts=*/{},
-      blink::mojom::CommonDirectory::kDefault, /*include_accepts_all=*/true,
+      blink::mojom::WellKnownDirectory::kDefault, /*include_accepts_all=*/true,
       base::BindLambdaForTesting(
           [&](blink::mojom::FileSystemAccessErrorPtr result,
               std::vector<blink::mojom::FileSystemAccessEntryPtr> entries) {
@@ -1190,8 +1191,9 @@
   EXPECT_CALL(permission_context_, CanObtainWritePermission(kTestOrigin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(permission_context_,
-              GetCommonDirectoryPath(blink::mojom::CommonDirectory::kDefault))
+  EXPECT_CALL(
+      permission_context_,
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context_, GetLastPickedDirectory(kTestOrigin))
       .WillOnce(testing::Return(PathInfo()));
@@ -1224,7 +1226,7 @@
   base::RunLoop loop;
   manager_remote->ChooseEntries(
       blink::mojom::ChooseFileSystemEntryType::kSaveFile, /*accepts=*/{},
-      blink::mojom::CommonDirectory::kDefault, /*include_accepts_all=*/true,
+      blink::mojom::WellKnownDirectory::kDefault, /*include_accepts_all=*/true,
       base::BindLambdaForTesting(
           [&](blink::mojom::FileSystemAccessErrorPtr result,
               std::vector<blink::mojom::FileSystemAccessEntryPtr> entries) {
@@ -1254,8 +1256,9 @@
   EXPECT_CALL(permission_context_, CanObtainReadPermission(kTestOrigin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(permission_context_,
-              GetCommonDirectoryPath(blink::mojom::CommonDirectory::kDefault))
+  EXPECT_CALL(
+      permission_context_,
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context_, GetLastPickedDirectory(kTestOrigin))
       .WillOnce(testing::Return(PathInfo()));
@@ -1287,7 +1290,7 @@
   base::RunLoop loop;
   manager_remote->ChooseEntries(
       blink::mojom::ChooseFileSystemEntryType::kOpenDirectory, {},
-      blink::mojom::CommonDirectory::kDefault, true,
+      blink::mojom::WellKnownDirectory::kDefault, true,
       base::BindLambdaForTesting(
           [&](blink::mojom::FileSystemAccessErrorPtr result,
               std::vector<blink::mojom::FileSystemAccessEntryPtr> entries) {
diff --git a/content/browser/file_system_access/file_system_chooser_browsertest.cc b/content/browser/file_system_access/file_system_chooser_browsertest.cc
index a1f9058a..566d9f99 100644
--- a/content/browser/file_system_access/file_system_chooser_browsertest.cc
+++ b/content/browser/file_system_access/file_system_chooser_browsertest.cc
@@ -486,8 +486,9 @@
   EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(permission_context,
-              GetCommonDirectoryPath(blink::mojom::CommonDirectory::kDefault))
+  EXPECT_CALL(
+      permission_context,
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
       .WillOnce(testing::Return(PathInfo()));
@@ -560,8 +561,9 @@
   EXPECT_CALL(permission_context, CanObtainWritePermission(origin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(permission_context,
-              GetCommonDirectoryPath(blink::mojom::CommonDirectory::kDefault))
+  EXPECT_CALL(
+      permission_context,
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
       .WillOnce(testing::Return(PathInfo()));
@@ -621,8 +623,9 @@
   EXPECT_CALL(permission_context, CanObtainWritePermission(origin))
       .WillOnce(testing::Return(true));
 
-  EXPECT_CALL(permission_context,
-              GetCommonDirectoryPath(blink::mojom::CommonDirectory::kDefault))
+  EXPECT_CALL(
+      permission_context,
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(base::FilePath()));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
       .WillOnce(testing::Return(PathInfo()));
@@ -825,8 +828,9 @@
   base::FilePath default_dir;
   default_dir = temp_dir_.GetPath().AppendASCII("default");
 
-  EXPECT_CALL(permission_context,
-              GetCommonDirectoryPath(blink::mojom::CommonDirectory::kDefault))
+  EXPECT_CALL(
+      permission_context,
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(default_dir));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
       .WillOnce(testing::Return(bad_dir_info));
@@ -996,8 +1000,9 @@
   base::FilePath default_dir;
   default_dir = temp_dir_.GetPath().AppendASCII("default");
 
-  EXPECT_CALL(permission_context,
-              GetCommonDirectoryPath(blink::mojom::CommonDirectory::kDefault))
+  EXPECT_CALL(
+      permission_context,
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDefault))
       .WillOnce(testing::Return(default_dir));
   EXPECT_CALL(permission_context, GetLastPickedDirectory(origin))
       .WillOnce(testing::Return(bad_dir_info));
@@ -1047,7 +1052,8 @@
   EXPECT_EQ(default_dir, dialog_params.default_path);
 }
 
-IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, StartInCommonDirectory) {
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
+                       StartInWellKnownDirectory) {
   base::FilePath test_dir = CreateTestDir();
 
   SelectFileDialogParams dialog_params;
@@ -1083,10 +1089,11 @@
         temp_dir_.GetPath(), FILE_PATH_LITERAL("Desktop"), &desktop_dir));
   }
 
-  // Starting directory specified, so do not call GetLastPickedDirectory.
+  // Well-known starting directory specified, so do not call
+  // GetLastPickedDirectory.
   EXPECT_CALL(
       permission_context,
-      GetCommonDirectoryPath(blink::mojom::CommonDirectory::kDirDesktop))
+      GetWellKnownDirectoryPath(blink::mojom::WellKnownDirectory::kDirDesktop))
       .WillOnce(testing::Return(desktop_dir));
   EXPECT_CALL(permission_context,
               SetLastPickedDirectory(origin, test_dir, PathType::kLocal));
@@ -1136,7 +1143,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
-                       StartInCommonDirectory_Nulled) {
+                       StartInWellKnownDirectory_Nulled) {
   SelectFileDialogParams dialog_params;
   ui::SelectFileDialog::SetFactory(
       new CancellingSelectFileDialogFactory(&dialog_params));
diff --git a/content/browser/file_system_access/mock_file_system_access_permission_context.h b/content/browser/file_system_access/mock_file_system_access_permission_context.h
index 355e9cb..c2ba5e3 100644
--- a/content/browser/file_system_access/mock_file_system_access_permission_context.h
+++ b/content/browser/file_system_access/mock_file_system_access_permission_context.h
@@ -80,8 +80,8 @@
               (override));
 
   MOCK_METHOD(base::FilePath,
-              GetCommonDirectoryPath,
-              (blink::mojom::CommonDirectory directory),
+              GetWellKnownDirectoryPath,
+              (blink::mojom::WellKnownDirectory directory),
               (override));
 };
 
diff --git a/content/browser/notifications/platform_notification_context_impl.cc b/content/browser/notifications/platform_notification_context_impl.cc
index 15ba957..7fd728b 100644
--- a/content/browser/notifications/platform_notification_context_impl.cc
+++ b/content/browser/notifications/platform_notification_context_impl.cc
@@ -39,6 +39,11 @@
 const base::FilePath::CharType kPlatformNotificationsDirectory[] =
     FILE_PATH_LITERAL("Platform Notifications");
 
+// Max age of a displayed notification before we consider it stale and remove it
+// from the database and ask the platform to close it.
+constexpr base::TimeDelta kMaxDisplayedNotificationAge =
+    base::TimeDelta::FromDays(7);
+
 // Checks if this notification can trigger in the future.
 bool CanTrigger(const NotificationDatabaseData& data) {
   if (!base::FeatureList::IsEnabled(features::kNotificationTriggers))
@@ -165,25 +170,14 @@
   if (has_shutdown_)
     return;
 
-  // Check if there are pending notifications to display.
-  base::Time next_trigger = base::Time::Max();
-  if (service_proxy_ &&
-      base::FeatureList::IsEnabled(features::kNotificationTriggers)) {
-    next_trigger = service_proxy_->GetNextTrigger();
-  }
-
   // Synchronize the notifications stored in the database with the set of
   // displaying notifications in |displayed_notifications|. This is necessary
   // because flakiness may cause a platform to inform Chrome of a notification
   // that has since been closed, or because the platform does not support
   // notifications that exceed the lifetime of the browser process.
-  if (supports_synchronization || next_trigger <= base::Time::Now()) {
-    LazyInitialize(base::BindOnce(
-        &PlatformNotificationContextImpl::DoSyncNotificationData, this,
-        supports_synchronization, std::move(displayed_notifications)));
-  } else if (service_proxy_ && next_trigger != base::Time::Max()) {
-    service_proxy_->ScheduleTrigger(next_trigger);
-  }
+  LazyInitialize(base::BindOnce(
+      &PlatformNotificationContextImpl::DoSyncNotificationData, this,
+      supports_synchronization, std::move(displayed_notifications)));
 
   // |service_worker_context_| may be NULL in tests.
   if (service_worker_context_)
@@ -202,25 +196,40 @@
   next_trigger_ = base::nullopt;
 
   // Iterate over all notifications and delete all expired ones.
+  std::set<std::string> close_notification_ids;
   NotificationDatabase::Status status =
       database_->ForEachNotificationData(base::BindRepeating(
           &PlatformNotificationContextImpl::DoHandleSyncNotification, this,
-          supports_synchronization, displayed_notifications));
+          supports_synchronization, displayed_notifications,
+          &close_notification_ids));
 
   // Blow away the database if reading data failed due to corruption.
   if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
     DestroyDatabase();
 
-  // Schedule the next trigger timestamp.
-  if (next_trigger_ && service_proxy_)
-    service_proxy_->ScheduleTrigger(next_trigger_.value());
+  base::UmaHistogramCounts10000(
+      "Notifications.Database.ExpiredNotificationCount",
+      close_notification_ids.size());
+
+  if (service_proxy_) {
+    // Schedule the next trigger timestamp.
+    if (next_trigger_)
+      service_proxy_->ScheduleTrigger(next_trigger_.value());
+
+    // Close old notifications.
+    if (!close_notification_ids.empty())
+      service_proxy_->CloseNotifications(close_notification_ids);
+  }
 }
 
 void PlatformNotificationContextImpl::DoHandleSyncNotification(
     bool supports_synchronization,
     const std::set<std::string>& displayed_notifications,
+    std::set<std::string>* close_notification_ids,
     const NotificationDatabaseData& data) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(close_notification_ids);
+
   // Handle pending notifications.
   if (CanTrigger(data)) {
     base::Time timestamp =
@@ -233,6 +242,19 @@
     return;
   }
 
+  // Delete very old notifications as they are most probably not on screen
+  // anymore and their relevance is questionable anyway. We still want to tell
+  // the platform to remove them for cleanup just in case.
+  base::Time display_time =
+      data.notification_data.show_trigger_timestamp.value_or(
+          data.creation_time_millis);
+  base::TimeDelta age = base::Time::Now() - display_time;
+  if (age >= kMaxDisplayedNotificationAge) {
+    database_->DeleteNotificationData(data.notification_id, data.origin);
+    close_notification_ids->insert(data.notification_id);
+    return;
+  }
+
   // Do not delete notifications if the platform does not support syncing them.
   if (!supports_synchronization)
     return;
@@ -388,10 +410,8 @@
     success = true;
   }
 
-  if (service_proxy_) {
-    for (const std::string& notification_id : deleted_notification_ids)
-      service_proxy_->CloseNotification(notification_id);
-  }
+  if (service_proxy_ && !deleted_notification_ids.empty())
+    service_proxy_->CloseNotifications(deleted_notification_ids);
 
   GetUIThreadTaskRunner({})->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), success,
@@ -966,7 +986,7 @@
   if (status == NotificationDatabase::STATUS_OK) {
     if (CanTrigger(write_database_data)) {
       if (replaces_existing)
-        service_proxy_->CloseNotification(notification_id);
+        service_proxy_->CloseNotifications({notification_id});
 
       // Schedule notification to be shown.
       service_proxy_->ScheduleNotification(std::move(write_database_data));
@@ -1006,7 +1026,7 @@
 
   // Close notification as we're about to delete its data.
   if (close_notification)
-    service_proxy_->CloseNotification(notification_id);
+    service_proxy_->CloseNotifications({notification_id});
 
   bool should_log_close = service_proxy_->ShouldLogClose(origin);
   LazyInitialize(base::BindOnce(
@@ -1088,10 +1108,8 @@
   if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
     DestroyDatabase();
 
-  if (service_proxy_) {
-    for (const std::string& notification_id : deleted_notification_ids)
-      service_proxy_->CloseNotification(notification_id);
-  }
+  if (service_proxy_ && !deleted_notification_ids.empty())
+    service_proxy_->CloseNotifications(deleted_notification_ids);
 }
 
 void PlatformNotificationContextImpl::OnStorageWiped() {
diff --git a/content/browser/notifications/platform_notification_context_impl.h b/content/browser/notifications/platform_notification_context_impl.h
index f9b6a18a..931ace4 100644
--- a/content/browser/notifications/platform_notification_context_impl.h
+++ b/content/browser/notifications/platform_notification_context_impl.h
@@ -182,10 +182,12 @@
                               bool initialized);
 
   // Checks if the given notification is still valid, otherwise deletes it from
-  // the database.
+  // the database. Fills |close_notification_ids| with notification ids that
+  // should be closed by the platform.
   void DoHandleSyncNotification(
       bool supports_synchronization,
       const std::set<std::string>& displayed_notifications,
+      std::set<std::string>* close_notification_ids,
       const NotificationDatabaseData& data);
 
   // Tries to get a list of displayed notification ids if the platform supports
diff --git a/content/browser/notifications/platform_notification_context_unittest.cc b/content/browser/notifications/platform_notification_context_unittest.cc
index a578b370..f1bb69e 100644
--- a/content/browser/notifications/platform_notification_context_unittest.cc
+++ b/content/browser/notifications/platform_notification_context_unittest.cc
@@ -782,6 +782,58 @@
   EXPECT_FALSE(success());
 }
 
+TEST_F(PlatformNotificationContextTest, DeleteOldNotifications) {
+  base::HistogramTester histogram_tester;
+  NotificationBrowserClient notification_browser_client(browser_context());
+  SetBrowserClientForTesting(&notification_browser_client);
+  scoped_refptr<PlatformNotificationContextImpl> context =
+      CreatePlatformNotificationContext();
+  PlatformNotificationService* service =
+      notification_browser_client.GetPlatformNotificationService(
+          browser_context());
+
+  // Let PlatformNotificationContext synchronize displayed notifications.
+  base::RunLoop().RunUntilIdle();
+
+  // Write a notification to the database.
+  GURL origin("https://example.com");
+  NotificationDatabaseData data;
+  data.service_worker_registration_id = kFakeServiceWorkerRegistrationId;
+  WriteNotificationDataSync(context.get(), origin, data);
+
+  // Let some time pass but not enough to delete the notification yet.
+  task_environment_.FastForwardBy(base::TimeDelta::FromDays(5));
+  context->TriggerNotifications();
+  // Allow for closing notifications on the UI thread.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1u, GetDisplayedNotificationsSync(service).size());
+  ASSERT_EQ(1u, GetStoredNotificationsSync(context.get(), origin).size());
+
+  // Add another notification now to verify it won't get cleaned up too early.
+  NotificationDatabaseData data_2;
+  data_2.service_worker_registration_id = kFakeServiceWorkerRegistrationId;
+  std::string notification_id =
+      WriteNotificationDataSync(context.get(), origin, data_2);
+  EXPECT_EQ(2u, GetDisplayedNotificationsSync(service).size());
+  ASSERT_EQ(2u, GetStoredNotificationsSync(context.get(), origin).size());
+
+  // Let some more time pass so the first notification is not considered new
+  // anymore and should get closed while the second one should stay.
+  task_environment_.FastForwardBy(base::TimeDelta::FromDays(2));
+  context->TriggerNotifications();
+  // Allow for closing notifications on the UI thread.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1u, GetDisplayedNotificationsSync(service).size());
+  std::vector<NotificationDatabaseData> notification_database_datas =
+      GetStoredNotificationsSync(context.get(), origin);
+  ASSERT_EQ(1u, notification_database_datas.size());
+  EXPECT_EQ(notification_id, notification_database_datas[0].notification_id);
+
+  histogram_tester.ExpectBucketCount(
+      "Notifications.Database.ExpiredNotificationCount",
+      /*sample=*/1, /*expected_count=*/1);
+}
+
 TEST_F(PlatformNotificationContextTest, WriteDisplaysNotification) {
   NotificationBrowserClient notification_browser_client(browser_context());
   SetBrowserClientForTesting(&notification_browser_client);
diff --git a/content/browser/notifications/platform_notification_service_proxy.cc b/content/browser/notifications/platform_notification_service_proxy.cc
index fa63220..e71465be 100644
--- a/content/browser/notifications/platform_notification_service_proxy.cc
+++ b/content/browser/notifications/platform_notification_service_proxy.cc
@@ -98,21 +98,22 @@
               weak_ptr_factory_io_.GetWeakPtr(), data, std::move(callback))));
 }
 
-void PlatformNotificationServiceProxy::CloseNotification(
-    const std::string& notification_id) {
+void PlatformNotificationServiceProxy::CloseNotifications(
+    const std::set<std::string>& notification_ids) {
   if (!notification_service_)
     return;
   GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE})
-      ->PostTask(
-          FROM_HERE,
-          base::BindOnce(&PlatformNotificationServiceProxy::DoCloseNotification,
-                         AsWeakPtr(), notification_id));
+      ->PostTask(FROM_HERE,
+                 base::BindOnce(
+                     &PlatformNotificationServiceProxy::DoCloseNotifications,
+                     AsWeakPtr(), notification_ids));
 }
 
-void PlatformNotificationServiceProxy::DoCloseNotification(
-    const std::string& notification_id) {
+void PlatformNotificationServiceProxy::DoCloseNotifications(
+    const std::set<std::string>& notification_ids) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  notification_service_->ClosePersistentNotification(notification_id);
+  for (const std::string& notification_id : notification_ids)
+    notification_service_->ClosePersistentNotification(notification_id);
 }
 
 void PlatformNotificationServiceProxy::ScheduleTrigger(base::Time timestamp) {
diff --git a/content/browser/notifications/platform_notification_service_proxy.h b/content/browser/notifications/platform_notification_service_proxy.h
index 70d20ba..57ea2b7 100644
--- a/content/browser/notifications/platform_notification_service_proxy.h
+++ b/content/browser/notifications/platform_notification_service_proxy.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_NOTIFICATIONS_PLATFORM_NOTIFICATION_SERVICE_PROXY_H_
 
 #include <memory>
+#include <set>
 #include <string>
 
 #include "base/callback_forward.h"
@@ -52,8 +53,8 @@
   void DisplayNotification(const NotificationDatabaseData& data,
                            DisplayResultCallback callback);
 
-  // Closes the notification with |notification_id|.
-  void CloseNotification(const std::string& notification_id);
+  // Closes the notifications with |notification_ids|.
+  void CloseNotifications(const std::set<std::string>& notification_ids);
 
   // Schedules a notification trigger for |timestamp|.
   void ScheduleTrigger(base::Time timestamp);
@@ -82,9 +83,9 @@
                              const GURL& service_worker_scope,
                              DisplayResultCallback callback);
 
-  // Actually closes the notification with |notification_id|. Must be called on
-  // the UI thread.
-  void DoCloseNotification(const std::string& notification_id);
+  // Actually closes the notifications with |notification_ids|. Must be called
+  // on the UI thread.
+  void DoCloseNotifications(const std::set<std::string>& notification_ids);
 
   // Actually calls |notification_service_| to schedule a trigger. Must be
   // called on the UI thread.
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index 021b881a..269a986 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -5928,10 +5928,17 @@
   }
 }
 
+#if defined(OS_ANDROID)
+#define MAYBE_FrameNavigationEntry_MainFrameRedirectChain_NormalThenCrossSiteNavigations \
+  DISABLED_FrameNavigationEntry_MainFrameRedirectChain_NormalThenCrossSiteNavigations
+#else
+#define MAYBE_FrameNavigationEntry_MainFrameRedirectChain_NormalThenCrossSiteNavigations \
+  FrameNavigationEntry_MainFrameRedirectChain_NormalThenCrossSiteNavigations
+#endif
 // Checks the contents of the redirect chain after cross-site navigations.
 IN_PROC_BROWSER_TEST_P(
     NavigationControllerBrowserTest,
-    FrameNavigationEntry_MainFrameRedirectChain_NormalThenCrossSiteNavigations) {
+    MAYBE_FrameNavigationEntry_MainFrameRedirectChain_NormalThenCrossSiteNavigations) {
   NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
       shell()->web_contents()->GetController());
 
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 50aa20c..d1be1e49 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -31,11 +31,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
-#include "content/browser/cache_storage/cache_storage.h"
-#include "content/browser/cache_storage/cache_storage_cache.h"
-#include "content/browser/cache_storage/cache_storage_cache_handle.h"
-#include "content/browser/cache_storage/cache_storage_context_impl.h"
-#include "content/browser/cache_storage/cache_storage_manager.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/renderer_host/code_cache_host_impl.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
@@ -2115,31 +2110,36 @@
 class CacheStorageSideDataSizeChecker
     : public base::RefCountedThreadSafe<CacheStorageSideDataSizeChecker> {
  public:
-  static int GetSize(CacheStorageContextImpl* cache_storage_context,
+  static int GetSize(storage::mojom::CacheStorageControl* cache_storage_control,
                      const GURL& origin,
                      const std::string& cache_name,
                      const GURL& url) {
-    scoped_refptr<CacheStorageSideDataSizeChecker> checker(
-        new CacheStorageSideDataSizeChecker(cache_storage_context, origin,
-                                            cache_name, url));
+    mojo::PendingRemote<blink::mojom::CacheStorage> cache_storage_remote;
+    network::CrossOriginEmbedderPolicy cross_origin_embedder_policy;
+    cache_storage_control->AddReceiver(
+        cross_origin_embedder_policy, mojo::NullRemote(),
+        url::Origin::Create(origin),
+        storage::mojom::CacheStorageOwner::kCacheAPI,
+        cache_storage_remote.InitWithNewPipeAndPassReceiver());
+
+    auto checker = base::MakeRefCounted<CacheStorageSideDataSizeChecker>(
+        std::move(cache_storage_remote), cache_name, url);
     return checker->GetSizeImpl();
   }
 
+  CacheStorageSideDataSizeChecker(
+      mojo::PendingRemote<blink::mojom::CacheStorage> cache_storage,
+      const std::string& cache_name,
+      const GURL& url)
+      : cache_storage_(std::move(cache_storage)),
+        cache_name_(cache_name),
+        url_(url) {}
+
  private:
   using self = CacheStorageSideDataSizeChecker;
   friend class base::RefCountedThreadSafe<self>;
 
-  CacheStorageSideDataSizeChecker(
-      CacheStorageContextImpl* cache_storage_context,
-      const GURL& origin,
-      const std::string& cache_name,
-      const GURL& url)
-      : cache_storage_context_(cache_storage_context),
-        origin_(origin),
-        cache_name_(cache_name),
-        url_(url) {}
-
-  ~CacheStorageSideDataSizeChecker() {}
+  virtual ~CacheStorageSideDataSizeChecker() = default;
 
   int GetSizeImpl() {
     int result = 0;
@@ -2149,45 +2149,45 @@
   }
 
   void OpenCacheOnCoreThread(int* result, base::OnceClosure continuation) {
-    CacheStorageHandle cache_storage =
-        cache_storage_context_->CacheManager()->OpenCacheStorage(
-            url::Origin::Create(origin_),
-            storage::mojom::CacheStorageOwner::kCacheAPI);
-    cache_storage.value()->OpenCache(
-        cache_name_, /* trace_id = */ 0,
-        base::BindOnce(&self::OnCacheStorageOpenCallback, this, result,
-                       std::move(continuation)));
+    cache_storage_->Open(base::UTF8ToUTF16(cache_name_), /*trace_id=*/0,
+                         base::BindOnce(&self::OnCacheStorageOpenCallback, this,
+                                        result, std::move(continuation)));
   }
 
   void OnCacheStorageOpenCallback(int* result,
                                   base::OnceClosure continuation,
-                                  CacheStorageCacheHandle cache_handle,
-                                  CacheStorageError error) {
-    ASSERT_EQ(CacheStorageError::kSuccess, error);
+                                  blink::mojom::OpenResultPtr open_result) {
+    ASSERT_TRUE(open_result->is_cache());
+
     auto scoped_request = blink::mojom::FetchAPIRequest::New();
     scoped_request->url = url_;
-    CacheStorageCache* cache = cache_handle.value();
-    cache->Match(
-        std::move(scoped_request), nullptr,
-        CacheStorageSchedulerPriority::kNormal, /* trace_id = */ 0,
-        base::BindOnce(&self::OnCacheStorageCacheMatchCallback, this, result,
-                       std::move(continuation), std::move(cache_handle)));
+
+    // Preserve lifetime of this remote across the Match call.
+    cache_storage_cache_.emplace(std::move(open_result->get_cache()));
+
+    (*cache_storage_cache_)
+        ->Match(std::move(scoped_request),
+                blink::mojom::CacheQueryOptions::New(),
+                /*in_related_fetch_event=*/false, /*trace_id=*/0,
+                base::BindOnce(&self::OnCacheStorageCacheMatchCallback, this,
+                               result, std::move(continuation)));
   }
 
   void OnCacheStorageCacheMatchCallback(
       int* result,
       base::OnceClosure continuation,
-      CacheStorageCacheHandle cache_handle,
-      CacheStorageError error,
-      blink::mojom::FetchAPIResponsePtr response) {
-    if (error == CacheStorageError::kErrorNotFound) {
+      blink::mojom::MatchResultPtr match_result) {
+    if (match_result->is_status()) {
+      ASSERT_EQ(match_result->get_status(), CacheStorageError::kErrorNotFound);
       *result = 0;
       std::move(continuation).Run();
       return;
     }
+    ASSERT_TRUE(match_result->is_response());
 
-    ASSERT_EQ(CacheStorageError::kSuccess, error);
+    auto& response = match_result->get_response();
     ASSERT_TRUE(response->side_data_blob);
+
     auto blob_handle = base::MakeRefCounted<storage::BlobHandle>(
         std::move(response->side_data_blob->blob));
     blob_handle->get()->ReadSideData(base::BindOnce(
@@ -2200,8 +2200,9 @@
         blob_handle, result, std::move(continuation)));
   }
 
-  CacheStorageContextImpl* cache_storage_context_;
-  const GURL origin_;
+  mojo::Remote<blink::mojom::CacheStorage> cache_storage_;
+  base::Optional<mojo::AssociatedRemote<blink::mojom::CacheStorageCache>>
+      cache_storage_cache_;
   const std::string cache_name_;
   const GURL url_;
   DISALLOW_COPY_AND_ASSIGN(CacheStorageSideDataSizeChecker);
@@ -2267,10 +2268,8 @@
     StoragePartition* partition = BrowserContext::GetDefaultStoragePartition(
         shell()->web_contents()->GetBrowserContext());
     return CacheStorageSideDataSizeChecker::GetSize(
-        static_cast<CacheStorageContextImpl*>(
-            partition->GetCacheStorageContext()),
-        embedded_test_server()->base_url(), std::string("cache_name"),
-        embedded_test_server()->GetURL(kScriptUrl));
+        partition->GetCacheStorageControl(), embedded_test_server()->base_url(),
+        std::string("cache_name"), embedded_test_server()->GetURL(kScriptUrl));
   }
 
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerV8CodeCacheForCacheStorageTest);
diff --git a/content/common/user_agent_unittest.cc b/content/common/user_agent_unittest.cc
index 10be1828..5a7bd7b8 100644
--- a/content/common/user_agent_unittest.cc
+++ b/content/common/user_agent_unittest.cc
@@ -126,4 +126,15 @@
   }
 }
 
+TEST(UserAgentStringTest, LowEntropyCpuArchitecture) {
+  std::string arch = GetLowEntropyCpuArchitecture();
+
+#if defined(OS_WIN) || defined(OS_MAC) || \
+    (defined(OS_POSIX) && !defined(OS_ANDROID))
+  EXPECT_TRUE("arm" == arch || "x86" == arch);
+#else
+  EXPECT_EQ("", arch);
+#endif
+}
+
 }  // namespace content
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index eaacbd57..6155a91c 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -527,6 +527,10 @@
     ]
   }
 
+  if (is_win) {
+    sources += [ "ax_inspect_factory_win.cc" ]
+  }
+
   if (use_atk) {
     sources += [ "ax_inspect_factory_auralinux.cc" ]
   }
diff --git a/content/public/browser/ax_inspect_factory_win.cc b/content/public/browser/ax_inspect_factory_win.cc
new file mode 100644
index 0000000..87232c9e
--- /dev/null
+++ b/content/public/browser/ax_inspect_factory_win.cc
@@ -0,0 +1,39 @@
+// Copyright 2021 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/public/browser/ax_inspect_factory.h"
+
+#include "base/notreached.h"
+#include "base/win/com_init_util.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_blink.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_uia_win.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_win.h"
+
+namespace content {
+
+// static
+std::unique_ptr<ui::AXTreeFormatter>
+AXInspectFactory::CreatePlatformFormatter() {
+  return CreateFormatter(kWinIA2);
+}
+
+// static
+std::unique_ptr<ui::AXTreeFormatter> AXInspectFactory::CreateFormatter(
+    AXInspectFactory::Type type) {
+  switch (type) {
+    case kBlink:
+      return std::make_unique<AccessibilityTreeFormatterBlink>();
+    case kWinIA2:
+      base::win::AssertComInitialized();
+      return std::make_unique<AccessibilityTreeFormatterWin>();
+    case kWinUIA:
+      base::win::AssertComInitialized();
+      return std::make_unique<AccessibilityTreeFormatterUia>();
+    default:
+      NOTREACHED() << "Unsupported formatter type " << type;
+  }
+  return nullptr;
+}
+
+}  // namespace content
diff --git a/content/public/browser/file_system_access_permission_context.h b/content/public/browser/file_system_access_permission_context.h
index 1dee003..b231f167 100644
--- a/content/public/browser/file_system_access_permission_context.h
+++ b/content/public/browser/file_system_access_permission_context.h
@@ -134,8 +134,8 @@
   // Return the path associated with well-known directories such as "desktop"
   // and "music", or a default path if the |directory| cannot be matched to a
   // well-known directory.
-  virtual base::FilePath GetCommonDirectoryPath(
-      blink::mojom::CommonDirectory directory) = 0;
+  virtual base::FilePath GetWellKnownDirectoryPath(
+      blink::mojom::WellKnownDirectory directory) = 0;
 
  protected:
   virtual ~FileSystemAccessPermissionContext() = default;
diff --git a/content/renderer/accessibility/render_accessibility_manager.cc b/content/renderer/accessibility/render_accessibility_manager.cc
index a66ebbad..8082be1 100644
--- a/content/renderer/accessibility/render_accessibility_manager.cc
+++ b/content/renderer/accessibility/render_accessibility_manager.cc
@@ -66,9 +66,7 @@
 }
 
 void RenderAccessibilityManager::FatalError() {
-  // Prevent code folding.
-  const int line_number = __LINE__;
-  base::debug::Alias(&line_number);
+  NO_CODE_FOLDING();
   CHECK(false) << "Invalid accessibility tree.";
 }
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 7981521..bb89ab0 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -95,6 +95,11 @@
 [ fuchsia ] Pixel_Canvas2DTabSwitch_SoftwareCompositing [ Skip ]
 [ fuchsia ] Pixel_WebGLReadPixelsTabSwitch_SoftwareCompositing [ Skip ]
 
+# Flakes on Linux, and less often flakes on Win and Mac per flake portal.
+crbug.com/1166141 [ linux ] Pixel_WebGLReadPixelsTabSwitch_SoftwareCompositing [ RetryOnFailure ]
+crbug.com/1166141 [ win ] Pixel_WebGLReadPixelsTabSwitch_SoftwareCompositing [ RetryOnFailure ]
+crbug.com/1166141 [ mac ] Pixel_WebGLReadPixelsTabSwitch_SoftwareCompositing [ RetryOnFailure ]
+
 # Skip tab switching tests on Android webview and Fuchsia since they don't have tabs
 [ android android-webview-instrumentation ] Pixel_Canvas2DTabSwitch [ Skip ]
 [ android android-webview-instrumentation ] Pixel_WebGLReadPixelsTabSwitch [ Skip ]
diff --git a/docs/testing/batching_instrumentation_tests.md b/docs/testing/batching_instrumentation_tests.md
index ec96a39..1c9ea9e 100644
--- a/docs/testing/batching_instrumentation_tests.md
+++ b/docs/testing/batching_instrumentation_tests.md
@@ -61,6 +61,10 @@
 and leaking state from these tests as you only have to think about tests within
 the suite. For smaller and less complex test suites, see Custom below.
 
+If you use different @Features annotations on test methods, you can use the
+@Batch.SplitByFeature annotation to run tests with different features in
+separate batches.
+
 ### Custom
 
 This batching type is best for smaller and less complex test suites, that
diff --git a/extensions/browser/api/declarative_net_request/rules_monitor_service.cc b/extensions/browser/api/declarative_net_request/rules_monitor_service.cc
index 3371fd7..57147fa0 100644
--- a/extensions/browser/api/declarative_net_request/rules_monitor_service.cc
+++ b/extensions/browser/api/declarative_net_request/rules_monitor_service.cc
@@ -202,19 +202,7 @@
 class RulesMonitorService::ApiCallQueue {
  public:
   ApiCallQueue() = default;
-  ~ApiCallQueue() {
-    // We currently require that any ExtensionFunction should Respond before
-    // being deleted. To satisfy this, dispatch all pending api calls; even
-    // though we know this will be a no-op.
-    // TODO(karandeepb): Change this requirement and remove the code below.
-    in_destruction_ = true;
-    while (!api_call_queue_.empty()) {
-      base::OnceClosure api_call = std::move(api_call_queue_.front());
-      api_call_queue_.pop();
-      std::move(api_call).Run();
-    }
-  }
-
+  ~ApiCallQueue() = default;
   ApiCallQueue(const ApiCallQueue&) = delete;
   ApiCallQueue& operator=(const ApiCallQueue&) = delete;
   ApiCallQueue(ApiCallQueue&&) = delete;
@@ -267,10 +255,6 @@
  private:
   // Signals that the last posted api call has completed.
   void OnApiCallCompleted() {
-    // This should never be called synchronously from the destructor since this
-    // is scheduled via `PostOnApiCallCompleted()`.
-    DCHECK(!in_destruction_);
-
     DCHECK(executing_api_call_);
     executing_api_call_ = false;
     ExecuteApiCallIfNecessary();
@@ -293,9 +277,6 @@
   bool ready_to_execute_api_calls_ = false;
   base::queue<base::OnceClosure> api_call_queue_;
 
-  // Whether we are in the `ApiCallQueue` destructor.
-  bool in_destruction_ = false;
-
   // Must be the last member variable. See WeakPtrFactory documentation for
   // details.
   base::WeakPtrFactory<ApiCallQueue> weak_factory_{this};
diff --git a/extensions/browser/extension_function.cc b/extensions/browser/extension_function.cc
index 1824acd..15bdc450 100644
--- a/extensions/browser/extension_function.cc
+++ b/extensions/browser/extension_function.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/dcheck_is_on.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/singleton.h"
@@ -29,6 +30,7 @@
 #include "extensions/browser/extension_function_dispatcher.h"
 #include "extensions/browser/extension_function_registry.h"
 #include "extensions/browser/extension_message_filter.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extensions_browser_client.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/error_utils.h"
@@ -388,14 +390,30 @@
         extension(), is_from_service_worker(), name());
   }
 
-  // The extension function should always respond to avoid leaks in the
-  // renderer, dangling callbacks, etc. The exception is if the system is
-  // shutting down.
-  extensions::ExtensionsBrowserClient* browser_client =
-      extensions::ExtensionsBrowserClient::Get();
-  DCHECK(!browser_client || browser_client->IsShuttingDown() || did_respond() ||
-         ignore_all_did_respond_for_testing_do_not_use)
-      << name();
+// The extension function should always respond to avoid leaks in the
+// renderer, dangling callbacks, etc. The exception is if the system is
+// shutting down or if the extension has been unloaded.
+#if DCHECK_IS_ON()
+  auto can_be_destroyed_before_responding = [this]() {
+    extensions::ExtensionsBrowserClient* browser_client =
+        extensions::ExtensionsBrowserClient::Get();
+    if (!browser_client || browser_client->IsShuttingDown())
+      return true;
+
+    if (ignore_all_did_respond_for_testing_do_not_use)
+      return true;
+
+    auto* registry = extensions::ExtensionRegistry::Get(browser_context());
+    if (registry && extension() &&
+        !registry->enabled_extensions().Contains(extension_id())) {
+      return true;
+    }
+
+    return false;
+  };
+
+  CHECK(did_respond() || can_be_destroyed_before_responding()) << name();
+#endif  // DCHECK_IS_ON()
 }
 
 void ExtensionFunction::AddWorkerResponseTarget() {
diff --git a/gin/isolate_holder.cc b/gin/isolate_holder.cc
index 872025f..73435b1 100644
--- a/gin/isolate_holder.cc
+++ b/gin/isolate_holder.cc
@@ -83,8 +83,6 @@
     params.only_terminate_in_safe_scope = true;
     params.embedder_wrapper_type_index = kWrapperInfoIndex;
     params.embedder_wrapper_object_index = kEncodedValueIndex;
-    params.supported_import_assertions = {kSupportedImportAssertions.begin(),
-                                          kSupportedImportAssertions.end()};
 
     v8::Isolate::Initialize(isolate_, params);
   }
@@ -120,6 +118,4 @@
   isolate_data_->EnableIdleTasks(std::move(idle_task_runner));
 }
 
-constexpr std::array<const char*, 1> IsolateHolder::kSupportedImportAssertions;
-
 }  // namespace gin
diff --git a/gin/public/isolate_holder.h b/gin/public/isolate_holder.h
index a8eb719..f23af2d9 100644
--- a/gin/public/isolate_holder.h
+++ b/gin/public/isolate_holder.h
@@ -5,7 +5,6 @@
 #ifndef GIN_PUBLIC_ISOLATE_HOLDER_H_
 #define GIN_PUBLIC_ISOLATE_HOLDER_H_
 
-#include <array>
 #include <memory>
 
 #include "base/macros.h"
@@ -121,13 +120,6 @@
   AccessMode access_mode_;
   IsolateType isolate_type_;
 
-  // Equivalent to the list returned by HostGetSupportedImportAssertions:
-  // https://tc39.es/proposal-import-assertions/#sec-hostgetsupportedimportassertions
-  // This will be used to indicate to V8 that Blink wants to receive
-  // module "type" import assertions.
-  static constexpr std::array<const char*, 1> kSupportedImportAssertions = {
-      "type"};
-
   DISALLOW_COPY_AND_ASSIGN(IsolateHolder);
 };
 
diff --git a/gpu/command_buffer/common/buffer.h b/gpu/command_buffer/common/buffer.h
index 21e4055..b02f6d76 100644
--- a/gpu/command_buffer/common/buffer.h
+++ b/gpu/command_buffer/common/buffer.h
@@ -87,20 +87,20 @@
   DISALLOW_COPY_AND_ASSIGN(Buffer);
 };
 
-static inline std::unique_ptr<BufferBacking> MakeBackingFromSharedMemory(
+inline std::unique_ptr<BufferBacking> MakeBackingFromSharedMemory(
     base::UnsafeSharedMemoryRegion shared_memory_region,
     base::WritableSharedMemoryMapping shared_memory_mapping) {
   return std::make_unique<SharedMemoryBufferBacking>(
       std::move(shared_memory_region), std::move(shared_memory_mapping));
 }
-static inline scoped_refptr<Buffer> MakeBufferFromSharedMemory(
+inline scoped_refptr<Buffer> MakeBufferFromSharedMemory(
     base::UnsafeSharedMemoryRegion shared_memory_region,
     base::WritableSharedMemoryMapping shared_memory_mapping) {
   return base::MakeRefCounted<Buffer>(MakeBackingFromSharedMemory(
       std::move(shared_memory_region), std::move(shared_memory_mapping)));
 }
 
-static inline scoped_refptr<Buffer> MakeMemoryBuffer(uint32_t size) {
+inline scoped_refptr<Buffer> MakeMemoryBuffer(uint32_t size) {
   return base::MakeRefCounted<Buffer>(
       std::make_unique<MemoryBufferBacking>(size));
 }
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 7558180..fc16033 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -19236,7 +19236,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.packager\",\"packages\":[{\"cipd_yaml\":\"third_party/android_sdk/cipd/build-tools/25.0.2.yaml\",\"sdk_package_name\":\"build-tools;25.0.2\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/build-tools/29.0.2.yaml\",\"sdk_package_name\":\"build-tools;29.0.2\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/build-tools/30.0.1.yaml\",\"sdk_package_name\":\"build-tools;30.0.1\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/cmdline-tools.yaml\",\"sdk_package_name\":\"cmdline-tools;latest\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/emulator.yaml\",\"sdk_package_name\":\"emulator\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/extras/google/gcm.yaml\",\"sdk_package_name\":\"extras;google;gcm\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/patcher/v4.yaml\",\"sdk_package_name\":\"patcher;v4\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/platforms/android-29.yaml\",\"sdk_package_name\":\"platforms;android-29\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/platforms/android-30.yaml\",\"sdk_package_name\":\"platforms;android-30\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/platform-tools.yaml\",\"sdk_package_name\":\"platform-tools\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/sources/android-29.yaml\",\"sdk_package_name\":\"sources;android-29\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-27/google_apis/x86.yaml\",\"sdk_package_name\":\"system-images;android-27;google_apis;x86\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-27/google_apis_playstore/x86.yaml\",\"sdk_package_name\":\"system-images;android-27;google_apis_playstore;x86\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-29/google_apis/x86.yaml\",\"sdk_package_name\":\"system-images;android-29;google_apis;x86\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-29/google_apis_playstore/x86.yaml\",\"sdk_package_name\":\"system-images;android-29;google_apis_playstore;x86\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-30/google_apis/x86.yaml\",\"sdk_package_name\":\"system-images;android-30;google_apis;x86\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-30/google_apis_playstore/x86.yaml\",\"sdk_package_name\":\"system-images;android-30;google_apis_playstore;x86\"}],\"recipe\":\"android/sdk_packager\"}"
+      properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.packager\",\"packages\":[{\"cipd_yaml\":\"third_party/android_sdk/cipd/build-tools/25.0.2.yaml\",\"sdk_package_name\":\"build-tools;25.0.2\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/build-tools/29.0.2.yaml\",\"sdk_package_name\":\"build-tools;29.0.2\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/build-tools/30.0.1.yaml\",\"sdk_package_name\":\"build-tools;30.0.1\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/cmdline-tools.yaml\",\"sdk_package_name\":\"cmdline-tools;latest\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/emulator.yaml\",\"sdk_package_name\":\"emulator\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/patcher/v4.yaml\",\"sdk_package_name\":\"patcher;v4\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/platforms/android-29.yaml\",\"sdk_package_name\":\"platforms;android-29\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/platforms/android-30.yaml\",\"sdk_package_name\":\"platforms;android-30\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/platform-tools.yaml\",\"sdk_package_name\":\"platform-tools\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/sources/android-29.yaml\",\"sdk_package_name\":\"sources;android-29\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-27/google_apis/x86.yaml\",\"sdk_package_name\":\"system-images;android-27;google_apis;x86\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-27/google_apis_playstore/x86.yaml\",\"sdk_package_name\":\"system-images;android-27;google_apis_playstore;x86\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-29/google_apis/x86.yaml\",\"sdk_package_name\":\"system-images;android-29;google_apis;x86\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-29/google_apis_playstore/x86.yaml\",\"sdk_package_name\":\"system-images;android-29;google_apis_playstore;x86\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-30/google_apis/x86.yaml\",\"sdk_package_name\":\"system-images;android-30;google_apis;x86\"},{\"cipd_yaml\":\"third_party/android_sdk/cipd/system_images/android-30/google_apis_playstore/x86.yaml\",\"sdk_package_name\":\"system-images;android-30;google_apis_playstore;x86\"}],\"recipe\":\"android/sdk_packager\"}"
       execution_timeout_secs: 10800
       build_numbers: YES
       service_account: "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index e06c5e1..32eb9d50 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -1791,10 +1791,6 @@
                 "cipd_yaml": "third_party/android_sdk/cipd/emulator.yaml",
             },
             {
-                "sdk_package_name": "extras;google;gcm",
-                "cipd_yaml": "third_party/android_sdk/cipd/extras/google/gcm.yaml",
-            },
-            {
                 "sdk_package_name": "patcher;v4",
                 "cipd_yaml": "third_party/android_sdk/cipd/patcher/v4.yaml",
             },
diff --git a/ios/OWNERS b/ios/OWNERS
index a21d7c40..a5172c6 100644
--- a/ios/OWNERS
+++ b/ios/OWNERS
@@ -1,4 +1,3 @@
-eugenebut@chromium.org
 marq@chromium.org
 noyau@chromium.org
 rohitrao@chromium.org
diff --git a/ios/chrome/browser/signin/authentication_service.mm b/ios/chrome/browser/signin/authentication_service.mm
index 71704ce2..b0efa6c 100644
--- a/ios/chrome/browser/signin/authentication_service.mm
+++ b/ios/chrome/browser/signin/authentication_service.mm
@@ -306,18 +306,19 @@
   ResetPromptForSignIn();
   sync_setup_service_->PrepareForFirstSyncSetup();
 
+  // Load all credentials from SSO library. This must load the credentials
+  // for the primary account too.
+  identity_manager_->GetDeviceAccountsSynchronizer()
+      ->ReloadAllAccountsFromSystemWithPrimaryAccount(CoreAccountId());
+
   const CoreAccountId account_id = identity_manager_->PickAccountIdForAccount(
       base::SysNSStringToUTF8(identity.gaiaID),
       base::SysNSStringToUTF8(identity.userEmail));
 
-  // Load all credentials from SSO library. This must load the credentials
-  // for the primary account too.
-  identity_manager_->GetDeviceAccountsSynchronizer()
-      ->ReloadAllAccountsFromSystem();
-
   // Ensure that the account the user is trying to sign into has been loaded
   // from the SSO library and that hosted_domain is set (should be the proper
   // hosted domain or kNoHostedDomainFound that are both non-empty strings).
+  CHECK(identity_manager_->HasAccountWithRefreshToken(account_id));
   const base::Optional<AccountInfo> account_info =
       identity_manager_
           ->FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId(
@@ -553,7 +554,8 @@
   HandleForgottenIdentity(nil, should_prompt);
   if (IsAuthenticated()) {
     identity_manager_->GetDeviceAccountsSynchronizer()
-        ->ReloadAllAccountsFromSystem();
+        ->ReloadAllAccountsFromSystemWithPrimaryAccount(
+            identity_manager_->GetPrimaryAccountId());
   }
 }
 
diff --git a/ios/web_view/internal/sync/cwv_sync_controller.mm b/ios/web_view/internal/sync/cwv_sync_controller.mm
index baad138..81b9b846 100644
--- a/ios/web_view/internal/sync/cwv_sync_controller.mm
+++ b/ios/web_view/internal/sync/cwv_sync_controller.mm
@@ -174,12 +174,12 @@
   DCHECK(!self.currentIdentity)
       << "Already syncing! Call -stopSyncAndClearIdentity first.";
 
+  _identityManager->GetDeviceAccountsSynchronizer()
+      ->ReloadAllAccountsFromSystemWithPrimaryAccount(CoreAccountId());
+
   const CoreAccountId accountId = _identityManager->PickAccountIdForAccount(
       base::SysNSStringToUTF8(identity.gaiaID),
       base::SysNSStringToUTF8(identity.email));
-
-  _identityManager->GetDeviceAccountsSynchronizer()
-      ->ReloadAllAccountsFromSystem();
   CHECK(_identityManager->HasAccountWithRefreshToken(accountId));
 
   _identityManager->GetPrimaryAccountMutator()->SetPrimaryAccount(accountId);
@@ -258,7 +258,8 @@
 
 - (void)reloadAccounts {
   _identityManager->GetDeviceAccountsSynchronizer()
-      ->ReloadAllAccountsFromSystem();
+      ->ReloadAllAccountsFromSystemWithPrimaryAccount(
+          _identityManager->GetPrimaryAccountInfo().account_id);
 }
 
 @end
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 6f95c86e..ff824b70 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -111,21 +111,21 @@
 //    time_t, suseconds_t (including typedefs to)
 // 3. Any template referencing types above (e.g. std::vector<size_t>)
 template <class P>
-static inline void WriteParam(base::Pickle* m, const P& p) {
+inline void WriteParam(base::Pickle* m, const P& p) {
   typedef typename SimilarTypeTraits<P>::Type Type;
   ParamTraits<Type>::Write(m, static_cast<const Type& >(p));
 }
 
 template <class P>
-static inline bool WARN_UNUSED_RESULT ReadParam(const base::Pickle* m,
-                                                base::PickleIterator* iter,
-                                                P* p) {
+inline bool WARN_UNUSED_RESULT ReadParam(const base::Pickle* m,
+                                         base::PickleIterator* iter,
+                                         P* p) {
   typedef typename SimilarTypeTraits<P>::Type Type;
   return ParamTraits<Type>::Read(m, iter, reinterpret_cast<Type* >(p));
 }
 
 template <class P>
-static inline void LogParam(const P& p, std::string* l) {
+inline void LogParam(const P& p, std::string* l) {
   typedef typename SimilarTypeTraits<P>::Type Type;
   ParamTraits<Type>::Log(static_cast<const Type& >(p), l);
 }
diff --git a/media/gpu/test/video_test_helpers.h b/media/gpu/test/video_test_helpers.h
index e1b2df44..b80b4476d 100644
--- a/media/gpu/test/video_test_helpers.h
+++ b/media/gpu/test/video_test_helpers.h
@@ -157,7 +157,7 @@
 constexpr size_t kPlatformBufferAlignment = 8;
 #endif
 
-inline static size_t AlignToPlatformRequirements(size_t value) {
+inline size_t AlignToPlatformRequirements(size_t value) {
   return base::bits::Align(value, kPlatformBufferAlignment);
 }
 
diff --git a/mojo/core/channel_linux.cc b/mojo/core/channel_linux.cc
index f226e93..222cba8 100644
--- a/mojo/core/channel_linux.cc
+++ b/mojo/core/channel_linux.cc
@@ -603,6 +603,11 @@
     case Message::MessageType::UPGRADE_OFFER: {
       const UpgradeOfferMessage* msg =
           reinterpret_cast<const UpgradeOfferMessage*>(payload);
+      if (payload_size < sizeof(UpgradeOfferMessage)) {
+        LOG(ERROR) << "Received a malformed UPGRADE_OFFER message";
+        return true;
+      }
+
       if (msg->version != UpgradeOfferMessage::kSupportedVersion) {
         LOG(ERROR) << "Reject shared mem upgrade unexpected version: "
                    << msg->version;
diff --git a/net/cert/cert_status_flags.h b/net/cert/cert_status_flags.h
index f88e160..4bd3518 100644
--- a/net/cert/cert_status_flags.h
+++ b/net/cert/cert_status_flags.h
@@ -26,7 +26,7 @@
 static const CertStatus CERT_STATUS_ALL_ERRORS = 0xFF00FFFF;
 
 // Returns true if the specified cert status has an error set.
-static inline bool IsCertStatusError(CertStatus status) {
+inline bool IsCertStatusError(CertStatus status) {
   return (CERT_STATUS_ALL_ERRORS & status) != 0;
 }
 
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 2806783..46772f1 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -838,19 +838,19 @@
     paint_manager().SetSize(view_device_size, device_scale());
 
     const gfx::Size old_image_data_size =
-        gfx::SkISizeToSize(image_data_.dimensions());
+        gfx::SkISizeToSize(image_data().dimensions());
     gfx::Size new_image_data_size =
         PaintManager::GetNewContextSize(old_image_data_size, plugin_size());
     if (new_image_data_size != old_image_data_size) {
       pepper_image_data_ =
           pp::ImageData(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
                         PPSizeFromSize(new_image_data_size), false);
-      image_data_ = SkBitmapFromPPImageData(
+      mutable_image_data() = SkBitmapFromPPImageData(
           std::make_unique<pp::ImageData>(pepper_image_data_));
       set_first_paint(true);
     }
 
-    if (image_data_.drawsNothing()) {
+    if (image_data().drawsNothing()) {
       DCHECK(plugin_size().IsEmpty());
       return;
     }
@@ -1176,14 +1176,14 @@
 void OutOfProcessInstance::DoPaint(const std::vector<gfx::Rect>& paint_rects,
                                    std::vector<PaintReadyRect>* ready,
                                    std::vector<gfx::Rect>* pending) {
-  if (image_data_.drawsNothing()) {
+  if (image_data().drawsNothing()) {
     DCHECK(plugin_size().IsEmpty());
     return;
   }
   if (first_paint()) {
     set_first_paint(false);
-    image_data_.eraseColor(GetBackgroundColor());
-    gfx::Rect rect(gfx::SkISizeToSize(image_data_.dimensions()));
+    mutable_image_data().eraseColor(GetBackgroundColor());
+    gfx::Rect rect(gfx::SkISizeToSize(image_data().dimensions()));
     ready->push_back(
         PaintReadyRect(rect, pepper_image_data_, /*flush_now=*/true));
   }
@@ -1206,7 +1206,7 @@
 
       std::vector<gfx::Rect> pdf_ready;
       std::vector<gfx::Rect> pdf_pending;
-      engine()->Paint(pdf_rect, image_data_, pdf_ready, pdf_pending);
+      engine()->Paint(pdf_rect, mutable_image_data(), pdf_ready, pdf_pending);
       for (auto& ready_rect : pdf_ready) {
         ready_rect.Offset(available_area().OffsetFromOrigin());
         ready->push_back(PaintReadyRect(ready_rect, pepper_image_data_));
@@ -1225,15 +1225,16 @@
       gfx::Rect region = gfx::IntersectRects(
           rect, gfx::Rect(gfx::Size(plugin_size().width(), first_page_ypos)));
       ready->push_back(PaintReadyRect(region, pepper_image_data_));
-      image_data_.erase(GetBackgroundColor(), gfx::RectToSkIRect(region));
+      mutable_image_data().erase(GetBackgroundColor(),
+                                 gfx::RectToSkIRect(region));
     }
 
     for (const auto& background_part : background_parts()) {
       gfx::Rect intersection =
           gfx::IntersectRects(background_part.location, rect);
       if (!intersection.IsEmpty()) {
-        image_data_.erase(background_part.color,
-                          gfx::RectToSkIRect(intersection));
+        mutable_image_data().erase(background_part.color,
+                                   gfx::RectToSkIRect(intersection));
         ready->push_back(PaintReadyRect(intersection, pepper_image_data_));
       }
     }
@@ -1298,7 +1299,7 @@
 }
 
 void OutOfProcessInstance::DidScroll(const gfx::Vector2d& offset) {
-  if (!image_data_.drawsNothing())
+  if (!image_data().drawsNothing())
     paint_manager().ScrollRect(available_area(), offset);
 }
 
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h
index ee6c4ff..85b7624 100644
--- a/pdf/out_of_process_instance.h
+++ b/pdf/out_of_process_instance.h
@@ -331,8 +331,8 @@
   // or not.
   bool SendInputEventToEngine(const pp::InputEvent& event);
 
+  // The Pepper image data that is in sync with image_data().
   pp::ImageData pepper_image_data_;
-  SkBitmap image_data_;  // Must be kept in sync with |pepper_image_data_|.
 
   // The current cursor.
   PP_CursorType_Dev cursor_ = PP_CURSORTYPE_POINTER;
diff --git a/pdf/pdf_view_plugin_base.h b/pdf/pdf_view_plugin_base.h
index 89841bd3..f824175 100644
--- a/pdf/pdf_view_plugin_base.h
+++ b/pdf/pdf_view_plugin_base.h
@@ -15,6 +15,7 @@
 #include "pdf/paint_manager.h"
 #include "pdf/pdf_engine.h"
 #include "pdf/pdfium/pdfium_form_filler.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace chrome_pdf {
@@ -114,6 +115,9 @@
     return deferred_invalidates_;
   }
 
+  const SkBitmap& image_data() const { return image_data_; }
+  SkBitmap& mutable_image_data() { return image_data_; }
+
   const gfx::Rect& available_area() const { return available_area_; }
 
   const gfx::Size& document_size() const { return document_size_; }
@@ -155,6 +159,9 @@
   std::unique_ptr<PDFiumEngine> engine_;
   PaintManager paint_manager_{this};
 
+  // Image data buffer for painting.
+  SkBitmap image_data_;
+
   std::vector<BackgroundPart> background_parts_;
 
   // Deferred invalidates while |in_paint_| is true.
diff --git a/services/tracing/BUILD.gn b/services/tracing/BUILD.gn
index fb4114b..b8dea4a6 100644
--- a/services/tracing/BUILD.gn
+++ b/services/tracing/BUILD.gn
@@ -41,8 +41,6 @@
 }
 
 source_set("privacy_check") {
-  testonly = true
-
   sources = [
     "perfetto/privacy_filtered_fields-inl.h",
     "perfetto/privacy_filtering_check.cc",
@@ -84,6 +82,7 @@
   sources = [
     "perfetto/consumer_host_unittest.cc",
     "perfetto/perfetto_integration_unittest.cc",
+    "perfetto/privacy_filtering_check_unittest.cc",
     "public/cpp/perfetto/producer_test_utils.cc",
     "public/cpp/perfetto/producer_test_utils.h",
     "public/cpp/perfetto/task_runner_unittest.cc",
@@ -112,6 +111,7 @@
 
   deps = [
     ":lib",
+    ":privacy_check",
     ":test_utils",
     "//base",
     "//base/test:test_support",
@@ -126,6 +126,7 @@
     "//third_party/perfetto/protos/perfetto/trace:lite",
     "//third_party/perfetto/protos/perfetto/trace/chrome:lite",
     "//third_party/perfetto/protos/perfetto/trace/interned_data:lite",
+    "//third_party/perfetto/protos/perfetto/trace/profiling:lite",
     "//third_party/perfetto/protos/perfetto/trace/track_event:lite",
   ]
 
diff --git a/services/tracing/perfetto/privacy_filtering_check.cc b/services/tracing/perfetto/privacy_filtering_check.cc
index 60c97d4..7b08a666 100644
--- a/services/tracing/perfetto/privacy_filtering_check.cc
+++ b/services/tracing/perfetto/privacy_filtering_check.cc
@@ -4,10 +4,15 @@
 
 #include "services/tracing/perfetto/privacy_filtering_check.h"
 
+#include <string.h>
+
 #include <sstream>
 
 #include "base/check.h"
+#include "base/check_op.h"
+#include "base/logging.h"
 #include "services/tracing/perfetto/privacy_filtered_fields-inl.h"
+#include "third_party/perfetto/include/perfetto/protozero/proto_utils.h"
 #include "third_party/perfetto/protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
@@ -23,6 +28,7 @@
 using perfetto::protos::pbzero::TrackEvent;
 using protozero::ProtoDecoder;
 
+// Find the index of |value| in |arr|.
 int FindIndexOfValue(const int* const arr, uint32_t value) {
   for (unsigned i = 0; arr[i] != -1; ++i) {
     if (static_cast<int>(value) == arr[i])
@@ -31,38 +37,142 @@
   return -1;
 }
 
-// Recursively verifies that the |proto| contains only accepted field IDs
-// including all sub messages. Keeps track of |parent_ids| for printing error
-// message.
-void VerifyProtoRecursive(const MessageInfo* root,
-                          ProtoDecoder* proto,
-                          std::vector<uint32_t>* parent_ids) {
+#if DCHECK_IS_ON()
+// Logs the disallowed field with the list of parent field IDs.
+void LogDisallowedField(std::vector<uint32_t>* parent_ids, uint32_t field_id) {
+  std::stringstream error;
+  error << "Skipping field in Trace proto. IDs from root to child";
+  for (int a : *parent_ids) {
+    error << " : " << a;
+  }
+  error << " : " << field_id;
+  VLOG(1) << error.rdbuf();
+}
+#endif  // DCHECK_IS_ON()
+
+uint8_t* OffsetToPtr(size_t offset, std::string& str) {
+  DCHECK_LT(offset, str.size());
+  return reinterpret_cast<uint8_t*>(&str[0] + offset);
+}
+
+// Recursively copies the |proto|'s accepted field IDs including all sub
+// messages, over to |output|. Keeps track of |parent_ids| - field id in parent
+// message, in order of most recent child last.
+bool FilterProtoRecursively(const MessageInfo* root,
+                            ProtoDecoder* proto,
+                            std::vector<uint32_t>* parent_ids,
+                            std::string& output) {
+  // Write any allowed fields of the message (the message's "payload") into
+  // |output| at the |out_msg_start_offset|. This will not include the field ID
+  // or size of the current message yet. We add those back below once we know
+  // the final message size. Emitting the message payload into |output| saves
+  // allocations for extra buffer, but will still require a memmove below. Other
+  // alternative is to just use the max length bytes like protozero does.
+  bool has_blocked_fields = false;
+  const size_t out_msg_start_offset = output.size();
+
   proto->Reset();
-  for (auto f = proto->ReadField(); f.valid(); f = proto->ReadField()) {
+  const uint8_t* current_field_start = proto->begin();
+  const uint8_t* next_field_start = nullptr;
+  for (auto f = proto->ReadField(); f.valid();
+       f = proto->ReadField(), current_field_start = next_field_start) {
+    next_field_start = proto->begin() + proto->read_offset();
+
+    // If the field is not available in the accepted fields, then skip copying.
     int index = FindIndexOfValue(root->accepted_field_ids, f.id());
     if (index == -1) {
-      std::stringstream error;
-      error << " Unexpected field in TracePacket proto. IDs from root to child";
-      for (int a : *parent_ids) {
-        error << " : " << a;
-      }
-      error << " : " << f.id();
-      DCHECK(false) << error.rdbuf();
+#if DCHECK_IS_ON()
+      LogDisallowedField(parent_ids, f.id());
+#endif
+      has_blocked_fields = true;
       continue;
     }
-    if (root->sub_messages && root->sub_messages[index] != nullptr) {
+
+    // If the field is allowed, then either the field is a nested message, or a
+    // POD. If it's a nested message, then the message description will be
+    // part of |sub_messages| list. If the message description is nullptr, then
+    // assume it is POD.
+    if (!root->sub_messages || root->sub_messages[index] == nullptr) {
+      // PODs can just be copied over to output. Packed fields can be treated
+      // just like primitive fields, by just copying over the full data. Note
+      // that there cannot be packed nested messages. Note that we cannot use
+      // |f.data()| here since it does not include the preamble (field id and
+      // possibly length), so we need to keep track of |current_field_start|.
+      output.append(current_field_start, next_field_start);
+    } else {
+      // Make recursive call to filter the nested message.
       ProtoDecoder decoder(f.data(), f.size());
       parent_ids->push_back(f.id());
-      VerifyProtoRecursive(root->sub_messages[index], &decoder, parent_ids);
+      has_blocked_fields |= FilterProtoRecursively(
+          root->sub_messages[index], &decoder, parent_ids, output);
       parent_ids->pop_back();
     }
   }
+
+  const uint32_t payload_size = output.size() - out_msg_start_offset;
+
+  // If there are any fields added, only then write the message to output.
+  if (payload_size == 0) {
+    return has_blocked_fields;
+  }
+
+  // The format is <field id><payload size><message data>.
+  // This function wrote the payload of the current message starting from the
+  // end of output. We need to insert the preamble (<field id><payload size>),
+  // after moving the payload by the size of the preamble.
+  const uint32_t field_id =
+      protozero::proto_utils::MakeTagLengthDelimited(parent_ids->back());
+  uint8_t field_id_buf[protozero::proto_utils::kMaxTagEncodedSize];
+  uint8_t* field_id_end =
+      protozero::proto_utils::WriteVarInt(field_id, field_id_buf);
+  const uint8_t field_id_length = field_id_end - field_id_buf;
+
+  uint8_t payload_size_buf[protozero::proto_utils::kMessageLengthFieldSize];
+  uint8_t* payload_size_end =
+      protozero::proto_utils::WriteVarInt(payload_size, payload_size_buf);
+  const uint8_t payload_size_length = payload_size_end - payload_size_buf;
+
+  // Resize |output| and move the payload, by size of the preamble.
+  const size_t out_payload_start_offset =
+      out_msg_start_offset + field_id_length + payload_size_length;
+  output.append(field_id_length + payload_size_length, 0);
+  memmove(OffsetToPtr(out_payload_start_offset, output),
+          OffsetToPtr(out_msg_start_offset, output), payload_size);
+
+  // Insert field id and payload length.
+  memcpy(OffsetToPtr(out_msg_start_offset, output), field_id_buf,
+         field_id_length);
+  memcpy(OffsetToPtr(out_msg_start_offset + field_id_length, output),
+         payload_size_buf, payload_size_length);
+
+  return has_blocked_fields;
 }
 
-// Verifies that the |proto| contains only accepted fields.
-void VerifyProto(const MessageInfo* root, ProtoDecoder* proto) {
+bool FilterProto(const std::string& serialized_trace_proto,
+                 std::string& output) {
+  constexpr uint32_t kTracePacketFieldId = 1;
+  // DO NOT use Trace::Decoder or TracePacket::Decoder since it sets the
+  // TypedProtoDecoder does a memset of 0 for all field IDs. TracePacket is
+  // especially bad because the max field ID is up to 1000s due to extensions.
+  ProtoDecoder trace(
+      reinterpret_cast<const uint8_t*>(serialized_trace_proto.data()),
+      serialized_trace_proto.size());
+  // Try to allocate all the memory before parsing the proto, so the parser runs
+  // faster.
+  output.reserve(serialized_trace_proto.size());
   std::vector<uint32_t> parent_ids;
-  VerifyProtoRecursive(root, proto, &parent_ids);
+  parent_ids.reserve(20);
+  parent_ids.push_back(kTracePacketFieldId);
+
+  bool has_blocked_fields = false;
+  for (auto f = trace.ReadField(); f.valid(); f = trace.ReadField()) {
+    CHECK_EQ(f.id(), kTracePacketFieldId);
+    ProtoDecoder packet(f.data(), f.size());
+    const MessageInfo* root = &kTracePacket;
+    has_blocked_fields |=
+        FilterProtoRecursively(root, &packet, &parent_ids, output);
+  }
+  return has_blocked_fields;
 }
 
 }  // namespace
@@ -71,16 +181,24 @@
 PrivacyFilteringCheck::~PrivacyFilteringCheck() = default;
 
 // static
+void PrivacyFilteringCheck::RemoveBlockedFields(
+    std::string& serialized_trace_proto) {
+  std::string output;
+  FilterProto(serialized_trace_proto, output);
+  serialized_trace_proto.swap(output);
+}
+
 void PrivacyFilteringCheck::CheckProtoForUnexpectedFields(
     const std::string& serialized_trace_proto) {
+  std::string output;
+  bool has_blocked_fields = FilterProto(serialized_trace_proto, output);
+  DCHECK(!has_blocked_fields);
+
   perfetto::protos::pbzero::Trace::Decoder trace(
       reinterpret_cast<const uint8_t*>(serialized_trace_proto.data()),
       serialized_trace_proto.size());
-
   for (auto it = trace.packet(); !!it; ++it) {
     TracePacket::Decoder packet(*it);
-    const MessageInfo* root = &kTracePacket;
-    VerifyProto(root, &packet);
 
     if (packet.has_track_event()) {
       ++stats_.track_event;
diff --git a/services/tracing/perfetto/privacy_filtering_check.h b/services/tracing/perfetto/privacy_filtering_check.h
index fc76da7..1f254f0 100644
--- a/services/tracing/perfetto/privacy_filtering_check.h
+++ b/services/tracing/perfetto/privacy_filtering_check.h
@@ -26,6 +26,9 @@
   PrivacyFilteringCheck();
   ~PrivacyFilteringCheck();
 
+  // Removes disallowed fields from the trace.
+  static void RemoveBlockedFields(std::string& serialized_trace_proto);
+
   void CheckProtoForUnexpectedFields(const std::string& serialized_trace_proto);
 
   const TraceStats& stats() const { return stats_; }
diff --git a/services/tracing/perfetto/privacy_filtering_check_unittest.cc b/services/tracing/perfetto/privacy_filtering_check_unittest.cc
new file mode 100644
index 0000000..b0ae8f7
--- /dev/null
+++ b/services/tracing/perfetto/privacy_filtering_check_unittest.cc
@@ -0,0 +1,168 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/tracing/perfetto/privacy_filtering_check.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/perfetto/protos/perfetto/trace/trace.pb.h"
+#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/process_descriptor.pb.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pb.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/track_descriptor.pb.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/track_event.pb.h"
+
+namespace tracing {
+
+void FillDisallowedTestField(perfetto::protos::TracePacket* packet) {
+  auto* for_testing = packet->mutable_for_testing();
+  for_testing->set_str("TestField");
+  for_testing->set_counter(10);
+}
+
+perfetto::protos::Trace GetFilteredTrace(const perfetto::protos::Trace& trace) {
+  std::string serialized = trace.SerializeAsString();
+  PrivacyFilteringCheck check;
+  check.RemoveBlockedFields(serialized);
+
+  perfetto::protos::Trace filtered;
+  EXPECT_TRUE(filtered.ParseFromString(serialized));
+  return filtered;
+}
+
+TEST(PrivacyFilteringTest, EmptyTrace) {
+  perfetto::protos::Trace trace;
+  perfetto::protos::Trace filtered = GetFilteredTrace(trace);
+  ASSERT_EQ(0, filtered.packet_size());
+}
+
+TEST(PrivacyFilteringTest, SafeToplevelField) {
+  perfetto::protos::Trace trace;
+  trace.add_packet()->set_timestamp(10);
+
+  perfetto::protos::Trace filtered = GetFilteredTrace(trace);
+  ASSERT_EQ(1, filtered.packet_size());
+  EXPECT_EQ(10u, filtered.packet(0).timestamp());
+}
+
+TEST(PrivacyFilteringTest, SafeToplevelMessageField) {
+  perfetto::protos::Trace trace;
+  trace.add_packet()->mutable_track_event()->set_track_uuid(11);
+
+  perfetto::protos::Trace filtered = GetFilteredTrace(trace);
+  ASSERT_EQ(1, filtered.packet_size());
+  EXPECT_EQ(11u, filtered.packet(0).track_event().track_uuid());
+}
+
+TEST(PrivacyFilteringTest, RepeatedFields) {
+  perfetto::protos::Trace trace;
+  auto* track_event = trace.add_packet()->mutable_track_event();
+  track_event->add_debug_annotations()->set_name_iid(5);
+  track_event->add_debug_annotations()->set_name_iid(2);
+  track_event->add_debug_annotations()->set_name_iid(8);
+  track_event->add_flow_ids(3);
+  track_event->add_flow_ids(6);
+  track_event->add_flow_ids(9);
+
+  perfetto::protos::Trace filtered = GetFilteredTrace(trace);
+  ASSERT_EQ(1, filtered.packet_size());
+  EXPECT_EQ(0, filtered.packet(0).track_event().debug_annotations_size());
+  EXPECT_EQ(3u, filtered.packet(0).track_event().flow_ids(0));
+  EXPECT_EQ(6u, filtered.packet(0).track_event().flow_ids(1));
+  EXPECT_EQ(9u, filtered.packet(0).track_event().flow_ids(2));
+}
+
+TEST(PrivacyFilteringTest, UnsafeToplevelField) {
+  perfetto::protos::Trace trace;
+  FillDisallowedTestField(trace.add_packet());
+
+  perfetto::protos::Trace filtered = GetFilteredTrace(trace);
+  ASSERT_EQ(0, filtered.packet_size());
+}
+
+TEST(PrivacyFilteringTest, SafeMessageWithOnlyUnsafeFields) {
+  perfetto::protos::Trace trace;
+  auto* packet = trace.add_packet();
+  packet->mutable_track_event()->mutable_legacy_event();
+  auto* debug_annotations =
+      packet->mutable_track_event()->add_debug_annotations();
+  debug_annotations->set_name_iid(2);
+  debug_annotations->set_int_value(10);
+  packet->mutable_track_event()->mutable_log_message()->set_body_iid(1);
+
+  perfetto::protos::Trace filtered = GetFilteredTrace(trace);
+  ASSERT_EQ(0, filtered.packet_size());
+}
+
+TEST(PrivacyFilteringTest, SafeAndUnsafeFields) {
+  perfetto::protos::Trace trace;
+  perfetto::protos::TracePacket* packet = trace.add_packet();
+  FillDisallowedTestField(packet);
+  packet->mutable_trace_packet_defaults()->set_timestamp_clock_id(11);
+
+  perfetto::protos::Trace filtered = GetFilteredTrace(trace);
+  ASSERT_EQ(1, filtered.packet_size());
+  EXPECT_EQ(11u,
+            filtered.packet(0).trace_packet_defaults().timestamp_clock_id());
+  EXPECT_FALSE(filtered.packet(0).has_for_testing());
+}
+
+TEST(PrivacyFilteringTest, SafeAndUnsafePackets) {
+  perfetto::protos::Trace trace;
+  FillDisallowedTestField(trace.add_packet());
+  trace.add_packet()->mutable_trace_packet_defaults()->set_timestamp_clock_id(
+      11);
+
+  perfetto::protos::Trace filtered = GetFilteredTrace(trace);
+  ASSERT_EQ(1, filtered.packet_size());
+  EXPECT_FALSE(filtered.packet(0).has_for_testing());
+  EXPECT_EQ(11u,
+            filtered.packet(0).trace_packet_defaults().timestamp_clock_id());
+}
+
+TEST(PrivacyFilteringTest, NestedSafeAndUnsafeFields) {
+  perfetto::protos::Trace trace;
+  perfetto::protos::TracePacket* packet = trace.add_packet();
+  FillDisallowedTestField(packet);
+  packet->set_timestamp(50);
+  auto* track_event = packet->mutable_track_event();
+  track_event->set_track_uuid(11);
+  track_event->mutable_log_message()->set_body_iid(10);
+  track_event->set_name_iid(1);
+  track_event->add_category_iids(2);
+  auto* hist = track_event->mutable_chrome_histogram_sample();
+  hist->set_name_hash(4);
+  hist->set_name("hist");
+  hist->set_sample(5);
+  track_event->add_flow_ids(3);
+  track_event->add_debug_annotations()->set_name_iid(2);
+  track_event->add_flow_ids(6);
+  track_event->add_debug_annotations()->set_name_iid(5);
+  track_event->add_flow_ids(9);
+  track_event->add_debug_annotations()->set_name_iid(8);
+
+  perfetto::protos::Trace filtered = GetFilteredTrace(trace);
+  ASSERT_EQ(1, filtered.packet_size());
+  const auto& packet1 = filtered.packet(0);
+  EXPECT_FALSE(packet1.has_for_testing());
+  EXPECT_EQ(50u, packet1.timestamp());
+
+  const auto& event = packet1.track_event();
+  EXPECT_EQ(11u, event.track_uuid());
+  EXPECT_FALSE(event.has_log_message());
+  EXPECT_EQ(1u, event.name_iid());
+  ASSERT_EQ(1, event.category_iids_size());
+  EXPECT_EQ(2u, event.category_iids(0));
+  EXPECT_EQ(0, event.debug_annotations_size());
+  ASSERT_EQ(3, event.flow_ids_size());
+  EXPECT_EQ(3u, event.flow_ids(0));
+  EXPECT_EQ(6u, event.flow_ids(1));
+  EXPECT_EQ(9u, event.flow_ids(2));
+
+  const auto& histogram = event.chrome_histogram_sample();
+  EXPECT_EQ(4u, histogram.name_hash());
+  EXPECT_FALSE(histogram.has_name());
+  EXPECT_EQ(5, histogram.sample());
+}
+
+}  // namespace tracing
diff --git a/skia/ext/platform_canvas.h b/skia/ext/platform_canvas.h
index dd6d354e..63ea7f5a 100644
--- a/skia/ext/platform_canvas.h
+++ b/skia/ext/platform_canvas.h
@@ -69,9 +69,9 @@
     OnFailureType failure_type);
 #endif
 
-static inline std::unique_ptr<SkCanvas> CreatePlatformCanvas(int width,
-                                                             int height,
-                                                             bool is_opaque) {
+inline std::unique_ptr<SkCanvas> CreatePlatformCanvas(int width,
+                                                      int height,
+                                                      bool is_opaque) {
 #if defined(WIN32)
   return CreatePlatformCanvasWithSharedSection(width, height, is_opaque, 0,
                                                CRASH_ON_FAILURE);
@@ -81,9 +81,9 @@
 #endif
 }
 
-static inline std::unique_ptr<SkCanvas> TryCreateBitmapCanvas(int width,
-                                                              int height,
-                                                              bool is_opaque) {
+inline std::unique_ptr<SkCanvas> TryCreateBitmapCanvas(int width,
+                                                       int height,
+                                                       bool is_opaque) {
 #if defined(WIN32)
   return CreatePlatformCanvasWithSharedSection(width, height, is_opaque, 0,
                                                RETURN_NULL_ON_FAILURE);
diff --git a/storage/browser/blob/blob_builder_from_stream_unittest.cc b/storage/browser/blob/blob_builder_from_stream_unittest.cc
index 89df0f7..3db6bbb 100644
--- a/storage/browser/blob/blob_builder_from_stream_unittest.cc
+++ b/storage/browser/blob/blob_builder_from_stream_unittest.cc
@@ -91,7 +91,11 @@
   std::unique_ptr<BlobDataHandle> BuildFromString(
       std::string data,
       bool initial_allocation_should_succeed = true) {
-    mojo::DataPipe pipe;
+    mojo::ScopedDataPipeProducerHandle producer_handle;
+    mojo::ScopedDataPipeConsumerHandle consumer_handle;
+    EXPECT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+              MOJO_RESULT_OK);
+
     base::RunLoop loop;
     std::unique_ptr<BlobDataHandle> result;
     uint64_t length_hint = GetLengthHint(data.length());
@@ -104,7 +108,7 @@
           result = std::move(blob);
           loop.Quit();
         }));
-    builder.Start(length_hint, std::move(pipe.consumer_handle),
+    builder.Start(length_hint, std::move(consumer_handle),
                   mojo::NullAssociatedRemote());
 
     // Make sure the initial memory allocation done by the builder matches the
@@ -117,8 +121,8 @@
           << ", disk_usage: " << context_->memory_controller().disk_usage();
     }
 
-    mojo::BlockingCopyFromString(data, pipe.producer_handle);
-    pipe.producer_handle.reset();
+    mojo::BlockingCopyFromString(data, producer_handle);
+    producer_handle.reset();
 
     loop.Run();
     EXPECT_EQ(&builder, finished_builder);
@@ -195,7 +199,10 @@
 };
 
 TEST_P(BlobBuilderFromStreamTest, CallbackCalledOnAbortBeforeDeletion) {
-  mojo::DataPipe pipe;
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
 
   base::RunLoop loop;
   BlobBuilderFromStream* builder_ptr = nullptr;
@@ -208,7 +215,7 @@
         loop.Quit();
       }));
   builder_ptr = builder.get();
-  builder->Start(GetLengthHint(16), std::move(pipe.consumer_handle),
+  builder->Start(GetLengthHint(16), std::move(consumer_handle),
                  mojo::NullAssociatedRemote());
   builder->Abort();
   builder.reset();
@@ -364,7 +371,11 @@
 TEST_F(BlobBuilderFromStreamTest, HintTooLargeForQuota) {
   const uint64_t kLengthHint =
       kTestBlobStorageMaxDiskSpace + kTestBlobStorageMaxBlobMemorySize + 1;
-  mojo::DataPipe pipe;
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
+
   base::RunLoop loop;
   std::unique_ptr<BlobDataHandle> result;
   BlobBuilderFromStream builder(
@@ -374,9 +385,9 @@
             result = std::move(blob);
             loop.Quit();
           }));
-  builder.Start(kLengthHint, std::move(pipe.consumer_handle),
+  builder.Start(kLengthHint, std::move(consumer_handle),
                 mojo::NullAssociatedRemote());
-  pipe.producer_handle.reset();
+  producer_handle.reset();
   loop.Run();
 
   EXPECT_FALSE(result);
@@ -388,7 +399,10 @@
   context_->DisableFilePagingForTesting();
 
   const uint64_t kLengthHint = kTestBlobStorageMaxBlobMemorySize + 1;
-  mojo::DataPipe pipe;
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
   base::RunLoop loop;
   std::unique_ptr<BlobDataHandle> result;
   BlobBuilderFromStream builder(
@@ -398,9 +412,9 @@
             result = std::move(blob);
             loop.Quit();
           }));
-  builder.Start(kLengthHint, std::move(pipe.consumer_handle),
+  builder.Start(kLengthHint, std::move(consumer_handle),
                 mojo::NullAssociatedRemote());
-  pipe.producer_handle.reset();
+  producer_handle.reset();
   loop.Run();
 
   EXPECT_FALSE(result);
@@ -418,7 +432,10 @@
       &progress_client,
       progress_client_remote.BindNewEndpointAndPassDedicatedReceiver());
 
-  mojo::DataPipe pipe;
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
   base::RunLoop loop;
   std::unique_ptr<BlobDataHandle> result;
   BlobBuilderFromStream builder(
@@ -428,10 +445,10 @@
             result = std::move(blob);
             loop.Quit();
           }));
-  builder.Start(GetLengthHint(kData.size()), std::move(pipe.consumer_handle),
+  builder.Start(GetLengthHint(kData.size()), std::move(consumer_handle),
                 progress_client_remote.Unbind());
-  mojo::BlockingCopyFromString(kData, pipe.producer_handle);
-  pipe.producer_handle.reset();
+  mojo::BlockingCopyFromString(kData, producer_handle);
+  producer_handle.reset();
 
   loop.Run();
   progress_receiver.FlushForTesting();
@@ -453,7 +470,10 @@
   limits_.desired_max_disk_space = kDefaultMinPageFileSize * 2;
   limits_.effective_max_disk_space = kDefaultMinPageFileSize * 2;
 
-  mojo::DataPipe pipe;
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
   base::RunLoop loop;
   std::unique_ptr<BlobDataHandle> result;
   BlobBuilderFromStream builder(
@@ -463,12 +483,12 @@
         result = std::move(blob);
         loop.Quit();
       }));
-  builder.Start(kData.size(), std::move(pipe.consumer_handle),
+  builder.Start(kData.size(), std::move(consumer_handle),
                 mojo::NullAssociatedRemote());
 
   context_->set_limits_for_testing(limits_);
   auto data_producer =
-      std::make_unique<mojo::DataPipeProducer>(std::move(pipe.producer_handle));
+      std::make_unique<mojo::DataPipeProducer>(std::move(producer_handle));
   auto* producer_ptr = data_producer.get();
   producer_ptr->Write(
       std::make_unique<mojo::StringDataSource>(
@@ -478,7 +498,6 @@
           base::DoNothing::Once<std::unique_ptr<mojo::DataPipeProducer>,
                                 MojoResult>(),
           std::move(data_producer)));
-  pipe.producer_handle.reset();
   loop.Run();
 
   ASSERT_TRUE(result);
diff --git a/storage/browser/blob/blob_impl_unittest.cc b/storage/browser/blob/blob_impl_unittest.cc
index 95f3908e..fcb5247 100644
--- a/storage/browser/blob/blob_impl_unittest.cc
+++ b/storage/browser/blob/blob_impl_unittest.cc
@@ -160,10 +160,14 @@
   MockBlobReaderClient client;
   mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client);
 
-  mojo::DataPipe pipe;
-  remote->ReadAll(std::move(pipe.producer_handle),
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
+
+  remote->ReadAll(std::move(producer_handle),
                   client_receiver.BindNewPipeAndPassRemote());
-  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  std::string received = ReadDataPipe(std::move(consumer_handle));
   EXPECT_EQ(kContents, received);
 
   client_receiver.FlushForTesting();
@@ -184,9 +188,12 @@
   mojo::Remote<blink::mojom::Blob> remote;
   BlobImpl::Create(std::move(handle), remote.BindNewPipeAndPassReceiver());
 
-  mojo::DataPipe pipe;
-  remote->ReadAll(std::move(pipe.producer_handle), mojo::NullRemote());
-  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
+  remote->ReadAll(std::move(producer_handle), mojo::NullRemote());
+  std::string received = ReadDataPipe(std::move(consumer_handle));
   EXPECT_EQ(kContents, received);
 }
 
@@ -201,11 +208,14 @@
   MockBlobReaderClient client;
   mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client);
 
-  mojo::DataPipe pipe;
-  remote->ReadAll(std::move(pipe.producer_handle),
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
+  remote->ReadAll(std::move(producer_handle),
                   client_receiver.BindNewPipeAndPassRemote());
 
-  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  std::string received = ReadDataPipe(std::move(consumer_handle));
   EXPECT_EQ("", received);
 
   client_receiver.FlushForTesting();
@@ -226,11 +236,14 @@
   MockBlobReaderClient client;
   mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client);
 
-  mojo::DataPipe pipe;
-  remote->ReadRange(2, 5, std::move(pipe.producer_handle),
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
+  remote->ReadRange(2, 5, std::move(producer_handle),
                     client_receiver.BindNewPipeAndPassRemote());
 
-  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  std::string received = ReadDataPipe(std::move(consumer_handle));
   EXPECT_EQ(kContents.substr(2, 5), received);
 
   client_receiver.FlushForTesting();
@@ -251,10 +264,13 @@
   mojo::Remote<blink::mojom::Blob> remote;
   BlobImpl::Create(std::move(handle), remote.BindNewPipeAndPassReceiver());
 
-  mojo::DataPipe pipe;
-  remote->ReadRange(2, 5, std::move(pipe.producer_handle), mojo::NullRemote());
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
+  remote->ReadRange(2, 5, std::move(producer_handle), mojo::NullRemote());
 
-  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  std::string received = ReadDataPipe(std::move(consumer_handle));
   EXPECT_EQ(kContents.substr(2, 5), received);
 }
 
@@ -269,11 +285,14 @@
   MockBlobReaderClient client;
   mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client);
 
-  mojo::DataPipe pipe;
-  remote->ReadRange(2, 15, std::move(pipe.producer_handle),
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
+  remote->ReadRange(2, 15, std::move(producer_handle),
                     client_receiver.BindNewPipeAndPassRemote());
 
-  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  std::string received = ReadDataPipe(std::move(consumer_handle));
   EXPECT_EQ(kContents.substr(2, 15), received);
 
   client_receiver.FlushForTesting();
@@ -297,12 +316,15 @@
   MockBlobReaderClient client;
   mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client);
 
-  mojo::DataPipe pipe;
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
   remote->ReadRange(2, std::numeric_limits<uint64_t>::max(),
-                    std::move(pipe.producer_handle),
+                    std::move(producer_handle),
                     client_receiver.BindNewPipeAndPassRemote());
 
-  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  std::string received = ReadDataPipe(std::move(consumer_handle));
   EXPECT_EQ(kContents.substr(2, kContents.size()), received);
 
   client_receiver.FlushForTesting();
@@ -326,11 +348,14 @@
   MockBlobReaderClient client;
   mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client);
 
-  mojo::DataPipe pipe;
-  remote->ReadRange(2, 5, std::move(pipe.producer_handle),
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
+  remote->ReadRange(2, 5, std::move(producer_handle),
                     client_receiver.BindNewPipeAndPassRemote());
 
-  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  std::string received = ReadDataPipe(std::move(consumer_handle));
   EXPECT_EQ("", received);
 
   client_receiver.FlushForTesting();
@@ -352,11 +377,14 @@
   mojo::Receiver<blink::mojom::BlobReaderClient> client_receiver(&client);
 
   base::RunLoop loop;
-  mojo::DataPipe pipe;
-  remote->ReadRange(15, 4, std::move(pipe.producer_handle),
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
+            MOJO_RESULT_OK);
+  remote->ReadRange(15, 4, std::move(producer_handle),
                     client_receiver.BindNewPipeAndPassRemote());
 
-  std::string received = ReadDataPipe(std::move(pipe.consumer_handle));
+  std::string received = ReadDataPipe(std::move(consumer_handle));
   EXPECT_EQ("", received);
 
   client_receiver.FlushForTesting();
diff --git a/storage/browser/blob/blob_registry_impl_unittest.cc b/storage/browser/blob/blob_registry_impl_unittest.cc
index 69039f0..61f127f 100644
--- a/storage/browser/blob/blob_registry_impl_unittest.cc
+++ b/storage/browser/blob/blob_registry_impl_unittest.cc
@@ -1161,11 +1161,20 @@
 }
 
 TEST_F(BlobRegistryImplTest, DestroyWithUnfinishedStream) {
-  mojo::DataPipe pipe1, pipe2;
-  registry_->RegisterFromStream("", "", 0, std::move(pipe1.consumer_handle),
+  mojo::ScopedDataPipeProducerHandle producer_handle1;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle1;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle1, consumer_handle1),
+            MOJO_RESULT_OK);
+
+  mojo::ScopedDataPipeProducerHandle producer_handle2;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle2;
+  ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle2, consumer_handle2),
+            MOJO_RESULT_OK);
+
+  registry_->RegisterFromStream("", "", 0, std::move(consumer_handle1),
                                 mojo::NullAssociatedRemote(),
                                 base::DoNothing());
-  registry_->RegisterFromStream("", "", 0, std::move(pipe2.consumer_handle),
+  registry_->RegisterFromStream("", "", 0, std::move(consumer_handle2),
                                 mojo::NullAssociatedRemote(),
                                 base::DoNothing());
   registry_.FlushForTesting();
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 981f4c6..372bba0 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2793,6 +2793,21 @@
             ]
         }
     ],
+    "EarlyLibraryLoad": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "EarlyLibraryLoad"
+                    ]
+                }
+            ]
+        }
+    ],
     "EnableCloseAllTabsConfirmation": [
         {
             "platforms": [
@@ -5929,6 +5944,24 @@
             ]
         }
     ],
+    "SafetyCheckWeakPasswords": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SafetyCheckWeakPasswords"
+                    ]
+                }
+            ]
+        }
+    ],
     "SchemefulSameSiteAllOS": [
         {
             "platforms": [
diff --git a/third_party/android_sdk/BUILD.gn b/third_party/android_sdk/BUILD.gn
index efaba4e0..ca571ae 100644
--- a/third_party/android_sdk/BUILD.gn
+++ b/third_party/android_sdk/BUILD.gn
@@ -63,7 +63,4 @@
     testonly = true
     sources = [ "//third_party/android_sdk/public/extras/chromium/support/src/org/chromium/android/support/PackageManagerWrapper.java" ]
   }
-  android_java_prebuilt("android_gcm_java") {
-    jar_path = "//third_party/android_sdk/public/extras/google/gcm/gcm-client/dist/gcm.jar"
-  }
 }
diff --git a/third_party/android_sdk/README.chromium b/third_party/android_sdk/README.chromium
index ab66ba5..ef5c34a5 100644
--- a/third_party/android_sdk/README.chromium
+++ b/third_party/android_sdk/README.chromium
@@ -7,7 +7,6 @@
   Android SDK Platform-tools 30.0.3
   Android SDK Platform API 30
   Android SDK Sources 29
-  Google Cloud Messaging 3
   SDK Patch Applier v4
 Security Critical: no
 License: Apache Version 2.0
diff --git a/third_party/android_sdk/cipd/extras/google/gcm.yaml b/third_party/android_sdk/cipd/extras/google/gcm.yaml
deleted file mode 100644
index ae20a19..0000000
--- a/third_party/android_sdk/cipd/extras/google/gcm.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-package: chromium/third_party/android_sdk/public/extras/google/gcm
-description: extras Android SDK Extras library
-root: ../../../public/
-data:
-  - dir: extras/google/gcm
-# Some tools inspect their argv0 and don't handle CIPD's symlink structure
-# correctly. Install in copy mode so that they can find the other directories
-# relative to themselves.
-install_mode: copy
diff --git a/third_party/androidx/fetch_all_androidx.py b/third_party/androidx/fetch_all_androidx.py
index a41cf76d..b09c568 100755
--- a/third_party/androidx/fetch_all_androidx.py
+++ b/third_party/androidx/fetch_all_androidx.py
@@ -139,28 +139,6 @@
             out.write(replacement)
 
 
-def _extract_files_from_yaml(yaml_path):
-    """Extracts '- file' file listings from yaml file."""
-
-    out = None
-    with open(yaml_path, 'r') as f:
-        for line in f.readlines():
-            line = line.rstrip('\n')
-            if line == 'data:':
-                out = []
-                continue
-            if out is not None:
-                if not line.startswith('- file:'):
-                    raise Exception(
-                        '{} has unsupported attributes. Only \'- file\' is supported'
-                        .format(yaml_path))
-                out.append(line.rsplit(' ', 1)[1])
-
-    if not out:
-        raise Exception('{} does not have \'data\' section.'.format(yaml_path))
-    return out
-
-
 def _write_cipd_yaml(libs_dir, cipd_yaml_path):
     """Writes cipd.yaml file at the passed-in path."""
 
@@ -178,19 +156,10 @@
         if not 'cipd.yaml' in lib_files:
             continue
 
-        if not 'README.chromium' in lib_files:
-            raise Exception('README.chromium not in {}'.format(abs_lib_dir))
-        if not 'LICENSE' in lib_files:
-            raise Exception('LICENSE not in {}'.format(abs_lib_dir))
-        data_files.append(os.path.join(androidx_rel_lib_dir,
-                                       'README.chromium'))
-        data_files.append(os.path.join(androidx_rel_lib_dir, 'LICENSE'))
-
-        _rel_extracted_files = _extract_files_from_yaml(
-            os.path.join(abs_lib_dir, 'cipd.yaml'))
-        data_files.extend(
-            os.path.join(androidx_rel_lib_dir, f)
-            for f in _rel_extracted_files)
+        for lib_file in lib_files:
+            if lib_file == 'cipd.yaml' or lib_file == 'OWNERS':
+                continue
+            data_files.append(os.path.join(androidx_rel_lib_dir, lib_file))
 
     contents = [
         '# Copyright 2020 The Chromium Authors. All rights reserved.',
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 75da6352..f84c19d 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -3163,10 +3163,11 @@
       string version
 
   # Used to specify User Agent Cient Hints to emulate. See https://wicg.github.io/ua-client-hints
+  # Missing optional values will be filled in by the target with what it would normally use.
   experimental type UserAgentMetadata extends object
     properties
-      array of UserAgentBrandVersion brands
-      string fullVersion
+      optional array of UserAgentBrandVersion brands
+      optional string fullVersion
       string platform
       string platformVersion
       string architecture
diff --git a/third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom b/third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom
index a878641..7d4ae58 100644
--- a/third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom
+++ b/third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom
@@ -18,7 +18,7 @@
   kOpenDirectory
 };
 
-enum CommonDirectory {
+enum WellKnownDirectory {
   kDefault,
   kDirDesktop,
   kDirDocuments,
@@ -56,7 +56,7 @@
   // |include_accepts_all| is treated as if it was true.
   ChooseEntries(ChooseFileSystemEntryType type,
                 array<ChooseFileSystemEntryAcceptsOption> accepts,
-                CommonDirectory starting_directory,
+                WellKnownDirectory well_known_starting_directory,
                 bool include_accepts_all) =>
       (FileSystemAccessError result,
        array<FileSystemAccessEntry> entries);
diff --git a/third_party/blink/renderer/bindings/core/v8/boxed_v8_module.h b/third_party/blink/renderer/bindings/core/v8/boxed_v8_module.h
index d53d823..52e3d6d 100644
--- a/third_party/blink/renderer/bindings/core/v8/boxed_v8_module.h
+++ b/third_party/blink/renderer/bindings/core/v8/boxed_v8_module.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/wtf/hash_functions.h"
 #include "v8/include/v8.h"
@@ -50,9 +51,9 @@
 
   static bool Equal(const Member<BoxedV8Module>& a,
                     const Member<BoxedV8Module>& b) {
-    if (a.IsHashTableDeletedValue() && b.IsHashTableDeletedValue())
+    if (IsHashTableDeletedValue(a) && IsHashTableDeletedValue(b))
       return true;
-    if (a.IsHashTableDeletedValue() || b.IsHashTableDeletedValue())
+    if (IsHashTableDeletedValue(a) || IsHashTableDeletedValue(b))
       return false;
 
     if (!a && !b)
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc b/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
index 110fe1f..94a956b 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
@@ -17,6 +17,17 @@
 #include "third_party/blink/renderer/platform/heap/unified_heap_controller.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/buildflags.h"
+
+#if BUILDFLAG(USE_V8_OILPAN)
+
+namespace blink {
+void EmbedderGraphBuilder::BuildEmbedderGraphCallback(v8::Isolate* isolate,
+                                                      v8::EmbedderGraph* graph,
+                                                      void*) {}
+}  // namespace blink
+
+#else  // !USE_V8_OILPAN
 
 namespace blink {
 
@@ -722,3 +733,5 @@
 }
 
 }  // namespace blink
+
+#endif  // !USE_V8_OILPAN
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index c64c933..099be40 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -934,8 +934,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_color_space_range_id.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_color_space_transfer_id.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_color_space_transfer_id.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_common_directory.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_common_directory.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_well_known_directory.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_well_known_directory.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_connection_type.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_connection_type.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_contact_property.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 8ff0c9a..5dd8326 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -759,6 +759,7 @@
           "//third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_output_callback.idl",
           "//third_party/blink/renderer/modules/webcodecs/image_decoder.idl",
           "//third_party/blink/renderer/modules/webcodecs/image_decoder_init.idl",
+          "//third_party/blink/renderer/modules/webcodecs/image_decode_options.idl",
           "//third_party/blink/renderer/modules/webcodecs/image_frame.idl",
           "//third_party/blink/renderer/modules/webcodecs/image_track.idl",
           "//third_party/blink/renderer/modules/webcodecs/plane.idl",
diff --git a/third_party/blink/renderer/core/editing/spellcheck/spell_checker.h b/third_party/blink/renderer/core/editing/spellcheck/spell_checker.h
index c467defb..899c7c1 100644
--- a/third_party/blink/renderer/core/editing/spellcheck/spell_checker.h
+++ b/third_party/blink/renderer/core/editing/spellcheck/spell_checker.h
@@ -40,6 +40,7 @@
 class LocalDOMWindow;
 class LocalFrame;
 class HTMLElement;
+class Node;
 class SpellCheckMarker;
 class SpellCheckRequest;
 class SpellCheckRequester;
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.h b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
index d6f0ac1d..c5928c5 100644
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.h
+++ b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
@@ -55,6 +55,7 @@
                         WebNavigationType navigation_type,
                         ContentSecurityPolicy*,
                         std::unique_ptr<WebNavigationParams> navigation_params);
+  ~WebDocumentLoaderImpl() override;
 
   static WebDocumentLoaderImpl* FromDocumentLoader(DocumentLoader* loader) {
     return static_cast<WebDocumentLoaderImpl*>(loader);
@@ -92,7 +93,6 @@
   void Trace(Visitor*) const override;
 
  private:
-  ~WebDocumentLoaderImpl() override;
   void DetachFromFrame(bool flush_microtask_queue) override;
 
   // Mutable because the const getters will magically sync these to the
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index d4d651d..b5925f4 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -54,6 +54,7 @@
 #include "third_party/blink/renderer/platform/loader/cors/cors.h"
 #include "third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h"
 #include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h"
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_utils.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.h b/third_party/blink/renderer/core/frame/frame_serializer.h
index b77fefd..d8a9f46 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.h
+++ b/third_party/blink/renderer/core/frame/frame_serializer.h
@@ -43,6 +43,7 @@
 
 namespace blink {
 
+class CSSPropertyValueSet;
 class CSSRule;
 class CSSStyleSheet;
 class CSSValue;
@@ -51,7 +52,7 @@
 class FontResource;
 class ImageResourceContent;
 class LocalFrame;
-class CSSPropertyValueSet;
+class Node;
 
 struct SerializedResource;
 
diff --git a/third_party/blink/renderer/core/frame/screen_metrics_emulator.h b/third_party/blink/renderer/core/frame/screen_metrics_emulator.h
index 970d8d8..5c65948 100644
--- a/third_party/blink/renderer/core/frame/screen_metrics_emulator.h
+++ b/third_party/blink/renderer/core/frame/screen_metrics_emulator.h
@@ -10,7 +10,9 @@
 #include "third_party/blink/public/common/widget/device_emulation_params.h"
 #include "third_party/blink/public/common/widget/screen_info.h"
 #include "third_party/blink/public/mojom/widget/device_emulation_params.mojom-blink.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
diff --git a/third_party/blink/renderer/core/html/forms/html_option_element.h b/third_party/blink/renderer/core/html/forms/html_option_element.h
index 78a6f79..1322882 100644
--- a/third_party/blink/renderer/core/html/forms/html_option_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_option_element.h
@@ -54,6 +54,7 @@
                                                    ExceptionState&);
 
   explicit HTMLOptionElement(Document&);
+  ~HTMLOptionElement() override;
   void Trace(Visitor* visitor) const override;
 
   // A text to be shown to users.  The difference from |label()| is |label()|
@@ -112,8 +113,6 @@
   void DidChangeTextContent();
 
  private:
-  ~HTMLOptionElement() override;
-
   bool SupportsFocus() const override;
   bool MatchesDefaultPseudoClass() const override;
   bool MatchesEnabledPseudoClass() const override;
diff --git a/third_party/blink/renderer/core/html/html_table_element.h b/third_party/blink/renderer/core/html/html_table_element.h
index 7cfaf43..57c7834c 100644
--- a/third_party/blink/renderer/core/html/html_table_element.h
+++ b/third_party/blink/renderer/core/html/html_table_element.h
@@ -42,6 +42,7 @@
 
  public:
   explicit HTMLTableElement(Document&);
+  ~HTMLTableElement() override;
 
   HTMLTableCaptionElement* caption() const;
   void setCaption(HTMLTableCaptionElement*, ExceptionState&);
@@ -76,8 +77,6 @@
   void Trace(Visitor*) const override;
 
  private:
-  ~HTMLTableElement() override;
-
   void ParseAttribute(const AttributeModificationParams&) override;
   bool IsPresentationAttribute(const QualifiedName&) const override;
   void CollectStyleForPresentationAttribute(
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
index fb9772e..483c7aa 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
@@ -589,6 +589,9 @@
   }
 
   if (ua_metadata_override.isJust()) {
+    blink::UserAgentMetadata default_ua_metadata =
+        Platform::Current()->UserAgentMetadata();
+
     if (user_agent.IsEmpty()) {
       ua_metadata_override_ = base::nullopt;
       serialized_ua_metadata_override_.Set(std::vector<uint8_t>());
@@ -598,15 +601,25 @@
     std::unique_ptr<protocol::Emulation::UserAgentMetadata> ua_metadata =
         ua_metadata_override.takeJust();
     ua_metadata_override_.emplace();
-    if (ua_metadata->getBrands()) {
-      for (const auto& bv : *ua_metadata->getBrands()) {
+    if (ua_metadata->hasBrands()) {
+      for (const auto& bv : *ua_metadata->getBrands(nullptr)) {
         blink::UserAgentBrandVersion out_bv;
         out_bv.brand = bv->getBrand().Ascii();
         out_bv.major_version = bv->getVersion().Ascii();
         ua_metadata_override_->brand_version_list.push_back(std::move(out_bv));
       }
+    } else {
+      ua_metadata_override_->brand_version_list =
+          std::move(default_ua_metadata.brand_version_list);
     }
-    ua_metadata_override_->full_version = ua_metadata->getFullVersion().Ascii();
+
+    if (ua_metadata->hasFullVersion()) {
+      ua_metadata_override_->full_version =
+          ua_metadata->getFullVersion("").Ascii();
+    } else {
+      ua_metadata_override_->full_version =
+          std::move(default_ua_metadata.full_version);
+    }
     ua_metadata_override_->platform = ua_metadata->getPlatform().Ascii();
     ua_metadata_override_->platform_version =
         ua_metadata->getPlatformVersion().Ascii();
diff --git a/third_party/blink/renderer/core/inspector/inspector_io_agent.h b/third_party/blink/renderer/core/inspector/inspector_io_agent.h
index d8726c7..61a5a961 100644
--- a/third_party/blink/renderer/core/inspector/inspector_io_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_io_agent.h
@@ -25,10 +25,9 @@
     : public InspectorBaseAgent<protocol::IO::Metainfo> {
  public:
   InspectorIOAgent(v8::Isolate*, v8_inspector::V8InspectorSession*);
-
- private:
   ~InspectorIOAgent() override;
 
+ private:
   void Restore() override {}
 
   // Called from the front-end.
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
index ad4dfba9..49b13aa 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
@@ -16,12 +16,7 @@
 
 bool LayoutNGTableSection::IsEmpty() const {
   NOT_DESTROYED();
-  for (LayoutObject* child = FirstChild(); child;
-       child = child->NextSibling()) {
-    if (!To<LayoutNGTableRow>(child)->IsEmpty())
-      return false;
-  }
-  return true;
+  return !FirstChild();
 }
 
 LayoutNGTable* LayoutNGTableSection::Table() const {
diff --git a/third_party/blink/renderer/core/layout/scroll_anchor.h b/third_party/blink/renderer/core/layout/scroll_anchor.h
index 7deea3e..7fdc92e 100644
--- a/third_party/blink/renderer/core/layout/scroll_anchor.h
+++ b/third_party/blink/renderer/core/layout/scroll_anchor.h
@@ -13,6 +13,7 @@
 namespace blink {
 
 class LayoutObject;
+class Node;
 class ScrollableArea;
 
 static const int kMaxSerializedSelectorLength = 500;
diff --git a/third_party/blink/renderer/core/page/link_highlight.h b/third_party/blink/renderer/core/page/link_highlight.h
index 7dc64a7..7a3d680 100644
--- a/third_party/blink/renderer/core/page/link_highlight.h
+++ b/third_party/blink/renderer/core/page/link_highlight.h
@@ -16,12 +16,13 @@
 }
 
 namespace blink {
-class GraphicsContext;
-class Page;
-class LinkHighlightImpl;
 class CompositorAnimationTimeline;
-class LocalFrame;
+class GraphicsContext;
+class LinkHighlightImpl;
 class LayoutObject;
+class LocalFrame;
+class Node;
+class Page;
 
 class CORE_EXPORT LinkHighlight final : public GarbageCollected<LinkHighlight> {
  public:
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h
index ae6cdfec..7f19992 100644
--- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h
+++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.h
@@ -14,6 +14,7 @@
 
 class LocalFrame;
 class KURL;
+class Node;
 
 // An element fragment anchor is a FragmentAnchor based on a single element.
 // This is the traditional fragment anchor of the web. For example, the fragment
diff --git a/third_party/blink/renderer/core/paint/box_painter_base.h b/third_party/blink/renderer/core/paint/box_painter_base.h
index 42f5ee3..c6190bc8 100644
--- a/third_party/blink/renderer/core/paint/box_painter_base.h
+++ b/third_party/blink/renderer/core/paint/box_painter_base.h
@@ -26,6 +26,7 @@
 class ImageResourceObserver;
 class IntRect;
 class LayoutBox;
+class Node;
 struct PaintInfo;
 struct PhysicalOffset;
 struct PhysicalRect;
diff --git a/third_party/blink/renderer/core/paint/text_painter_base.h b/third_party/blink/renderer/core/paint/text_painter_base.h
index 82c49b7..0efa0b5 100644
--- a/third_party/blink/renderer/core/paint/text_painter_base.h
+++ b/third_party/blink/renderer/core/paint/text_painter_base.h
@@ -23,6 +23,7 @@
 class Document;
 class GraphicsContext;
 class GraphicsContextStateSaver;
+class Node;
 class TextDecorationOffsetBase;
 struct PaintInfo;
 
diff --git a/third_party/blink/renderer/core/timing/performance_mark.h b/third_party/blink/renderer/core/timing/performance_mark.h
index 2ae62a7..ea3a650 100644
--- a/third_party/blink/renderer/core/timing/performance_mark.h
+++ b/third_party/blink/renderer/core/timing/performance_mark.h
@@ -58,6 +58,7 @@
                   double start_time,
                   scoped_refptr<SerializedScriptValue>,
                   ExceptionState& exception_state);
+  ~PerformanceMark() override = default;
 
   AtomicString entryType() const override;
   PerformanceEntryType EntryTypeEnum() const override;
@@ -69,8 +70,6 @@
   void Trace(Visitor*) const override;
 
  private:
-  ~PerformanceMark() override = default;
-
   scoped_refptr<SerializedScriptValue> serialized_detail_;
   // In order to prevent cross-world reference leak, we create a copy of the
   // detail for each world.
diff --git a/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl b/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl
index 1f6af0e..e733b3e 100644
--- a/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl
@@ -4,5 +4,5 @@
 
 // https://wicg.github.io/file-system-access/#dictdef-directorypickeroptions
 dictionary DirectoryPickerOptions {
-  [RuntimeEnabled=FileSystemAccessAPIExperimental] CommonDirectory? startIn;
+  [RuntimeEnabled=FileSystemAccessAPIExperimental] WellKnownDirectory? startIn;
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl b/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl
index 0b73e5cd..24ba2e2 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-enum CommonDirectory {
+enum WellKnownDirectory {
   "desktop",
   "documents",
   "downloads",
@@ -15,5 +15,5 @@
 dictionary FilePickerOptions {
   sequence<FilePickerAcceptType> types;
   boolean excludeAcceptAllOption = false;
-  [RuntimeEnabled=FileSystemAccessAPIExperimental] CommonDirectory? startIn;
+  [RuntimeEnabled=FileSystemAccessAPIExperimental] WellKnownDirectory? startIn;
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc b/third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc
index 5f6b72d..f564d9d 100644
--- a/third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc
+++ b/third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc
@@ -168,26 +168,26 @@
   }
 }
 
-mojom::blink::CommonDirectory ConvertCommonDirectory(
-    const String& starting_directory,
+mojom::blink::WellKnownDirectory ConvertWellKnownDirectory(
+    const String& directory,
     ExceptionState& exception_state) {
-  if (starting_directory == "")
-    return mojom::blink::CommonDirectory::kDefault;
-  else if (starting_directory == "desktop")
-    return mojom::blink::CommonDirectory::kDirDesktop;
-  else if (starting_directory == "documents")
-    return mojom::blink::CommonDirectory::kDirDocuments;
-  else if (starting_directory == "downloads")
-    return mojom::blink::CommonDirectory::kDirDownloads;
-  else if (starting_directory == "music")
-    return mojom::blink::CommonDirectory::kDirMusic;
-  else if (starting_directory == "pictures")
-    return mojom::blink::CommonDirectory::kDirPictures;
-  else if (starting_directory == "videos")
-    return mojom::blink::CommonDirectory::kDirVideos;
+  if (directory == "")
+    return mojom::blink::WellKnownDirectory::kDefault;
+  else if (directory == "desktop")
+    return mojom::blink::WellKnownDirectory::kDirDesktop;
+  else if (directory == "documents")
+    return mojom::blink::WellKnownDirectory::kDirDocuments;
+  else if (directory == "downloads")
+    return mojom::blink::WellKnownDirectory::kDirDownloads;
+  else if (directory == "music")
+    return mojom::blink::WellKnownDirectory::kDirMusic;
+  else if (directory == "pictures")
+    return mojom::blink::WellKnownDirectory::kDirPictures;
+  else if (directory == "videos")
+    return mojom::blink::WellKnownDirectory::kDirVideos;
 
   NOTREACHED();
-  return mojom::blink::CommonDirectory::kDefault;
+  return mojom::blink::WellKnownDirectory::kDefault;
 }
 
 ScriptPromise ShowFilePickerImpl(
@@ -195,7 +195,7 @@
     LocalDOMWindow& window,
     mojom::blink::ChooseFileSystemEntryType chooser_type,
     Vector<mojom::blink::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
-    mojom::blink::CommonDirectory starting_directory,
+    mojom::blink::WellKnownDirectory well_known_starting_directory,
     bool accept_all,
     bool return_as_sequence) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
@@ -213,8 +213,8 @@
   raw_manager->ChooseEntries(
       chooser_type, std::move(accepts),
       RuntimeEnabledFeatures::FileSystemAccessAPIExperimentalEnabled()
-          ? starting_directory
-          : mojom::blink::CommonDirectory::kDefault,
+          ? std::move(well_known_starting_directory)
+          : mojom::blink::WellKnownDirectory::kDefault,
       accept_all,
       WTF::Bind(
           [](ScriptPromiseResolver* resolver,
@@ -283,9 +283,10 @@
     return ScriptPromise();
   }
 
-  auto starting_directory = mojom::blink::CommonDirectory::kDefault;
+  auto well_known_starting_directory =
+      mojom::blink::WellKnownDirectory::kDefault;
   if (options->hasStartInNonNull()) {
-    starting_directory = ConvertCommonDirectory(
+    well_known_starting_directory = ConvertWellKnownDirectory(
         IDLEnumAsString(options->startInNonNull()), exception_state);
     if (exception_state.HadException())
       return ScriptPromise();
@@ -300,7 +301,7 @@
       options->multiple()
           ? mojom::blink::ChooseFileSystemEntryType::kOpenMultipleFiles
           : mojom::blink::ChooseFileSystemEntryType::kOpenFile,
-      std::move(accepts), starting_directory,
+      std::move(accepts), std::move(well_known_starting_directory),
       !options->excludeAcceptAllOption(),
       /*return_as_sequence=*/true);
 }
@@ -324,9 +325,10 @@
     return ScriptPromise();
   }
 
-  auto starting_directory = mojom::blink::CommonDirectory::kDefault;
+  auto well_known_starting_directory =
+      mojom::blink::WellKnownDirectory::kDefault;
   if (options->hasStartInNonNull()) {
-    starting_directory = ConvertCommonDirectory(
+    well_known_starting_directory = ConvertWellKnownDirectory(
         IDLEnumAsString(options->startInNonNull()), exception_state);
     if (exception_state.HadException())
       return ScriptPromise();
@@ -336,11 +338,11 @@
   if (exception_state.HadException())
     return ScriptPromise();
 
-  return ShowFilePickerImpl(script_state, window,
-                            mojom::blink::ChooseFileSystemEntryType::kSaveFile,
-                            std::move(accepts), starting_directory,
-                            !options->excludeAcceptAllOption(),
-                            /*return_as_sequence=*/false);
+  return ShowFilePickerImpl(
+      script_state, window, mojom::blink::ChooseFileSystemEntryType::kSaveFile,
+      std::move(accepts), std::move(well_known_starting_directory),
+      !options->excludeAcceptAllOption(),
+      /*return_as_sequence=*/false);
 }
 
 // static
@@ -351,9 +353,10 @@
     ExceptionState& exception_state) {
   UseCounter::Count(window, WebFeature::kFileSystemPickerMethod);
 
-  auto starting_directory = mojom::blink::CommonDirectory::kDefault;
+  auto well_known_starting_directory =
+      mojom::blink::WellKnownDirectory::kDefault;
   if (options->hasStartInNonNull()) {
-    starting_directory = ConvertCommonDirectory(
+    well_known_starting_directory = ConvertWellKnownDirectory(
         IDLEnumAsString(options->startInNonNull()), exception_state);
     if (exception_state.HadException())
       return ScriptPromise();
@@ -365,7 +368,7 @@
   return ShowFilePickerImpl(
       script_state, window,
       mojom::blink::ChooseFileSystemEntryType::kOpenDirectory, {},
-      starting_directory,
+      std::move(well_known_starting_directory),
       /*accept_all=*/true,
       /*return_as_sequence=*/false);
 }
diff --git a/third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc b/third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc
index 6df0d661..0f41733 100644
--- a/third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc
+++ b/third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc
@@ -60,7 +60,7 @@
   void ChooseEntries(
       mojom::ChooseFileSystemEntryType type,
       WTF::Vector<mojom::blink::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
-      mojom::CommonDirectory starting_directory,
+      mojom::WellKnownDirectory well_known_starting_directory,
       bool include_accepts_all,
       ChooseEntriesCallback callback) override {
     if (choose_entries_response_callback_) {
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source.cc
index f2459f2f..653aed5 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source.cc
@@ -87,6 +87,12 @@
   return Controller()->DesiredSize();
 }
 
+void MediaStreamAudioTrackUnderlyingSource::ContextDestroyed() {
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
+  UnderlyingSourceBase::ContextDestroyed();
+  queue_.clear();
+}
+
 void MediaStreamAudioTrackUnderlyingSource::Close() {
   DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
   DisconnectFromTrack();
@@ -152,7 +158,9 @@
 
 void MediaStreamAudioTrackUnderlyingSource::SendFrameToStream(
     std::unique_ptr<AudioFrameSerializationData> queue_data) {
-  DCHECK(Controller());
+  if (!Controller())
+    return;
+
   AudioFrame* audio_frame =
       MakeGarbageCollected<AudioFrame>(std::move(queue_data));
   Controller()->Enqueue(audio_frame);
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source.h b/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source.h
index 0f90ee1..77e9c862 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source.h
@@ -40,6 +40,9 @@
               base::TimeTicks estimated_capture_time) override;
   void OnSetFormat(const media::AudioParameters& params) override;
 
+  // ExecutionLifecycleObserver
+  void ContextDestroyed() override;
+
   MediaStreamComponent* Track() const { return track_.Get(); }
   wtf_size_t MaxQueueSize() const { return max_queue_size_; }
 
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source_test.cc
index 570fdea..777f7bb7 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source_test.cc
@@ -91,9 +91,8 @@
   PushableMediaStreamAudioSource* const pushable_audio_source_;
 };
 
-// TODO(crbug.com/1157608): Tests are failing on some platforms.
 TEST_F(MediaStreamAudioTrackUnderlyingSourceTest,
-       DISABLED_AudioFrameFlowsThroughStreamAndCloses) {
+       AudioFrameFlowsThroughStreamAndCloses) {
   V8TestingScope v8_scope;
   ScriptState* script_state = v8_scope.GetScriptState();
   auto* source = CreateSource(script_state);
@@ -132,9 +131,8 @@
   EXPECT_FALSE(source->Track());
 }
 
-// TODO(crbug.com/1157608): Tests are failing on some platforms.
 TEST_F(MediaStreamAudioTrackUnderlyingSourceTest,
-       DISABLED_DropOldFramesWhenQueueIsFull) {
+       DropOldFramesWhenQueueIsFull) {
   V8TestingScope v8_scope;
   ScriptState* script_state = v8_scope.GetScriptState();
   const wtf_size_t buffer_size = 5;
@@ -193,9 +191,8 @@
   WebMediaStreamAudioSink::RemoveFromAudioTrack(&mock_sink, track);
 }
 
-// TODO(crbug.com/1157608): Tests are failing on some platforms.
 TEST_F(MediaStreamAudioTrackUnderlyingSourceTest,
-       DISABLED_BypassQueueAfterPullWithEmptyBuffer) {
+       BypassQueueAfterPullWithEmptyBuffer) {
   V8TestingScope v8_scope;
   ScriptState* script_state = v8_scope.GetScriptState();
   auto* source = CreateSource(script_state);
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_generator.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_generator.cc
index 115dbc1..f540b14 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_generator.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_generator.cc
@@ -111,7 +111,7 @@
     const String& kind,
     ExceptionState& exception_state) {
   if (!script_state->ContextIsValid()) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Invalid context");
     return nullptr;
   }
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_processor.cc
index 5daeb7d2..7d85463 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_processor.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_audio_track_underlying_source.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_utils.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source.h"
@@ -28,9 +29,14 @@
 }
 
 ReadableStream* MediaStreamTrackProcessor::readable(ScriptState* script_state) {
-  DCHECK_EQ(input_track_->Source()->GetType(), MediaStreamSource::kTypeVideo);
-  if (!source_stream_)
+  if (source_stream_)
+    return source_stream_;
+
+  if (input_track_->Source()->GetType() == MediaStreamSource::kTypeVideo)
     CreateVideoSourceStream(script_state);
+  else
+    CreateAudioSourceStream(script_state);
+
   return source_stream_;
 }
 
@@ -44,26 +50,28 @@
       script_state, video_underlying_source_, /*high_water_mark=*/0);
 }
 
+void MediaStreamTrackProcessor::CreateAudioSourceStream(
+    ScriptState* script_state) {
+  DCHECK(!source_stream_);
+  audio_underlying_source_ =
+      MakeGarbageCollected<MediaStreamAudioTrackUnderlyingSource>(
+          script_state, input_track_, buffer_size_);
+  source_stream_ = ReadableStream::CreateWithCountQueueingStrategy(
+      script_state, audio_underlying_source_, /*high_water_mark=*/0);
+}
+
 MediaStreamTrackProcessor* MediaStreamTrackProcessor::Create(
     ScriptState* script_state,
     MediaStreamTrack* track,
     uint16_t buffer_size,
     ExceptionState& exception_state) {
   if (!track) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
-                                      "Input track cannot be null");
+    exception_state.ThrowTypeError("Input track cannot be null");
     return nullptr;
   }
 
   if (track->readyState() == "ended") {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Input track cannot be ended");
-    return nullptr;
-  }
-
-  if (track->kind() != "video") {
-    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
-                                      "Only video tracks are supported");
+    exception_state.ThrowTypeError("Input track cannot be ended");
     return nullptr;
   }
 
@@ -83,8 +91,7 @@
     MediaStreamTrack* track,
     ExceptionState& exception_state) {
   if (!track) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
-                                      "Input track cannot be null");
+    exception_state.ThrowTypeError("Input track cannot be null");
     return nullptr;
   }
   // Using 1 as default buffer size for video since by default we do not want
@@ -98,6 +105,7 @@
 
 void MediaStreamTrackProcessor::Trace(Visitor* visitor) const {
   visitor->Trace(input_track_);
+  visitor->Trace(audio_underlying_source_);
   visitor->Trace(video_underlying_source_);
   visitor->Trace(source_stream_);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_processor.h b/third_party/blink/renderer/modules/mediastream/media_stream_track_processor.h
index 41eadea..370fb543 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_processor.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_processor.h
@@ -14,6 +14,7 @@
 
 class MediaStreamComponent;
 class MediaStreamVideoTrackUnderlyingSource;
+class MediaStreamAudioTrackUnderlyingSource;
 class ScriptState;
 class ReadableStream;
 
@@ -44,9 +45,11 @@
 
  private:
   void CreateVideoSourceStream(ScriptState* script_state);
+  void CreateAudioSourceStream(ScriptState* script_state);
 
   Member<MediaStreamComponent> input_track_;
   Member<MediaStreamVideoTrackUnderlyingSource> video_underlying_source_;
+  Member<MediaStreamAudioTrackUnderlyingSource> audio_underlying_source_;
   Member<ReadableStream> source_stream_;
   uint16_t buffer_size_;
 };
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_processor_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_processor_test.cc
index 449489e..5b1f7622 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_processor_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_processor_test.cc
@@ -18,9 +18,12 @@
 #include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_track_generator.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
+#include "third_party/blink/renderer/modules/mediastream/mock_media_stream_audio_sink.h"
 #include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_sink.h"
 #include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h"
+#include "third_party/blink/renderer/modules/mediastream/pushable_media_stream_audio_source.h"
 #include "third_party/blink/renderer/modules/mediastream/pushable_media_stream_video_source.h"
+#include "third_party/blink/renderer/modules/webcodecs/audio_frame_serialization_data.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h"
@@ -33,6 +36,13 @@
 
 namespace {
 
+std::unique_ptr<PushableMediaStreamAudioSource> CreatePushableAudioSource() {
+  // Use the IO thread for testing purposes.
+  return std::make_unique<PushableMediaStreamAudioSource>(
+      Thread::MainThread()->GetTaskRunner(),
+      Platform::Current()->GetIOTaskRunner());
+}
+
 PushableMediaStreamVideoSource* CreatePushableVideoSource() {
   PushableMediaStreamVideoSource* pushable_video_source =
       new PushableMediaStreamVideoSource();
@@ -53,21 +63,22 @@
                    /*enabled=*/true));
 }
 
-MediaStreamTrack* CreateAudioMediaStreamTrack(ExecutionContext* context) {
-  std::unique_ptr<MediaStreamAudioSource> audio_source =
-      std::make_unique<MediaStreamAudioSource>(
-          blink::scheduler::GetSingleThreadTaskRunnerForTesting(),
-          /*is_local_source=*/false);
+MediaStreamTrack* CreateAudioMediaStreamTrack(
+    ExecutionContext* context,
+    std::unique_ptr<PushableMediaStreamAudioSource> source) {
+  auto* source_ptr = source.get();
+
   MediaStreamSource* media_stream_source =
       MakeGarbageCollected<MediaStreamSource>(
           "source_id", MediaStreamSource::kTypeAudio, "source_name",
-          /*is_remote=*/false);
-  media_stream_source->SetPlatformSource(std::move(audio_source));
-  std::unique_ptr<MediaStreamAudioTrack> audio_track =
-      std::make_unique<MediaStreamAudioTrack>(/*is_local_track=*/false);
+          /*remote=*/false);
+  media_stream_source->SetPlatformSource(std::move(source));
+
   MediaStreamComponent* component =
       MakeGarbageCollected<MediaStreamComponent>(media_stream_source);
-  component->SetPlatformTrack(std::move(audio_track));
+
+  source_ptr->ConnectToTrack(component);
+
   return MakeGarbageCollected<MediaStreamTrack>(context, component);
 }
 
@@ -131,6 +142,53 @@
   EXPECT_EQ(mock_video_sink.last_frame(), frame);
 }
 
+TEST_F(MediaStreamTrackProcessorTest, AudioFramesAreExposed) {
+  V8TestingScope v8_scope;
+  ScriptState* script_state = v8_scope.GetScriptState();
+  ExceptionState& exception_state = v8_scope.GetExceptionState();
+  std::unique_ptr<PushableMediaStreamAudioSource> pushable_audio_source =
+      CreatePushableAudioSource();
+  auto* pushable_source_ptr = pushable_audio_source.get();
+  MediaStreamTrackProcessor* track_processor =
+      MediaStreamTrackProcessor::Create(
+          script_state,
+          CreateAudioMediaStreamTrack(v8_scope.GetExecutionContext(),
+                                      std::move(pushable_audio_source)),
+          exception_state);
+  EXPECT_FALSE(exception_state.HadException());
+  EXPECT_EQ(track_processor->input_track()->Source()->GetPlatformSource(),
+            pushable_source_ptr);
+
+  MockMediaStreamAudioSink mock_audio_sink;
+  WebMediaStreamAudioSink::AddToAudioTrack(
+      &mock_audio_sink, WebMediaStreamTrack(track_processor->input_track()));
+
+  auto* reader =
+      track_processor->readable(script_state)
+          ->GetDefaultReaderForTesting(script_state, exception_state);
+  EXPECT_FALSE(exception_state.HadException());
+
+  // Deliver a frame.
+  base::RunLoop sink_loop;
+  EXPECT_CALL(mock_audio_sink, OnData(_, _))
+      .WillOnce(base::test::RunOnceClosure(sink_loop.QuitClosure()));
+  pushable_source_ptr->PushAudioData(AudioFrameSerializationData::Wrap(
+      media::AudioBus::Create(/*channels=*/2, /*frames=*/100),
+      /*sample_rate=*/8000, base::TimeDelta::FromSeconds(1)));
+
+  ScriptPromiseTester read_tester(script_state,
+                                  reader->read(script_state, exception_state));
+  EXPECT_FALSE(read_tester.IsFulfilled());
+  read_tester.WaitUntilSettled();
+  EXPECT_FALSE(exception_state.HadException());
+  EXPECT_TRUE(read_tester.IsFulfilled());
+  EXPECT_TRUE(read_tester.Value().IsObject());
+  sink_loop.Run();
+
+  WebMediaStreamAudioSink::RemoveFromAudioTrack(
+      &mock_audio_sink, WebMediaStreamTrack(track_processor->input_track()));
+}
+
 TEST_F(MediaStreamTrackProcessorTest, CanceledReadableDisconnects) {
   V8TestingScope v8_scope;
   ScriptState* script_state = v8_scope.GetScriptState();
@@ -233,8 +291,8 @@
 
   EXPECT_EQ(track_processor, nullptr);
   EXPECT_TRUE(exception_state.HadException());
-  EXPECT_EQ(static_cast<DOMExceptionCode>(v8_scope.GetExceptionState().Code()),
-            DOMExceptionCode::kOperationError);
+  EXPECT_EQ(static_cast<ESErrorType>(v8_scope.GetExceptionState().Code()),
+            ESErrorType::kTypeError);
 }
 
 TEST_F(MediaStreamTrackProcessorTest, EndedTrack) {
@@ -251,25 +309,8 @@
 
   EXPECT_EQ(track_processor, nullptr);
   EXPECT_TRUE(exception_state.HadException());
-  EXPECT_EQ(static_cast<DOMExceptionCode>(v8_scope.GetExceptionState().Code()),
-            DOMExceptionCode::kInvalidStateError);
-}
-
-// TODO(crbug.com/1142955): Add support for audio.
-TEST_F(MediaStreamTrackProcessorTest, Audio) {
-  V8TestingScope v8_scope;
-  ScriptState* script_state = v8_scope.GetScriptState();
-  ExceptionState& exception_state = v8_scope.GetExceptionState();
-  MediaStreamTrack* media_stream_track =
-      CreateAudioMediaStreamTrack(v8_scope.GetExecutionContext());
-  MediaStreamTrackProcessor* track_processor =
-      MediaStreamTrackProcessor::Create(script_state, media_stream_track,
-                                        exception_state);
-
-  EXPECT_EQ(track_processor, nullptr);
-  EXPECT_TRUE(exception_state.HadException());
-  EXPECT_EQ(static_cast<DOMExceptionCode>(v8_scope.GetExceptionState().Code()),
-            DOMExceptionCode::kNotSupportedError);
+  EXPECT_EQ(static_cast<ESErrorType>(v8_scope.GetExceptionState().Code()),
+            ESErrorType::kTypeError);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source.cc
index f2d8558..0fe7bdb8 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source.cc
@@ -80,6 +80,12 @@
   return Controller()->DesiredSize();
 }
 
+void MediaStreamVideoTrackUnderlyingSource::ContextDestroyed() {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  UnderlyingSourceBase::ContextDestroyed();
+  queue_.clear();
+}
+
 void MediaStreamVideoTrackUnderlyingSource::Close() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   DisconnectFromTrack();
@@ -132,7 +138,9 @@
 void MediaStreamVideoTrackUnderlyingSource::SendFrameToStream(
     scoped_refptr<media::VideoFrame> media_frame) {
   DCHECK(media_frame);
-  DCHECK(Controller());
+  if (!Controller())
+    return;
+
   VideoFrame* video_frame = MakeGarbageCollected<VideoFrame>(
       std::move(media_frame), GetExecutionContext());
   Controller()->Enqueue(video_frame);
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source.h b/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source.h
index afa6dc3..ad436c9e 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_track_underlying_source.h
@@ -32,6 +32,9 @@
   ScriptPromise Start(ScriptState*) override;
   ScriptPromise Cancel(ScriptState*, ScriptValue reason) override;
 
+  // ExecutionLifecycleObserver
+  void ContextDestroyed() override;
+
   MediaStreamComponent* Track() const { return track_.Get(); }
   wtf_size_t MaxQueueSize() const { return max_queue_size_; }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.h b/third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.h
index b999131..b0d87605 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/sctp_transport_proxy.h
@@ -8,7 +8,9 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/webrtc/api/sctp_transport_interface.h"
 
 // The SctpTransportProxy class takes care of thread-jumping when
diff --git a/third_party/blink/renderer/modules/shapedetection/barcode_detector.h b/third_party/blink/renderer/modules/shapedetection/barcode_detector.h
index 4217f89..a1898d9 100644
--- a/third_party/blink/renderer/modules/shapedetection/barcode_detector.h
+++ b/third_party/blink/renderer/modules/shapedetection/barcode_detector.h
@@ -36,12 +36,11 @@
   explicit BarcodeDetector(ExecutionContext*,
                            const BarcodeDetectorOptions*,
                            ExceptionState& exception_state);
+  ~BarcodeDetector() override = default;
 
   void Trace(Visitor*) const override;
 
  private:
-  ~BarcodeDetector() override = default;
-
   ScriptPromise DoDetect(ScriptPromiseResolver*, SkBitmap) override;
   void OnDetectBarcodes(
       ScriptPromiseResolver*,
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet.cc
index 7045f8f..581f659 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/workers/threaded_worklet_object_proxy.h"
 #include "third_party/blink/renderer/core/workers/worker_clients.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_worklet_messaging_proxy.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_worklet_node.h"
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_frame.cc b/third_party/blink/renderer/modules/webcodecs/audio_frame.cc
index 81fa4e3..7a15007 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_frame.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_frame.cc
@@ -12,33 +12,6 @@
 
 namespace blink {
 
-namespace {
-class SharedAudioData final : public AudioFrameSerializationData {
- public:
-  SharedAudioData(std::unique_ptr<SharedAudioBuffer> shared_buffer,
-                  base::TimeDelta timestamp)
-      : AudioFrameSerializationData(shared_buffer->sampleRate(), timestamp),
-        backing_buffer_(std::move(shared_buffer)) {
-    buffer_wrapper_ =
-        media::AudioBus::CreateWrapper(backing_buffer_->numberOfChannels());
-
-    for (int i = 0; i < buffer_wrapper_->channels(); ++i) {
-      float* channel_data =
-          static_cast<float*>(backing_buffer_->channels()[i].Data());
-      buffer_wrapper_->SetChannelData(i, channel_data);
-    }
-    buffer_wrapper_->set_frames(backing_buffer_->length());
-  }
-  ~SharedAudioData() override = default;
-
-  media::AudioBus* data() override { return buffer_wrapper_.get(); }
-
- private:
-  std::unique_ptr<media::AudioBus> buffer_wrapper_;
-  std::unique_ptr<SharedAudioBuffer> backing_buffer_;
-};
-}  // namespace
-
 // static
 AudioFrame* AudioFrame::Create(AudioFrameInit* init,
                                ExceptionState& exception_state) {
@@ -54,55 +27,67 @@
   buffer_ = AudioBuffer::CreateUninitialized(
       buffer->channel_count(), buffer->frame_count(), buffer->sample_rate());
 
-  // Wrap blink buffer with a media::AudioBus so we can interface with
-  // media::AudioBuffer to copy the data out.
-  auto media_bus_wrapper =
-      media::AudioBus::CreateWrapper(buffer->channel_count());
-  for (int i = 0; i < media_bus_wrapper->channels(); ++i) {
-    DCHECK_EQ(buffer_->getChannelData(i)->byteLength(),
-              buffer->frame_count() * sizeof(float));
-    float* channel_data = buffer_->getChannelData(i)->Data();
-    media_bus_wrapper->SetChannelData(i, channel_data);
-  }
-  media_bus_wrapper->set_frames(buffer->frame_count());
+  auto converted_data =
+      media::AudioBus::Create(buffer->channel_count(), buffer->frame_count());
 
-  // Copy the frames.
+  // Copy the frames, converting from |buffer|'s internal format to float.
+  // TODO(https://crbug.com/1171840): Add a version of
+  // media::AudioBus::ReadFrame that can directly read into |buffer_|'s data.
   // TODO(chcunningham): Avoid this copy by refactoring blink::AudioBuffer to
   // ref a media::AudioBuffer and only copy for calls to copyToChannel().
-  buffer->ReadFrames(media_bus_wrapper->frames(), 0 /* source_frame_offset */,
-                     0 /* dest_frame_offset */, media_bus_wrapper.get());
+  buffer->ReadFrames(converted_data->frames(), 0 /* source_frame_offset */,
+                     0 /* dest_frame_offset */, converted_data.get());
+
+  CopyDataToInternalBuffer(converted_data.get());
+}
+
+void AudioFrame::CopyDataToInternalBuffer(media::AudioBus* data) {
+  DCHECK_EQ(static_cast<int>(buffer_->numberOfChannels()), data->channels());
+  DCHECK_EQ(static_cast<int>(buffer_->length()), data->frames());
+
+  for (int i = 0; i < data->channels(); ++i) {
+    size_t byte_length = buffer_->getChannelData(i)->byteLength();
+    DCHECK_EQ(byte_length, data->frames() * sizeof(float));
+    float* buffer_data_dest = buffer_->getChannelData(i)->Data();
+    memcpy(data->channel(i), buffer_data_dest, byte_length);
+  }
 }
 
 std::unique_ptr<AudioFrameSerializationData>
 AudioFrame::GetSerializationData() {
   DCHECK(buffer_);
-  return std::make_unique<SharedAudioData>(
-      buffer_->CreateSharedAudioBuffer(),
+
+  // Copy buffer unaligned memory into media::AudioBus' aligned memory.
+  // TODO(https://crbug.com/1168418): reevaluate if this copy is necessary after
+  // our changes. E.g. If we can ever guarantee AudioBuffer's memory alignment,
+  // we could save this copy here, by using buffer_->GetSharedAudioBuffer() and
+  // wrapping it directly.
+  auto data_copy =
+      media::AudioBus::Create(buffer_->numberOfChannels(), buffer_->length());
+
+  for (int i = 0; i < data_copy->channels(); ++i) {
+    size_t byte_length = buffer_->getChannelData(i)->byteLength();
+    DCHECK_EQ(byte_length, data_copy->frames() * sizeof(float));
+    float* buffer_data_src = buffer_->getChannelData(i)->Data();
+    memcpy(buffer_data_src, data_copy->channel(i), byte_length);
+  }
+
+  return AudioFrameSerializationData::Wrap(
+      std::move(data_copy), buffer_->sampleRate(),
       base::TimeDelta::FromMicroseconds(timestamp_));
 }
 
 AudioFrame::AudioFrame(std::unique_ptr<AudioFrameSerializationData> data)
     : timestamp_(data->timestamp().InMicroseconds()) {
-  const media::AudioBus& audio_bus = *data->data();
-  buffer_ = AudioBuffer::CreateUninitialized(
-      audio_bus.channels(), audio_bus.frames(), data->sample_rate());
+  media::AudioBus* data_bus = data->data();
 
-  // Wrap blink buffer with a media::AudioBus so we can interface with
-  // media::AudioBuffer to copy the data out.
-  auto audio_buffer_wrapper =
-      media::AudioBus::CreateWrapper(audio_bus.channels());
-  for (int i = 0; i < audio_buffer_wrapper->channels(); ++i) {
-    DCHECK_EQ(buffer_->getChannelData(i)->byteLength(),
-              audio_bus.frames() * sizeof(float));
-    float* channel_data = buffer_->getChannelData(i)->Data();
-    audio_buffer_wrapper->SetChannelData(i, channel_data);
-  }
-  audio_buffer_wrapper->set_frames(audio_bus.frames());
+  buffer_ = AudioBuffer::CreateUninitialized(
+      data_bus->channels(), data_bus->frames(), data->sample_rate());
 
   // Copy the frames.
   // TODO(https://crbug.com/1168418): Avoid this copy by refactoring
   // blink::AudioBuffer accept a serializable audio data backing object.
-  audio_bus.CopyTo(audio_buffer_wrapper.get());
+  CopyDataToInternalBuffer(data_bus);
 }
 
 void AudioFrame::close() {
@@ -112,6 +97,7 @@
 uint64_t AudioFrame::timestamp() const {
   return timestamp_;
 }
+
 AudioBuffer* AudioFrame::buffer() const {
   return buffer_;
 }
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_frame.h b/third_party/blink/renderer/modules/webcodecs/audio_frame.h
index ab27cdda..c4c35d0a 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_frame.h
+++ b/third_party/blink/renderer/modules/webcodecs/audio_frame.h
@@ -39,6 +39,8 @@
   void Trace(Visitor*) const override;
 
  private:
+  void CopyDataToInternalBuffer(media::AudioBus* data);
+
   uint64_t timestamp_;
   Member<AudioBuffer> buffer_;
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/idls.gni b/third_party/blink/renderer/modules/webcodecs/idls.gni
index 71262fa2..df42ce14 100644
--- a/third_party/blink/renderer/modules/webcodecs/idls.gni
+++ b/third_party/blink/renderer/modules/webcodecs/idls.gni
@@ -34,6 +34,7 @@
   "encoded_video_chunk_init.idl",
   "encoded_audio_chunk_init.idl",
   "image_decoder_init.idl",
+  "image_decode_options.idl",
   "image_frame.idl",
   "image_track.idl",
   "plane_init.idl",
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decode_options.idl b/third_party/blink/renderer/modules/webcodecs/image_decode_options.idl
new file mode 100644
index 0000000..a38fdff
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/image_decode_options.idl
@@ -0,0 +1,15 @@
+// Copyright 2020 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.
+
+// https://github.com/WICG/web-codecs
+
+dictionary ImageDecodeOptions {
+  // The index of the frame to decode.
+  unsigned long frameIndex = 0;
+
+  // When |completeFramesOnly| is set to false, partial progressive frames will
+  // be returned. When in this mode, decode() calls will resolve only once per
+  // new partial image at |frameIndex| until the frame is complete.
+  boolean completeFramesOnly = true;
+};
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder.idl b/third_party/blink/renderer/modules/webcodecs/image_decoder.idl
index a32837c..b31ea28 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder.idl
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder.idl
@@ -16,16 +16,11 @@
   // Returns true if ImageDecoder supports decoding of the given mime type.
   static boolean canDecodeType(USVString type);
 
-  // Decodes the frame at the given index. If we're still receiving data, this
-  // method will wait to resolve the promise until the given |frameIndex| is
-  // available or reject the promise if we receive all data or fail before
-  // |frameIndex| is available.
-  //
-  // When |completeFramesOnly| is set to false, partial progressive frames will
-  // be returned. When in this mode, decode() calls will resolve only once per
-  // new partial image at |frameIndex| until the frame is complete.
-  Promise<ImageFrame> decode(optional unsigned long frameIndex = 0,
-                             optional boolean completeFramesOnly = true);
+  // Decodes a frame using the given |options| or the first frame if no options
+  // are provided. If data is still being received, the promise won't be
+  // resolved or rejected until the given |options.frameIndex| is available,
+  // all data is received, or a decoding error occurs.
+  Promise<ImageFrame> decode(optional ImageDecodeOptions options);
 
   // Decodes only the metadata for an image; resolves the promise when metadata
   // can be decoded. Normally this is done automatically at construction time.
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc b/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc
index 59300963d..7a2d69a 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/public/common/mime_util/mime_util.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_image_decode_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_image_decoder_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_image_frame.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_image_track.h"
@@ -147,14 +148,14 @@
   DVLOG(1) << __func__;
 }
 
-ScriptPromise ImageDecoderExternal::decode(uint32_t frame_index,
-                                           bool complete_frames_only) {
+ScriptPromise ImageDecoderExternal::decode(const ImageDecodeOptions* options) {
   DVLOG(1) << __func__;
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state_);
   auto promise = resolver->Promise();
   pending_decodes_.push_back(MakeGarbageCollected<DecodeRequest>(
-      resolver, frame_index, complete_frames_only));
+      resolver, options ? options->frameIndex() : 0,
+      options ? options->completeFramesOnly() : true));
   MaybeSatisfyPendingDecodes();
   return promise;
 }
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_external.h b/third_party/blink/renderer/modules/webcodecs/image_decoder_external.h
index 68bc92d..dbb4fa7 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_external.h
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_external.h
@@ -20,6 +20,7 @@
 class ExceptionState;
 class ScriptState;
 class ImageBitmapOptions;
+class ImageDecodeOptions;
 class ImageDecoder;
 class ImageDecoderInit;
 class ImageFrameExternal;
@@ -48,7 +49,7 @@
   using ImageTrackList = HeapVector<Member<ImageTrackExternal>>;
 
   // image_decoder.idl implementation.
-  ScriptPromise decode(uint32_t frame_index, bool complete_frames_only);
+  ScriptPromise decode(const ImageDecodeOptions* options = nullptr);
   ScriptPromise decodeMetadata();
   void selectTrack(uint32_t track_id, ExceptionState&);
   uint32_t frameCount() const;
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc b/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc
index a138e67..fe943479 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_image_decode_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_image_decoder_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_image_frame.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_image_track.h"
@@ -43,6 +44,14 @@
         v8_scope->GetIsolate(), value.V8Value(), v8_scope->GetExceptionState());
   }
 
+  ImageDecodeOptions* MakeOptions(uint32_t frame_index = 0,
+                                  bool complete_frames_only = true) {
+    auto* options = MakeGarbageCollected<ImageDecodeOptions>();
+    options->setFrameIndex(frame_index);
+    options->setCompleteFramesOnly(complete_frames_only);
+    return options;
+  }
+
   scoped_refptr<SharedBuffer> ReadFile(StringView file_name) {
     StringBuilder file_path;
     file_path.Append(test::BlinkWebTestsDir());
@@ -138,7 +147,7 @@
   ArrayBufferContents contents;
   ASSERT_TRUE(buffer->Transfer(v8_scope.GetIsolate(), contents));
 
-  auto promise = decoder->decode(0, true);
+  auto promise = decoder->decode(MakeOptions(0, true));
   ScriptPromiseTester tester(v8_scope.GetScriptState(), promise);
   tester.WaitUntilSettled();
   ASSERT_TRUE(tester.IsRejected());
@@ -192,7 +201,7 @@
   EXPECT_EQ(tracks[0]->animated(), true);
 
   {
-    auto promise = decoder->decode(0, true);
+    auto promise = decoder->decode(MakeOptions(0, true));
     ScriptPromiseTester tester(v8_scope.GetScriptState(), promise);
     tester.WaitUntilSettled();
     ASSERT_TRUE(tester.IsFulfilled());
@@ -206,7 +215,7 @@
   }
 
   {
-    auto promise = decoder->decode(1, true);
+    auto promise = decoder->decode(MakeOptions(1, true));
     ScriptPromiseTester tester(v8_scope.GetScriptState(), promise);
     tester.WaitUntilSettled();
     ASSERT_TRUE(tester.IsFulfilled());
@@ -220,7 +229,7 @@
   }
 
   // Decoding past the end should result in a rejected promise.
-  auto promise = decoder->decode(3, true);
+  auto promise = decoder->decode(MakeOptions(3, true));
   ScriptPromiseTester tester(v8_scope.GetScriptState(), promise);
   tester.WaitUntilSettled();
   ASSERT_TRUE(tester.IsRejected());
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc b/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc
index 078cf98..9fb87fdf 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc
@@ -6,6 +6,7 @@
 #include "testing/libfuzzer/proto/lpm_interface.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_image_decode_options.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_image_decoder_init.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
@@ -131,14 +132,16 @@
                                      IGNORE_EXCEPTION_FOR_TESTING);
 
     if (image_decoder) {
+      Persistent<ImageDecodeOptions> options = ImageDecodeOptions::Create();
       // Promises will be fulfilled synchronously since we're using an array
       // buffer based source.
       for (auto& invocation : proto.invocations()) {
         switch (invocation.Api_case()) {
           case wc_fuzzer::ImageDecoderApiInvocation::kDecodeImage:
-            image_decoder->decode(
-                invocation.decode_image().frame_index(),
+            options->setFrameIndex(invocation.decode_image().frame_index());
+            options->setCompleteFramesOnly(
                 invocation.decode_image().complete_frames_only());
+            image_decoder->decode(options);
             break;
           case wc_fuzzer::ImageDecoderApiInvocation::kDecodeMetadata:
             image_decoder->decodeMetadata();
diff --git a/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.h b/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.h
index 3c137e1..cc14582 100644
--- a/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.h
+++ b/third_party/blink/renderer/platform/graphics/filters/fe_box_reflect.h
@@ -15,14 +15,13 @@
 class PLATFORM_EXPORT FEBoxReflect final : public FilterEffect {
  public:
   FEBoxReflect(Filter*, const BoxReflection&);
+  ~FEBoxReflect() final;
 
   // FilterEffect implementation
   WTF::TextStream& ExternalRepresentation(WTF::TextStream&,
                                           int indentation) const final;
 
  private:
-  ~FEBoxReflect() final;
-
   FloatRect MapEffect(const FloatRect&) const final;
 
   sk_sp<PaintFilter> CreateImageFilter() final;
diff --git a/third_party/blink/renderer/platform/heap/impl/member.h b/third_party/blink/renderer/platform/heap/impl/member.h
index 0ffb6f7e..7ab36a8 100644
--- a/third_party/blink/renderer/platform/heap/impl/member.h
+++ b/third_party/blink/renderer/platform/heap/impl/member.h
@@ -484,6 +484,11 @@
 template <typename T>
 struct TraceTrait<WeakMember<T>> : public MemberTraceTraits<WeakMember<T>> {};
 
+template <typename T>
+inline bool IsHashTableDeletedValue(const Member<T>& m) {
+  return m.IsHashTableDeletedValue();
+}
+
 }  // namespace blink
 
 namespace WTF {
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/member.h b/third_party/blink/renderer/platform/heap/v8_wrapper/member.h
index 78f61452..8ff02bd 100644
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/member.h
+++ b/third_party/blink/renderer/platform/heap/v8_wrapper/member.h
@@ -18,6 +18,11 @@
 template <typename T>
 using UntracedMember = cppgc::UntracedMember<T>;
 
+template <typename T>
+inline bool IsHashTableDeletedValue(const Member<T>& m) {
+  return m == cppgc::kSentinelPointer;
+}
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_MEMBER_H_
diff --git a/third_party/blink/renderer/platform/wtf/type_traits.h b/third_party/blink/renderer/platform/wtf/type_traits.h
index 4cfd33a..562628a92 100644
--- a/third_party/blink/renderer/platform/wtf/type_traits.h
+++ b/third_party/blink/renderer/platform/wtf/type_traits.h
@@ -123,7 +123,8 @@
 struct IsTraceable : cppgc::internal::IsTraceable<T> {};
 
 template <typename T>
-struct IsGarbageCollectedType : cppgc::internal::IsGarbageCollectedType<T> {};
+struct IsGarbageCollectedType
+    : cppgc::internal::IsGarbageCollectedOrMixinType<T> {};
 
 template <typename T>
 struct IsWeak : cppgc::internal::IsWeak<T> {};
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index ec35830..bb23e4ac 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1114,9 +1114,6 @@
 # TODO fails because cell size with only input element is 18px, not 15. line-height: 0px fixes it.
 crbug.com/1171616 external/wpt/css/css-tables/height-distribution/percentage-sizing-of-table-cell-children.html [ Failure ]
 
-# TODO in NG, if section has only empty rows, it is empty. Not so in Legacy. Needs C++ fix.
-crbug.com/958381 accessibility/table-with-hidden-head-section.html [ Failure ]
-
 # Composited background painting leaves gaps.
 crbug.com/958381 fast/table/border-collapsing/composited-cell-collapsed-border.html [ Failure ]
 crbug.com/958381 fast/table/border-collapsing/composited-row-collapsed-border.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-inside-display-none.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-inside-display-none.tentative.html
new file mode 100644
index 0000000..824e7005
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-inside-display-none.tentative.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel=author title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
+<link rel=match href="popup-hidden-display-ref.tentative.html">
+
+No popup should be displayed here.<p>
+
+<div style="display:none">
+    <popup>This content should be hidden</popup>
+</div>
+
+<script>
+  const popup = document.querySelector('popup');
+  popup.show();
+  if (!popup.open)
+    document.body.appendChild(document.createTextNode('FAIL'));
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-shadow-dom.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-shadow-dom.tentative.html
new file mode 100644
index 0000000..f17bdaba
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-shadow-dom.tentative.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+  function ensureShadowDom(host) {
+    host.querySelectorAll('my-element').forEach(host => {
+      if (host.shadowRoot)
+        return; // Declarative Shadow DOM is enabled
+      const template = host.firstElementChild;
+      assert_true(template instanceof HTMLTemplateElement);
+      const shadow = host.attachShadow({mode: 'open'});
+      shadow.appendChild(template.content);
+      template.remove();
+    })
+  }
+  function findPopups(root) {
+    let popups = [];
+    if (!root)
+      return popups;
+    if (root instanceof HTMLPopupElement)
+      popups.push(root);
+    popups.push(...findPopups(root.shadowRoot));
+    root.childNodes.forEach(child => {
+      popups.push(...findPopups(child));
+    })
+    return popups;
+  }
+  function getPopupReferences(testId) {
+    const testRoot = document.querySelector(`#${testId}`);
+    assert_true(!!testRoot);
+    ensureShadowDom(testRoot);
+    return findPopups(testRoot);
+  }
+  function popupVisible(popup) {
+    const style = getComputedStyle(popup);
+    return !!(style.display !== "none" && style.visibility !== "hidden" &&
+      (popup.offsetWidth || popup.offsetHeight || popup.getClientRects().length));
+  }
+  function showPopup(testId,popupNum) {
+    getPopupReferences(testId)[popupNum].show();
+  }
+</script>
+
+<div id=test1>
+  <button onclick='showPopup("test1",0)'>Test1 Popup</button>
+  <my-element>
+    <template shadowroot=open>
+      <popup>
+        <p>This should show, even though it is inside shadow DOM.</p>
+      </popup>
+    </template>
+  </my-element>
+</div>
+
+<script>
+  test(function() {
+    const popup = getPopupReferences('test1')[0];
+    popup.show();
+    assert_true(popup.open);
+    assert_true(popupVisible(popup));
+  }, "Popups located inside shadow DOM can still be shown");
+</script>
+
+
+<div id=test2>
+  <button id=t2b1 onclick='showPopup("test2",0)'>Test 2 Popup 1</button>
+  <popup anchor=t2b1 style="top: 400px;">
+    <p>Popup 1</p>
+    <button id=t2b2 onclick='showPopup("test2",1)'>Test 2 Popup 2</button>
+    <my-element>
+      <template shadowroot=open>
+        <popup anchor=t2b2 style="top: 400px;">
+          <p>This popup can never be visible:</p>
+          <p>Hiding this popup will hide *all* open popups,</p>
+          <p>because t2b2 doesn't exist in this context.</p>
+          <p>And since popup 1 is not shown, it is display:none,</p>
+          <p>which means no child content is shown at all.</p>
+        </popup>
+      </template>
+    </my-element>
+  </popup>
+</div>
+
+<script>
+  test(function() {
+    const [popup1,popup2] = getPopupReferences('test2');
+    popup1.show();
+    assert_true(popup1.open);
+    assert_true(popupVisible(popup1));
+    popup2.show();
+    assert_false(popup1.open); // P1 was closed by P2
+    assert_true(popup2.open); // P2 thinks it is open
+    assert_false(popupVisible(popup1)); // But neither is visible
+    assert_false(popupVisible(popup2));
+  }, "anchor references do not cross shadow boundaries");
+</script>
+
+
+<div id=test3>
+  <my-element>
+    <template shadowroot=open>
+      <button id=t3b1 onclick='showPopup("test3",0)'>Test 3 Popup 1</button>
+      <popup anchor=t3b1>
+        <p>This popup will be hidden when popup2 shows.</p>
+        <slot></slot>
+      </popup>
+    </template>
+    <button id=t3b2 onclick='showPopup("test3",1)'>Test 3 Popup 2</button>
+  </my-element>
+  <popup anchor=t3b2>Popup 2</popup>
+</div>
+
+<script>
+  test(function() {
+    const [popup1,popup2] = getPopupReferences('test3');
+    popup1.show();
+    assert_true(popup1.open);
+    assert_true(popupVisible(popup1));
+    // Showing popup2 will close popup1, since it is not a DOM
+    // tree ancestor of popup2's anchor button.
+    popup2.show();
+    assert_true(popup2.open);
+    assert_true(popupVisible(popup2));
+    assert_false(popup1.open);
+    assert_false(popupVisible(popup1));
+    popup2.hide();
+  }, "anchor references use the DOM tree not the flat tree");
+</script>
+
+
+<div id=test4>
+  <button id=t4b1 onclick='showPopup("test4",0)'>Test 4 Popup 1</button>
+  <popup anchor=t4b1>
+    <p>This should not get hidden when popup2 opens.</p>
+    <my-element>
+      <template shadowroot=open>
+        <button id=t4b2 onclick='showPopup("test4",1)'>Test 4 Popup 2</button>
+        <popup anchor=t4b2>
+          <p>This should not hide popup1.</p>
+        </popup>
+      </template>
+    </my-element>
+  </popup>
+</div>
+
+<script>
+  test(function() {
+    const [popup1,popup2] = getPopupReferences('test4');
+    popup1.show();
+    popup2.show();
+    // Both 1 and 2 should be open at this point.
+    assert_true(popup1.open);
+    assert_true(popupVisible(popup1));
+    assert_true(popup2.open);
+    assert_true(popupVisible(popup2));
+    // This should hide both of them.
+    popup1.hide();
+    assert_false(popup2.open);
+    assert_false(popupVisible(popup2));
+  }, "The popup stack is preserved across shadow-inclusive ancestors");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/native-file-system/showPicker-errors.https.window.js b/third_party/blink/web_tests/external/wpt/native-file-system/showPicker-errors.https.window.js
index ecc64dfe..2310c323 100644
--- a/third_party/blink/web_tests/external/wpt/native-file-system/showPicker-errors.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/native-file-system/showPicker-errors.https.window.js
@@ -84,7 +84,7 @@
     await promise_rejects_js(t, TypeError, self[showPickerMethod]({
       startIn: 'secrets',
     }));
-  }, showPickerMethod + ': unknown common starting directory.');
+  }, showPickerMethod + ': unknown well-known starting directory.');
 
   const invalid_extensions = {
     '.extensiontoolong': 'extension length more than 16.',
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override-expected.txt
index 1faf202..63aec60 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override-expected.txt
@@ -15,7 +15,13 @@
 navigator.userAgent == Ferrum Typewriter
 brands == [{"brand":"Ferrum","version":"42.0"},{"brand":"Iron","version":"3"}]
 is mobile?true
-{"architecture":"Electromechanical","model":"QWERTY","platform":"Typewriter","platformVersion":"1950","uaFullVersion":"42.0.3.14159"}
+{
+    architecture : Electromechanical
+    model : QWERTY
+    platform : Typewriter
+    platformVersion : 1950
+    uaFullVersion : 42.0.3.14159
+}
 sec-ch-ua: "Ferrum";v="42.0", "Iron";v="3"
 sec-ch-ua-full-version: "42.0.3.14159"
 sec-ch-ua-arch: "Electromechanical"
@@ -33,3 +39,22 @@
 sec-ch-ua-mobile: ?1
 sec-ch-ua-model: "QWERTY"
 
+Testing defaulting of brand and fullVersion
+navigator.userAgent == Electric Typewriter
+brands == [{"brand":"content_shell","version":"999"}]
+is mobile?true
+{
+    architecture : Electronic
+    model : With erase tape
+    platform : Electric Typewriter
+    platformVersion : 1970
+    uaFullVersion : 999.77.34.5
+}
+sec-ch-ua: "content_shell";v="999"
+sec-ch-ua-full-version: "999.77.34.5"
+sec-ch-ua-arch: "Electronic"
+sec-ch-ua-platform: "Electric Typewriter"
+sec-ch-ua-platform-version: "1970"
+sec-ch-ua-mobile: ?1
+sec-ch-ua-model: "With erase tape"
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override.js b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override.js
index 4828c95..a293ed5 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/emulation/emulation-user-agent-metadata-override.js
@@ -41,9 +41,9 @@
   testRunner.log('navigator.userAgent == ' + await session.evaluate('navigator.userAgent'));
   testRunner.log('brands == ' + await session.evaluate('JSON.stringify(navigator.userAgentData.brands)'));
   testRunner.log('is mobile?' + await session.evaluate('navigator.userAgentData.mobile'));
-  testRunner.log(JSON.stringify(await session.evaluateAsync(
+  testRunner.log(await session.evaluateAsync(
       'navigator.userAgentData.getHighEntropyValues(' +
-          '["platform", "platformVersion", "architecture", "model", "uaFullVersion"])')));
+          '["platform", "platformVersion", "architecture", "model", "uaFullVersion"])'));
   await printHeader('sec-ch-ua');
   await printHeader('sec-ch-ua-full-version');
   await printHeader('sec-ch-ua-arch');
@@ -65,6 +65,34 @@
   printHeaderFromList('sec-ch-ua-mobile', navHeaders);
   printHeaderFromList('sec-ch-ua-model', navHeaders);
 
+  // Tests to make sure that not passing in brand and fullVersion uses defaults
+  testRunner.log('');
+  testRunner.log('Testing defaulting of brand and fullVersion');
+
+  await dp.Emulation.setUserAgentOverride({
+    userAgent: 'Electric Typewriter',
+    userAgentMetadata: {
+      platform: 'Electric Typewriter',
+      platformVersion: '1970',
+      architecture: 'Electronic',
+      model: 'With erase tape',
+      mobile: true
+    }
+  });
+  testRunner.log('navigator.userAgent == ' + await session.evaluate('navigator.userAgent'));
+  testRunner.log('brands == ' + await session.evaluate('JSON.stringify(navigator.userAgentData.brands)'));
+  testRunner.log('is mobile?' + await session.evaluate('navigator.userAgentData.mobile'));
+  testRunner.log(await session.evaluateAsync(
+      'navigator.userAgentData.getHighEntropyValues(' +
+          '["platform", "platformVersion", "architecture", "model", "uaFullVersion"])'));
+  await printHeader('sec-ch-ua');
+  await printHeader('sec-ch-ua-full-version');
+  await printHeader('sec-ch-ua-arch');
+  await printHeader('sec-ch-ua-platform');
+  await printHeader('sec-ch-ua-platform-version');
+  await printHeader('sec-ch-ua-mobile');
+  await printHeader('sec-ch-ua-model');
+
   function printHeaderFromList(name, headers) {
     let logged = false;
     for (const header of headers.split('\n')) {
diff --git a/third_party/blink/web_tests/http/tests/webcodecs/basic_image_decoding.html b/third_party/blink/web_tests/http/tests/webcodecs/basic_image_decoding.html
index 556236e2..6fb5f01 100644
--- a/third_party/blink/web_tests/http/tests/webcodecs/basic_image_decoding.html
+++ b/third_party/blink/web_tests/http/tests/webcodecs/basic_image_decoding.html
@@ -8,7 +8,7 @@
   fetch("/resources/square.png").then(t.step_func(response => {
     response.arrayBuffer().then(t.step_func(buffer => {
       let decoder = new ImageDecoder({data: buffer, type: "image/png"})
-      decoder.decode(0).then(t.step_func(frame => {
+      decoder.decode({frameIndex: 0}).then(t.step_func(frame => {
         let ctx = document.querySelector('canvas').getContext('2d');
         ctx.drawImage(frame.image, 0, 0);
         t.done();
diff --git a/third_party/blink/web_tests/http/tests/webcodecs/image_decoder_reentrant_decode.html b/third_party/blink/web_tests/http/tests/webcodecs/image_decoder_reentrant_decode.html
index 313741df..9e794a0 100644
--- a/third_party/blink/web_tests/http/tests/webcodecs/image_decoder_reentrant_decode.html
+++ b/third_party/blink/web_tests/http/tests/webcodecs/image_decoder_reentrant_decode.html
@@ -13,13 +13,13 @@
     Object.defineProperty(Object.prototype, "then", { get() {
       if (count++ == 0) {
         for (var i = 0; i < 4; i++)
-          decoder.decode(2).then(() => {});
-        decoder.decode(3).then(t.step_func_done());
+          decoder.decode({frameIndex: 2}).then(() => {});
+        decoder.decode({frameIndex: 3}).then(t.step_func_done());
       }
     }});
 
-    decoder.decode(0).then(() => {});
-    decoder.decode(1).then(() => {});
+    decoder.decode({frameIndex: 0}).then(() => {});
+    decoder.decode({frameIndex: 1}).then(() => {});
   })).catch(t.unreached_func());
 });
 </script>
diff --git a/third_party/blink/web_tests/platform/mac/editing/pasteboard/drag-selected-image-to-contenteditable-expected.txt b/third_party/blink/web_tests/platform/mac/editing/pasteboard/drag-selected-image-to-contenteditable-expected.txt
index 7703852..1d3111c1 100644
--- a/third_party/blink/web_tests/platform/mac/editing/pasteboard/drag-selected-image-to-contenteditable-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/editing/pasteboard/drag-selected-image-to-contenteditable-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL assertSelection(inputText, tester, expectedText, options) resources/testharness.js:2044:25)
+FAIL assertSelection(inputText, tester, expectedText, options) resources/testharness.js:2092:25)
 	 expected <style>div, img { width: 200px; height: 100px; }</style><div contenteditable id="target">^<img id="image">|</div><img id="image">,
 	 but got  <style>div, img { width: 200px; height: 100px; }</style><div contenteditable id="target"></div>^<img id="image">|,
 	 sameupto <style>div, img { width: 200px; height: 100px; }</style><div contenteditable id="target">
diff --git a/third_party/blink/web_tests/resources/testharness.js b/third_party/blink/web_tests/resources/testharness.js
index f7fe7531..3623aa4 100644
--- a/third_party/blink/web_tests/resources/testharness.js
+++ b/third_party/blink/web_tests/resources/testharness.js
@@ -15,7 +15,6 @@
 
 (function (global_scope)
 {
-    var debug = false;
     // default timeout is 10 seconds, test can override if needed
     var settings = {
         output:true,
@@ -24,7 +23,8 @@
             "long":60000
         },
         test_timeout:null,
-        message_events: ["start", "test_state", "result", "completion"]
+        message_events: ["start", "test_state", "result", "completion"],
+        debug: false,
     };
 
     var xhtml_ns = "http://www.w3.org/1999/xhtml";
@@ -87,14 +87,15 @@
                                              test: test.structured_clone()});
                      }],
             completion: [add_completion_callback, remove_completion_callback,
-                         function (tests, harness_status) {
+                         function (tests, harness_status, asserts) {
                              var cloned_tests = map(tests, function(test) {
                                  return test.structured_clone();
                              });
                              this_obj._dispatch("completion_callback", [tests, harness_status],
                                                 {type: "complete",
                                                  tests: cloned_tests,
-                                                 status: harness_status.structured_clone()});
+                                                 status: harness_status.structured_clone(),
+                                                 asserts: asserts.map(assert => assert.structured_clone())});
                          }]
         }
 
@@ -131,11 +132,7 @@
                         if (has_selector) {
                             try {
                                 w[selector].apply(undefined, callback_args);
-                            } catch (e) {
-                                if (debug) {
-                                    throw e;
-                                }
-                            }
+                            } catch (e) {}
                         }
                     }
                     if (supports_post_message(w) && w !== self) {
@@ -192,8 +189,8 @@
             this_obj.output_handler.show_status();
         });
 
-        add_completion_callback(function (tests, harness_status) {
-            this_obj.output_handler.show_results(tests, harness_status);
+        add_completion_callback(function (tests, harness_status, asserts_run) {
+            this_obj.output_handler.show_results(tests, harness_status, asserts_run);
         });
         this.setup_messages(settings.message_events);
     };
@@ -314,14 +311,15 @@
                     });
                 });
         add_completion_callback(
-                function(tests, harness_status) {
+                function(tests, harness_status, asserts) {
                     this_obj._dispatch({
                         type: "complete",
                         tests: map(tests,
                             function(test) {
                                 return test.structured_clone();
                             }),
-                        status: harness_status.structured_clone()
+                        status: harness_status.structured_clone(),
+                        asserts: asserts.map(assert => assert.structured_clone()),
                     });
                 });
     };
@@ -500,11 +498,7 @@
             return new DedicatedWorkerTestEnvironment();
         }
 
-        if (!('location' in global_scope)) {
-            return new ShellTestEnvironment();
-        }
-
-        throw new Error("Unsupported test environment");
+        return new ShellTestEnvironment();
     }
 
     var test_environment = create_test_environment();
@@ -1187,19 +1181,53 @@
      * Assertions
      */
 
+    function expose_assert(f, name) {
+        function assert_wrapper(...args) {
+            let status = Test.statuses.TIMEOUT;
+            let stack = null;
+            try {
+                if (settings.debug) {
+                    console.debug("ASSERT", name, tests.current_test.name, args);
+                }
+                if (tests.output) {
+                    tests.set_assert(name, ...args);
+                }
+                const rv = f(...args);
+                status = Test.statuses.PASS;
+                return rv;
+            } catch(e) {
+                if (e instanceof AssertionError) {
+                    status = Test.statuses.FAIL;
+                    stack = e.stack;
+                 } else {
+                    status = Test.statuses.ERROR;
+                 }
+                throw e;
+            } finally {
+                if (tests.output && !stack) {
+                    stack = get_stack();
+                }
+                if (tests.output) {
+                    tests.set_assert_status(status, stack);
+                }
+            }
+        }
+        expose(assert_wrapper, name);
+    }
+
     function assert_true(actual, description)
     {
         assert(actual === true, "assert_true", description,
                                 "expected true got ${actual}", {actual:actual});
     }
-    expose(assert_true, "assert_true");
+    expose_assert(assert_true, "assert_true");
 
     function assert_false(actual, description)
     {
         assert(actual === false, "assert_false", description,
                                  "expected false got ${actual}", {actual:actual});
     }
-    expose(assert_false, "assert_false");
+    expose_assert(assert_false, "assert_false");
 
     function same_value(x, y) {
         if (y !== y) {
@@ -1229,7 +1257,7 @@
                                              "expected ${expected} but got ${actual}",
                                              {expected:expected, actual:actual});
     }
-    expose(assert_equals, "assert_equals");
+    expose_assert(assert_equals, "assert_equals");
 
     function assert_not_equals(actual, expected, description)
     {
@@ -1241,7 +1269,7 @@
                                               "got disallowed value ${actual}",
                                               {actual:actual});
     }
-    expose(assert_not_equals, "assert_not_equals");
+    expose_assert(assert_not_equals, "assert_not_equals");
 
     function assert_in_array(actual, expected, description)
     {
@@ -1249,7 +1277,7 @@
                                                "value ${actual} not in array ${expected}",
                                                {actual:actual, expected:expected});
     }
-    expose(assert_in_array, "assert_in_array");
+    expose_assert(assert_in_array, "assert_in_array");
 
     // This function was deprecated in July of 2015.
     // See https://github.com/web-platform-tests/wpt/issues/2033
@@ -1287,7 +1315,7 @@
          }
          check_equal(actual, expected, []);
     }
-    expose(assert_object_equals, "assert_object_equals");
+    expose_assert(assert_object_equals, "assert_object_equals");
 
     function assert_array_equals(actual, expected, description)
     {
@@ -1344,7 +1372,7 @@
                     arrayExpected:shorten_array(expected, i), arrayActual:shorten_array(actual, i)});
         }
     }
-    expose(assert_array_equals, "assert_array_equals");
+    expose_assert(assert_array_equals, "assert_array_equals");
 
     function assert_array_approx_equals(actual, expected, epsilon, description)
     {
@@ -1372,7 +1400,7 @@
                    {i:i, expected:expected[i], actual:actual[i], epsilon:epsilon});
         }
     }
-    expose(assert_array_approx_equals, "assert_array_approx_equals");
+    expose_assert(assert_array_approx_equals, "assert_array_approx_equals");
 
     function assert_approx_equals(actual, expected, epsilon, description)
     {
@@ -1395,7 +1423,7 @@
             assert_equals(actual, expected);
         }
     }
-    expose(assert_approx_equals, "assert_approx_equals");
+    expose_assert(assert_approx_equals, "assert_approx_equals");
 
     function assert_less_than(actual, expected, description)
     {
@@ -1412,7 +1440,7 @@
                "expected a number less than ${expected} but got ${actual}",
                {expected:expected, actual:actual});
     }
-    expose(assert_less_than, "assert_less_than");
+    expose_assert(assert_less_than, "assert_less_than");
 
     function assert_greater_than(actual, expected, description)
     {
@@ -1429,7 +1457,7 @@
                "expected a number greater than ${expected} but got ${actual}",
                {expected:expected, actual:actual});
     }
-    expose(assert_greater_than, "assert_greater_than");
+    expose_assert(assert_greater_than, "assert_greater_than");
 
     function assert_between_exclusive(actual, lower, upper, description)
     {
@@ -1447,7 +1475,7 @@
                "and less than ${upper} but got ${actual}",
                {lower:lower, upper:upper, actual:actual});
     }
-    expose(assert_between_exclusive, "assert_between_exclusive");
+    expose_assert(assert_between_exclusive, "assert_between_exclusive");
 
     function assert_less_than_equal(actual, expected, description)
     {
@@ -1464,7 +1492,7 @@
                "expected a number less than or equal to ${expected} but got ${actual}",
                {expected:expected, actual:actual});
     }
-    expose(assert_less_than_equal, "assert_less_than_equal");
+    expose_assert(assert_less_than_equal, "assert_less_than_equal");
 
     function assert_greater_than_equal(actual, expected, description)
     {
@@ -1481,7 +1509,7 @@
                "expected a number greater than or equal to ${expected} but got ${actual}",
                {expected:expected, actual:actual});
     }
-    expose(assert_greater_than_equal, "assert_greater_than_equal");
+    expose_assert(assert_greater_than_equal, "assert_greater_than_equal");
 
     function assert_between_inclusive(actual, lower, upper, description)
     {
@@ -1499,7 +1527,7 @@
                "and less than or equal to ${upper} but got ${actual}",
                {lower:lower, upper:upper, actual:actual});
     }
-    expose(assert_between_inclusive, "assert_between_inclusive");
+    expose_assert(assert_between_inclusive, "assert_between_inclusive");
 
     function assert_regexp_match(actual, expected, description) {
         /*
@@ -1510,7 +1538,7 @@
                "expected ${expected} but got ${actual}",
                {expected:expected, actual:actual});
     }
-    expose(assert_regexp_match, "assert_regexp_match");
+    expose_assert(assert_regexp_match, "assert_regexp_match");
 
     function assert_class_string(object, class_string, description) {
         var actual = {}.toString.call(object);
@@ -1519,22 +1547,21 @@
                                              "expected ${expected} but got ${actual}",
                                              {expected:expected, actual:actual});
     }
-    expose(assert_class_string, "assert_class_string");
-
+    expose_assert(assert_class_string, "assert_class_string");
 
     function assert_own_property(object, property_name, description) {
         assert(object.hasOwnProperty(property_name),
                "assert_own_property", description,
                "expected property ${p} missing", {p:property_name});
     }
-    expose(assert_own_property, "assert_own_property");
+    expose_assert(assert_own_property, "assert_own_property");
 
     function assert_not_own_property(object, property_name, description) {
         assert(!object.hasOwnProperty(property_name),
                "assert_not_own_property", description,
                "unexpected property ${p} is found on object", {p:property_name});
     }
-    expose(assert_not_own_property, "assert_not_own_property");
+    expose_assert(assert_not_own_property, "assert_not_own_property");
 
     function _assert_inherits(name) {
         return function (object, property_name, description)
@@ -1560,8 +1587,8 @@
                    {p:property_name});
         };
     }
-    expose(_assert_inherits("assert_inherits"), "assert_inherits");
-    expose(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute");
+    expose_assert(_assert_inherits("assert_inherits"), "assert_inherits");
+    expose_assert(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute");
 
     function assert_readonly(object, property_name, description)
     {
@@ -1578,7 +1605,7 @@
              object[property_name] = initial_value;
          }
     }
-    expose(assert_readonly, "assert_readonly");
+    expose_assert(assert_readonly, "assert_readonly");
 
     /**
      * Assert a JS Error with the expected constructor is thrown.
@@ -1592,7 +1619,7 @@
         assert_throws_js_impl(constructor, func, description,
                               "assert_throws_js");
     }
-    expose(assert_throws_js, "assert_throws_js");
+    expose_assert(assert_throws_js, "assert_throws_js");
 
     /**
      * Like assert_throws_js but allows specifying the assertion type
@@ -1690,7 +1717,7 @@
         }
         assert_throws_dom_impl(type, func, description, "assert_throws_dom", constructor)
     }
-    expose(assert_throws_dom, "assert_throws_dom");
+    expose_assert(assert_throws_dom, "assert_throws_dom");
 
     /**
      * Similar to assert_throws_dom but allows specifying the assertion type
@@ -1853,7 +1880,7 @@
         assert_throws_exactly_impl(exception, func, description,
                                    "assert_throws_exactly");
     }
-    expose(assert_throws_exactly, "assert_throws_exactly");
+    expose_assert(assert_throws_exactly, "assert_throws_exactly");
 
     /**
      * Like assert_throws_exactly but allows specifying the assertion type
@@ -1881,7 +1908,7 @@
          assert(false, "assert_unreached", description,
                 "Reached unreachable code");
     }
-    expose(assert_unreached, "assert_unreached");
+    expose_assert(assert_unreached, "assert_unreached");
 
     function assert_any(assert_func, actual, expected_array)
     {
@@ -1902,7 +1929,7 @@
             throw new AssertionError(errors.join("\n\n"));
         }
     }
-    expose(assert_any, "assert_any");
+    expose_assert(assert_any, "assert_any");
 
     /**
      * Assert that a feature is implemented, based on a 'truthy' condition.
@@ -1919,7 +1946,7 @@
     function assert_implements(condition, description) {
         assert(!!condition, "assert_implements", description);
     }
-    expose(assert_implements, "assert_implements")
+    expose_assert(assert_implements, "assert_implements")
 
     /**
      * Assert that an optional feature is implemented, based on a 'truthy' condition.
@@ -1939,7 +1966,7 @@
             throw new OptionalFeatureUnsupportedError(description);
         }
     }
-    expose(assert_implements_optional, "assert_implements_optional")
+    expose_assert(assert_implements_optional, "assert_implements_optional")
 
     function Test(name, properties)
     {
@@ -1999,6 +2026,18 @@
         COMPLETE:4
     };
 
+    Test.prototype.status_formats = {
+        0: "Pass",
+        1: "Fail",
+        2: "Timeout",
+        3: "Not Run",
+        4: "Optional Feature Unsupported",
+    }
+
+    Test.prototype.format_status = function() {
+        return this.status_formats[this.status];
+    }
+
     Test.prototype.structured_clone = function()
     {
         if (!this._structured_clone) {
@@ -2023,11 +2062,16 @@
         if (this.phase > this.phases.STARTED) {
             return;
         }
+
+        if (settings.debug && this.phase !== this.phases.STARTED) {
+            console.log("TEST START", this.name);
+        }
         this.phase = this.phases.STARTED;
         //If we don't get a result before the harness times out that will be a test timeout
         this.set_status(this.TIMEOUT, "Test timed out");
 
         tests.started = true;
+        tests.current_test = this;
         tests.notify_test_state(this);
 
         if (this.timeout_id === null) {
@@ -2040,6 +2084,10 @@
             this_obj = this;
         }
 
+        if (settings.debug) {
+            console.debug("TEST STEP", this.name);
+        }
+
         try {
             return func.apply(this_obj, Array.prototype.slice.call(arguments, 2));
         } catch (e) {
@@ -2053,6 +2101,8 @@
             this.set_status(status, message, stack);
             this.phase = this.phases.HAS_RESULT;
             this.done();
+        } finally {
+            this.current_test = null;
         }
     };
 
@@ -2270,6 +2320,12 @@
             clearTimeout(this.timeout_id);
         }
 
+        if (settings.debug) {
+            console.log("TEST DONE",
+                        this.status,
+                        this.name,)
+        }
+
         this.cleanup();
     };
 
@@ -2469,6 +2525,10 @@
                 });
     }
 
+    RemoteTest.prototype.format_status = function() {
+        return Test.prototype.status_formats[this.status];
+    }
+
     /*
      * A RemoteContext listens for test events from a remote test context, such
      * as another window or a worker. These events are then used to construct
@@ -2577,6 +2637,16 @@
             tests.set_status(data.status.status, data.status.message, data.status.sack);
         }
 
+        for (let assert of data.asserts) {
+            var record = new AssertRecord();
+            record.assert_name = assert.assert_name;
+            record.args = assert.args;
+            record.test = assert.test != null ? this.tests[assert.test.index] : null;
+            record.status = assert.status;
+            record.stack = assert.stack;
+            tests.asserts_run.push(record);
+        }
+
         this.message_target.removeEventListener("message", this.message_handler);
         this.running = false;
 
@@ -2626,6 +2696,14 @@
 
     TestsStatus.prototype = merge({}, TestsStatus.statuses);
 
+    TestsStatus.prototype.formats = {
+        0: "OK",
+        1: "Error",
+        2: "Timeout",
+        3: "Optional Feature Unsupported"
+    }
+
+
     TestsStatus.prototype.structured_clone = function()
     {
         if (!this._structured_clone) {
@@ -2640,6 +2718,27 @@
         return this._structured_clone;
     };
 
+    TestsStatus.prototype.format_status = function() {
+        return this.formats[this.status];
+    }
+
+    function AssertRecord(test, assert_name, ...args) {
+        this.assert_name = assert_name;
+        this.test = test;
+        // Avoid keeping complex objects alive
+        this.args = args.map(x => format_value(x).replace(/\n/g, " "));
+        this.status = null;
+    }
+
+    AssertRecord.prototype.structured_clone = function() {
+        return {
+            assert_name: this.assert_name,
+            test: this.test ? this.test.structured_clone() : null,
+            args: this.args,
+            status: this.status,
+        }
+    }
+
     function Tests()
     {
         this.tests = [];
@@ -2679,6 +2778,18 @@
         this.hide_test_state = false;
         this.pending_remotes = [];
 
+        this.current_test = null;
+        this.asserts_run = [];
+
+        // Track whether output is enabled, and thus whether or not we should
+        // track asserts.
+        //
+        // On workers we don't get properties set from testharnessreport.js, so
+        // we don't know whether or not to track asserts. To avoid the
+        // resulting performance hit, we assume we are not meant to. This means
+        // that assert tracking does not function on workers.
+        this.output = settings.output && 'document' in global_scope;
+
         this.status = new TestsStatus();
 
         var this_obj = this;
@@ -2726,6 +2837,8 @@
                     }
                 } else if (p == "hide_test_state") {
                     this.hide_test_state = value;
+                } else if (p == "output") {
+                    this.output = value;
                 }
             }
         }
@@ -2914,6 +3027,16 @@
                   all_complete);
     };
 
+    Tests.prototype.set_assert = function(assert_name, ...args) {
+        this.asserts_run.push(new AssertRecord(this.current_test, assert_name, ...args))
+    }
+
+    Tests.prototype.set_assert_status = function(status, stack) {
+        let assert_record = this.asserts_run[this.asserts_run.length - 1];
+        assert_record.status = status;
+        assert_record.stack = stack;
+    }
+
     /**
      * Update the harness status to reflect an unrecoverable harness error that
      * should cancel all further testing. Update all previously-defined tests
@@ -3011,7 +3134,7 @@
         forEach (this.all_done_callbacks,
                  function(callback)
                  {
-                     callback(this_obj.tests, this_obj.status);
+                     callback(this_obj.tests, this_obj.status, this_obj.asserts_run);
                  });
     };
 
@@ -3237,7 +3360,7 @@
         }
     };
 
-    Output.prototype.show_results = function (tests, harness_status) {
+    Output.prototype.show_results = function (tests, harness_status, asserts_run) {
         if (this.phase >= this.COMPLETE) {
             return;
         }
@@ -3266,23 +3389,10 @@
             heads[0].appendChild(stylesheet);
         }
 
-        var status_text_harness = {};
-        status_text_harness[harness_status.OK] = "OK";
-        status_text_harness[harness_status.ERROR] = "Error";
-        status_text_harness[harness_status.TIMEOUT] = "Timeout";
-        status_text_harness[harness_status.PRECONDITION_FAILED] = "Optional Feature Unsupported";
-
-        var status_text = {};
-        status_text[Test.prototype.PASS] = "Pass";
-        status_text[Test.prototype.FAIL] = "Fail";
-        status_text[Test.prototype.TIMEOUT] = "Timeout";
-        status_text[Test.prototype.NOTRUN] = "Not Run";
-        status_text[Test.prototype.PRECONDITION_FAILED] = "Optional Feature Unsupported";
-
         var status_number = {};
         forEach(tests,
                 function(test) {
-                    var status = status_text[test.status];
+                    var status = test.format_status();
                     if (status_number.hasOwnProperty(status)) {
                         status_number[status] += 1;
                     } else {
@@ -3299,8 +3409,7 @@
                                 ["h2", {}, "Summary"],
                                 function()
                                 {
-
-                                    var status = status_text_harness[harness_status.status];
+                                    var status = harness_status.format_status();
                                     var rv = [["section", {},
                                                ["p", {},
                                                 "Harness status: ",
@@ -3322,13 +3431,14 @@
                                 function() {
                                     var rv = [["div", {}]];
                                     var i = 0;
-                                    while (status_text.hasOwnProperty(i)) {
-                                        if (status_number.hasOwnProperty(status_text[i])) {
-                                            var status = status_text[i];
-                                            rv[0].push(["div", {"class":status_class(status)},
+                                    while (Test.prototype.status_formats.hasOwnProperty(i)) {
+                                        if (status_number.hasOwnProperty(Test.prototype.status_formats[i])) {
+                                            var status = Test.prototype.status_formats[i];
+                                            rv[0].push(["div", {},
                                                         ["label", {},
                                                          ["input", {type:"checkbox", checked:"checked"}],
-                                                         status_number[status] + " " + status]]);
+                                                         status_number[status] + " ",
+                                                         ["span", {"class":status_class(status)}, status]]]);
                                         }
                                         i++;
                                     }
@@ -3395,6 +3505,51 @@
             return '';
         }
 
+        var asserts_run_by_test = new Map();
+        asserts_run.forEach(assert => {
+            if (!asserts_run_by_test.has(assert.test)) {
+                asserts_run_by_test.set(assert.test, []);
+            }
+            asserts_run_by_test.get(assert.test).push(assert);
+        });
+
+        function get_asserts_output(test) {
+            var asserts = asserts_run_by_test.get(test);
+            if (!asserts) {
+                return "No asserts ran";
+            }
+            rv = "<table>";
+            rv += asserts.map(assert => {
+                var output_fn = "<strong>" + escape_html(assert.assert_name) + "</strong>(";
+                var prefix_len = output_fn.length;
+                var output_args = assert.args;
+                var output_len = output_args.reduce((prev, current) => prev+current, prefix_len);
+                if (output_len[output_len.length - 1] > 50) {
+                    output_args = output_args.map((x, i) =>
+                    (i > 0 ? "  ".repeat(prefix_len) : "" )+ x + (i < output_args.length - 1 ? ",\n" : ""));
+                } else {
+                    output_args = output_args.map((x, i) => x + (i < output_args.length - 1 ? ", " : ""));
+                }
+                output_fn += escape_html(output_args.join(""));
+                output_fn += ')';
+                var output_location;
+                if (assert.stack) {
+                    output_location = assert.stack.split("\n", 1)[0].replace(/@?\w+:\/\/[^ "\/]+(?::\d+)?/g, " ");
+                }
+                return "<tr><td class=" +
+                    status_class(Test.prototype.status_formats[assert.status]) + ">" +
+                    Test.prototype.status_formats[assert.status] + "</td>" +
+                    "</td>" +
+                    "<td><pre>" +
+                    output_fn +
+                    (output_location ? "\n" + escape_html(output_location) : "") +
+                    "</pre></td></tr>";
+            }
+            ).join("\n");
+            rv += "</table>";
+            return rv;
+        }
+
         log.appendChild(document.createElementNS(xhtml_ns, "section"));
         var assertions = has_assertions();
         var html = "<h2>Details</h2><table id='results' " + (assertions ? "class='assertions'" : "" ) + ">" +
@@ -3403,18 +3558,20 @@
             "<th>Message</th></tr></thead>" +
             "<tbody>";
         for (var i = 0; i < tests.length; i++) {
-            html += '<tr class="' +
-                escape_html(status_class(status_text[tests[i].status])) +
-                '"><td>' +
-                escape_html(status_text[tests[i].status]) +
+            var test = tests[i];
+            html += '<tr><td class="' +
+                status_class(test.format_status()) +
+                '">' +
+                test.format_status() +
                 "</td><td>" +
-                escape_html(tests[i].name) +
+                escape_html(test.name) +
                 "</td><td>" +
-                (assertions ? escape_html(get_assertion(tests[i])) + "</td><td>" : "") +
-                escape_html(tests[i].message ? tests[i].message : " ") +
+                (assertions ? escape_html(get_assertion(test)) + "</td><td>" : "") +
+                escape_html(test.message ? tests[i].message : " ") +
                 (tests[i].stack ? "<pre>" +
                  escape_html(tests[i].stack) +
                  "</pre>": "") +
+                 "<details><summary>Asserts run</summary>" + get_asserts_output(test) + "</details>"
                 "</td></tr>";
         }
         html += "</tbody></table>";
@@ -3610,13 +3767,13 @@
             message = sanitize_unpaired_surrogates(message);
         }
         this.message = message;
-        this.stack = this.get_stack();
+        this.stack = get_stack();
     }
     expose(AssertionError, "AssertionError");
 
     AssertionError.prototype = Object.create(Error.prototype);
 
-    AssertionError.prototype.get_stack = function() {
+    const get_stack = function() {
         var stack = new Error().stack;
         // IE11 does not initialize 'Error.stack' until the object is thrown.
         if (!stack) {
@@ -3980,54 +4137,62 @@
     width:100%;\
 }\
 \
-table#results th:first-child,\
-table#results td:first-child {\
+table#results > thead > tr > th:first-child,\
+table#results > tbody > tr > td:first-child {\
     width:8em;\
 }\
 \
-table#results th:last-child,\
-table#results td:last-child {\
+table#results > thead > tr > th:last-child,\
+table#results > thead > tr > td:last-child {\
     width:50%;\
 }\
 \
-table#results.assertions th:last-child,\
-table#results.assertions td:last-child {\
+table#results.assertions > thead > tr > th:last-child,\
+table#results.assertions > tbody > tr > td:last-child {\
     width:35%;\
 }\
 \
-table#results th {\
+table#results > thead > > tr > th {\
     padding:0;\
     padding-bottom:0.5em;\
     border-bottom:medium solid black;\
 }\
 \
-table#results td {\
+table#results > tbody > tr> td {\
     padding:1em;\
     padding-bottom:0.5em;\
     border-bottom:thin solid black;\
 }\
 \
-tr.pass > td:first-child {\
+.pass {\
     color:green;\
 }\
 \
-tr.fail > td:first-child {\
+.fail {\
     color:red;\
 }\
 \
-tr.timeout > td:first-child {\
+tr.timeout {\
     color:red;\
 }\
 \
-tr.notrun > td:first-child {\
+tr.notrun {\
     color:blue;\
 }\
 \
-tr.optionalunsupported > td:first-child {\
+tr.optionalunsupported {\
     color:blue;\
 }\
 \
-.pass > td:first-child, .fail > td:first-child, .timeout > td:first-child, .notrun > td:first-child, .optionalunsupported > td:first-child {\
+.ok {\
+    color:green;\
+}\
+\
+.error {\
+    color:red;\
+}\
+\
+.pass, .fail, .timeout, .notrun, .optionalunsupported .ok, .timeout, .error {\
     font-variant:small-caps;\
 }\
 \
@@ -4044,22 +4209,6 @@
     font-family:DejaVu Sans Mono, Bitstream Vera Sans Mono, Monospace;\
     white-space:pre;\
 }\
-\
-span.ok {\
-    color:green;\
-}\
-\
-tr.error {\
-    color:red;\
-}\
-\
-span.timeout {\
-    color:red;\
-}\
-\
-span.ok, span.timeout, span.error {\
-    font-variant:small-caps;\
-}\
 ";
 
 })(self);
diff --git a/third_party/blink/web_tests/virtual/force-defer-script/defer-script/async-script-expected.txt b/third_party/blink/web_tests/virtual/force-defer-script/defer-script/async-script-expected.txt
index c351351..708691c 100644
--- a/third_party/blink/web_tests/virtual/force-defer-script/defer-script/async-script-expected.txt
+++ b/third_party/blink/web_tests/virtual/force-defer-script/defer-script/async-script-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 3603: Uncaught Error: assert_equals: Normal script execution order comparison expected "Inline1;Sync1;EndOfBody;DOMContentLoaded;Async1;" but got "EndOfBody;Inline1;Sync1;DOMContentLoaded;Async1;"
+CONSOLE ERROR: line 1205: Uncaught Error: assert_equals: Normal script execution order comparison expected "Inline1;Sync1;EndOfBody;DOMContentLoaded;Async1;" but got "EndOfBody;Inline1;Sync1;DOMContentLoaded;Async1;"
 This is a testharness.js-based test.
 FAIL Async Script Execution Order (wrt possibly deferred Synchronous Script) Uncaught Error: assert_equals: Normal script execution order comparison expected "Inline1;Sync1;EndOfBody;DOMContentLoaded;Async1;" but got "EndOfBody;Inline1;Sync1;DOMContentLoaded;Async1;"
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/force-defer-script/defer-script/defer-script-expected.txt b/third_party/blink/web_tests/virtual/force-defer-script/defer-script/defer-script-expected.txt
index 2f5dce66..a69a488 100644
--- a/third_party/blink/web_tests/virtual/force-defer-script/defer-script/defer-script-expected.txt
+++ b/third_party/blink/web_tests/virtual/force-defer-script/defer-script/defer-script-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 3603: Uncaught Error: assert_equals: Normal defer script execution order comparison expected "Inline1;Sync1;Inline2;Sync2;EndOfBody;Defer1;Defer2;DOMContentLoaded;" but got "EndOfBody;Inline1;Sync1;Inline2;Sync2;Defer1;Defer2;DOMContentLoaded;"
+CONSOLE ERROR: line 1205: Uncaught Error: assert_equals: Normal defer script execution order comparison expected "Inline1;Sync1;Inline2;Sync2;EndOfBody;Defer1;Defer2;DOMContentLoaded;" but got "EndOfBody;Inline1;Sync1;Inline2;Sync2;Defer1;Defer2;DOMContentLoaded;"
 This is a testharness.js-based test.
 FAIL Defer Script Execution Order Uncaught Error: assert_equals: Normal defer script execution order comparison expected "Inline1;Sync1;Inline2;Sync2;EndOfBody;Defer1;Defer2;DOMContentLoaded;" but got "EndOfBody;Inline1;Sync1;Inline2;Sync2;Defer1;Defer2;DOMContentLoaded;"
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-content-visibility/accessibility/content-visibility-accessibility-009.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-content-visibility/accessibility/content-visibility-accessibility-009.html
new file mode 100644
index 0000000..cfa48ca
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-content-visibility/accessibility/content-visibility-accessibility-009.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<input id=inputid aria-labelledby="hiddenmatchable-label">
+<div style="content-visibility: hidden-matchable">
+  <span id="hiddenmatchable-label">hiddenmatchable label text</span>
+</div>
+
+<script>
+test(() => {
+  const axInput = accessibilityController.accessibleElementById('inputid');
+  assert_equals(axInput.name, 'hiddenmatchable label text');
+}, `aria labels inside content-visibility:hidden-matchable subtrees should still work.`);
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/serial/serial_getPorts-mojoServiceUnavailable.https.any.js b/third_party/blink/web_tests/wpt_internal/serial/serial_getPorts-mojoServiceUnavailable.https.any.js
index ba2c5285..0281e90 100644
--- a/third_party/blink/web_tests/wpt_internal/serial/serial_getPorts-mojoServiceUnavailable.https.any.js
+++ b/third_party/blink/web_tests/wpt_internal/serial/serial_getPorts-mojoServiceUnavailable.https.any.js
@@ -1,13 +1,11 @@
 // META: script=/resources/test-only-api.js
 
 promise_test(async () => {
-  await loadMojoResources([
-    '/gen/mojo/public/mojom/base/unguessable_token.mojom.js',
-    '/gen/third_party/blink/public/mojom/serial/serial.mojom.js',
-  ]);
+  const {SerialService} = await import(
+      '/gen/third_party/blink/public/mojom/serial/serial.mojom.m.js');
 
   let interceptor =
-      new MojoInterfaceInterceptor(blink.mojom.SerialService.name);
+      new MojoInterfaceInterceptor(SerialService.$interfaceName);
   interceptor.oninterfacerequest = e => e.handle.close();
   interceptor.start();
 
diff --git a/third_party/blink/web_tests/wpt_internal/serial/serial_requestPort-mojoServiceUnavailable.https.window.js b/third_party/blink/web_tests/wpt_internal/serial/serial_requestPort-mojoServiceUnavailable.https.window.js
index 82d29842..fb1b4431 100644
--- a/third_party/blink/web_tests/wpt_internal/serial/serial_requestPort-mojoServiceUnavailable.https.window.js
+++ b/third_party/blink/web_tests/wpt_internal/serial/serial_requestPort-mojoServiceUnavailable.https.window.js
@@ -4,13 +4,11 @@
 // META: script=/serial/resources/automation.js
 
 promise_test(async (t) => {
-  await loadMojoResources([
-    '/gen/mojo/public/mojom/base/unguessable_token.mojom.js',
-    '/gen/third_party/blink/public/mojom/serial/serial.mojom.js',
-  ]);
+  const {SerialService} = await import(
+      '/gen/third_party/blink/public/mojom/serial/serial.mojom.m.js');
 
   let interceptor =
-      new MojoInterfaceInterceptor(blink.mojom.SerialService.name);
+      new MojoInterfaceInterceptor(SerialService.$interfacName);
   interceptor.oninterfacerequest = e => e.handle.close();
   interceptor.start();
 
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index 4dab233..d29f8f1 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -42,3 +42,4 @@
  - MemoryMap.SelfLargeMapFile, SelfBasic, SelfLargeFiles are disabled when
    BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) are defined. crbug.com/1163794
    (third_party/crashpad/crashpad/util/linux/memory_map_test.cc)
+ - Exclude //build/config/mac:sdk_inputs dependency from iOS builds
\ No newline at end of file
diff --git a/third_party/crashpad/crashpad/util/BUILD.gn b/third_party/crashpad/crashpad/util/BUILD.gn
index a8daea8..831b1d3 100644
--- a/third_party/crashpad/crashpad/util/BUILD.gn
+++ b/third_party/crashpad/crashpad/util/BUILD.gn
@@ -77,7 +77,9 @@
           migcom_path,
         ]
       }
-      deps = [ "//build/config/mac:sdk_inputs" ]
+      if (crashpad_is_mac) {
+        deps = [ "//build/config/mac:sdk_inputs" ]
+      }
     }
     if (sysroot != "") {
       if (crashpad_is_in_chromium) {
diff --git a/third_party/libaom/README.chromium b/third_party/libaom/README.chromium
index ce18d14..ce21094 100644
--- a/third_party/libaom/README.chromium
+++ b/third_party/libaom/README.chromium
@@ -2,9 +2,9 @@
 Short Name: libaom
 URL: https://aomedia.googlesource.com/aom/
 Version: 0
-Date: Saturday December 05 2020
+Date: Wednesday January 27 2021
 Branch: master
-Commit: 43927e4611e7c3062a67ebaca38a625faa9a39d6
+Commit: 61c6fda0fdc927830559597bbb3410a0000bdc9c
 License: BSD
 License File: source/libaom/LICENSE
 Security Critical: yes
diff --git a/third_party/libaom/libaom_srcs.gni b/third_party/libaom/libaom_srcs.gni
index eb6fce5..99922bd 100644
--- a/third_party/libaom/libaom_srcs.gni
+++ b/third_party/libaom/libaom_srcs.gni
@@ -73,6 +73,8 @@
     [ "//third_party/libaom/source/libaom/av1/common/ppc/cfl_ppc.c" ]
 
 aom_av1_common_sources = [
+  "//third_party/libaom/source/libaom/av1/arg_defs.c",
+  "//third_party/libaom/source/libaom/av1/arg_defs.h",
   "//third_party/libaom/source/libaom/av1/av1_iface_common.h",
   "//third_party/libaom/source/libaom/av1/common/alloccommon.c",
   "//third_party/libaom/source/libaom/av1/common/alloccommon.h",
@@ -142,6 +144,8 @@
   "//third_party/libaom/source/libaom/av1/common/token_cdfs.h",
   "//third_party/libaom/source/libaom/av1/common/txb_common.c",
   "//third_party/libaom/source/libaom/av1/common/txb_common.h",
+  "//third_party/libaom/source/libaom/common/args_helper.c",
+  "//third_party/libaom/source/libaom/common/args_helper.h",
 ]
 
 aom_av1_decoder_sources = [
@@ -341,6 +345,8 @@
   "//third_party/libaom/source/libaom/av1/encoder/tokenize.h",
   "//third_party/libaom/source/libaom/av1/encoder/tx_search.c",
   "//third_party/libaom/source/libaom/av1/encoder/tx_search.h",
+  "//third_party/libaom/source/libaom/av1/encoder/txb_rdopt.c",
+  "//third_party/libaom/source/libaom/av1/encoder/txb_rdopt.h",
   "//third_party/libaom/source/libaom/av1/encoder/var_based_part.c",
   "//third_party/libaom/source/libaom/av1/encoder/var_based_part.h",
   "//third_party/libaom/source/libaom/av1/encoder/wedge_utils.c",
@@ -443,6 +449,7 @@
 
 aom_dsp_common_intrin_ssse3 = [
   "//third_party/libaom/source/libaom/aom_dsp/x86/aom_subpixel_8t_intrin_ssse3.c",
+  "//third_party/libaom/source/libaom/aom_dsp/x86/convolve_ssse3.h",
   "//third_party/libaom/source/libaom/aom_dsp/x86/intrapred_ssse3.c",
 ]
 
diff --git a/third_party/libaom/source/config/config/aom_version.h b/third_party/libaom/source/config/config/aom_version.h
index e4b865f2..db8676c 100644
--- a/third_party/libaom/source/config/config/aom_version.h
+++ b/third_party/libaom/source/config/config/aom_version.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -12,8 +12,8 @@
 #define VERSION_MAJOR 2
 #define VERSION_MINOR 0
 #define VERSION_PATCH 1
-#define VERSION_EXTRA "1090-g43927e461"
+#define VERSION_EXTRA "1184-g61c6fda0f"
 #define VERSION_PACKED \
   ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH))
-#define VERSION_STRING_NOSP "2.0.1-1090-g43927e461"
-#define VERSION_STRING " 2.0.1-1090-g43927e461"
+#define VERSION_STRING_NOSP "2.0.1-1184-g61c6fda0f"
+#define VERSION_STRING " 2.0.1-1184-g61c6fda0f"
diff --git a/third_party/libaom/source/config/ios/arm-neon/config/aom_config.asm b/third_party/libaom/source/config/ios/arm-neon/config/aom_config.asm
index 8fbd732d..a6811f5 100644
--- a/third_party/libaom/source/config/ios/arm-neon/config/aom_config.asm
+++ b/third_party/libaom/source/config/ios/arm-neon/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2020, Alliance for Open Media. All rights reserved
+; Copyright (c) 2021, Alliance for Open Media. All rights reserved
 ;
 ; This source code is subject to the terms of the BSD 2 Clause License and
 ; the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -48,6 +48,7 @@
 CONFIG_PIC equ 0
 CONFIG_RD_DEBUG equ 0
 CONFIG_REALTIME_ONLY equ 1
+CONFIG_RT_ML_PARTITIONING equ 0
 CONFIG_RUNTIME_CPU_DETECT equ 0
 CONFIG_SHARED equ 0
 CONFIG_SHARP_SETTINGS equ 0
diff --git a/third_party/libaom/source/config/ios/arm-neon/config/aom_config.h b/third_party/libaom/source/config/ios/arm-neon/config/aom_config.h
index fa9522d..e0e191d 100644
--- a/third_party/libaom/source/config/ios/arm-neon/config/aom_config.h
+++ b/third_party/libaom/source/config/ios/arm-neon/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 0
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 0
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/ios/arm-neon/config/av1_rtcd.h b/third_party/libaom/source/config/ios/arm-neon/config/av1_rtcd.h
index 3351447b..c07b2fb 100644
--- a/third_party/libaom/source/config/ios/arm-neon/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/ios/arm-neon/config/av1_rtcd.h
@@ -191,62 +191,6 @@
                              int k);
 #define av1_calc_indices_dim2 av1_calc_indices_dim2_c
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -543,18 +487,6 @@
                               int bd);
 #define av1_fwd_txfm2d_16x4 av1_fwd_txfm2d_16x4_neon
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_16x64_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-#define av1_fwd_txfm2d_16x64 av1_fwd_txfm2d_16x64_neon
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -603,30 +535,6 @@
                                int bd);
 #define av1_fwd_txfm2d_32x64 av1_fwd_txfm2d_32x64_neon
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_32x8_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_32x8 av1_fwd_txfm2d_32x8_neon
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_4x16_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_4x16 av1_fwd_txfm2d_4x16_neon
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -651,18 +559,6 @@
                              int bd);
 #define av1_fwd_txfm2d_4x8 av1_fwd_txfm2d_4x8_neon
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_64x16_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-#define av1_fwd_txfm2d_64x16 av1_fwd_txfm2d_64x16_neon
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -699,18 +595,6 @@
                               int bd);
 #define av1_fwd_txfm2d_8x16 av1_fwd_txfm2d_8x16_neon
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_8x32_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_8x32 av1_fwd_txfm2d_8x32_neon
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
diff --git a/third_party/libaom/source/config/ios/arm64/config/aom_config.asm b/third_party/libaom/source/config/ios/arm64/config/aom_config.asm
index 8fbd732d..a6811f5 100644
--- a/third_party/libaom/source/config/ios/arm64/config/aom_config.asm
+++ b/third_party/libaom/source/config/ios/arm64/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2020, Alliance for Open Media. All rights reserved
+; Copyright (c) 2021, Alliance for Open Media. All rights reserved
 ;
 ; This source code is subject to the terms of the BSD 2 Clause License and
 ; the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -48,6 +48,7 @@
 CONFIG_PIC equ 0
 CONFIG_RD_DEBUG equ 0
 CONFIG_REALTIME_ONLY equ 1
+CONFIG_RT_ML_PARTITIONING equ 0
 CONFIG_RUNTIME_CPU_DETECT equ 0
 CONFIG_SHARED equ 0
 CONFIG_SHARP_SETTINGS equ 0
diff --git a/third_party/libaom/source/config/ios/arm64/config/aom_config.h b/third_party/libaom/source/config/ios/arm64/config/aom_config.h
index fa9522d..e0e191d 100644
--- a/third_party/libaom/source/config/ios/arm64/config/aom_config.h
+++ b/third_party/libaom/source/config/ios/arm64/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 0
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 0
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/ios/arm64/config/av1_rtcd.h b/third_party/libaom/source/config/ios/arm64/config/av1_rtcd.h
index 3351447b..c07b2fb 100644
--- a/third_party/libaom/source/config/ios/arm64/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/ios/arm64/config/av1_rtcd.h
@@ -191,62 +191,6 @@
                              int k);
 #define av1_calc_indices_dim2 av1_calc_indices_dim2_c
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -543,18 +487,6 @@
                               int bd);
 #define av1_fwd_txfm2d_16x4 av1_fwd_txfm2d_16x4_neon
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_16x64_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-#define av1_fwd_txfm2d_16x64 av1_fwd_txfm2d_16x64_neon
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -603,30 +535,6 @@
                                int bd);
 #define av1_fwd_txfm2d_32x64 av1_fwd_txfm2d_32x64_neon
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_32x8_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_32x8 av1_fwd_txfm2d_32x8_neon
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_4x16_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_4x16 av1_fwd_txfm2d_4x16_neon
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -651,18 +559,6 @@
                              int bd);
 #define av1_fwd_txfm2d_4x8 av1_fwd_txfm2d_4x8_neon
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_64x16_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-#define av1_fwd_txfm2d_64x16 av1_fwd_txfm2d_64x16_neon
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -699,18 +595,6 @@
                               int bd);
 #define av1_fwd_txfm2d_8x16 av1_fwd_txfm2d_8x16_neon
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_8x32_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_8x32 av1_fwd_txfm2d_8x32_neon
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm
index c49e1dd9..5d2c2a8 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2020, Alliance for Open Media. All rights reserved
+; Copyright (c) 2021, Alliance for Open Media. All rights reserved
 ;
 ; This source code is subject to the terms of the BSD 2 Clause License and
 ; the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -48,6 +48,7 @@
 CONFIG_PIC equ 0
 CONFIG_RD_DEBUG equ 0
 CONFIG_REALTIME_ONLY equ 1
+CONFIG_RT_ML_PARTITIONING equ 0
 CONFIG_RUNTIME_CPU_DETECT equ 1
 CONFIG_SHARED equ 0
 CONFIG_SHARP_SETTINGS equ 0
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h
index 8b5fb4a..9d110c6 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 0
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 1
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/av1_rtcd.h b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/av1_rtcd.h
index 7c689d24..649f86d 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/av1_rtcd.h
@@ -219,62 +219,6 @@
                              int k);
 #define av1_calc_indices_dim2 av1_calc_indices_dim2_c
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -674,22 +618,6 @@
                                         TX_TYPE tx_type,
                                         int bd);
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_16x64_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_16x64)(const int16_t* input,
-                                         int32_t* output,
-                                         int stride,
-                                         TX_TYPE tx_type,
-                                         int bd);
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -754,38 +682,6 @@
                                          TX_TYPE tx_type,
                                          int bd);
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_32x8_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_32x8)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_4x16_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_4x16)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -818,22 +714,6 @@
                                        TX_TYPE tx_type,
                                        int bd);
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_64x16_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_64x16)(const int16_t* input,
-                                         int32_t* output,
-                                         int stride,
-                                         TX_TYPE tx_type,
-                                         int bd);
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -882,22 +762,6 @@
                                         TX_TYPE tx_type,
                                         int bd);
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_8x32_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_8x32)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -2040,9 +1904,6 @@
   av1_fwd_txfm2d_16x4 = av1_fwd_txfm2d_16x4_c;
   if (flags & HAS_NEON)
     av1_fwd_txfm2d_16x4 = av1_fwd_txfm2d_16x4_neon;
-  av1_fwd_txfm2d_16x64 = av1_fwd_txfm2d_16x64_c;
-  if (flags & HAS_NEON)
-    av1_fwd_txfm2d_16x64 = av1_fwd_txfm2d_16x64_neon;
   av1_fwd_txfm2d_16x8 = av1_fwd_txfm2d_16x8_c;
   if (flags & HAS_NEON)
     av1_fwd_txfm2d_16x8 = av1_fwd_txfm2d_16x8_neon;
@@ -2055,21 +1916,12 @@
   av1_fwd_txfm2d_32x64 = av1_fwd_txfm2d_32x64_c;
   if (flags & HAS_NEON)
     av1_fwd_txfm2d_32x64 = av1_fwd_txfm2d_32x64_neon;
-  av1_fwd_txfm2d_32x8 = av1_fwd_txfm2d_32x8_c;
-  if (flags & HAS_NEON)
-    av1_fwd_txfm2d_32x8 = av1_fwd_txfm2d_32x8_neon;
-  av1_fwd_txfm2d_4x16 = av1_fwd_txfm2d_4x16_c;
-  if (flags & HAS_NEON)
-    av1_fwd_txfm2d_4x16 = av1_fwd_txfm2d_4x16_neon;
   av1_fwd_txfm2d_4x4 = av1_fwd_txfm2d_4x4_c;
   if (flags & HAS_NEON)
     av1_fwd_txfm2d_4x4 = av1_fwd_txfm2d_4x4_neon;
   av1_fwd_txfm2d_4x8 = av1_fwd_txfm2d_4x8_c;
   if (flags & HAS_NEON)
     av1_fwd_txfm2d_4x8 = av1_fwd_txfm2d_4x8_neon;
-  av1_fwd_txfm2d_64x16 = av1_fwd_txfm2d_64x16_c;
-  if (flags & HAS_NEON)
-    av1_fwd_txfm2d_64x16 = av1_fwd_txfm2d_64x16_neon;
   av1_fwd_txfm2d_64x32 = av1_fwd_txfm2d_64x32_c;
   if (flags & HAS_NEON)
     av1_fwd_txfm2d_64x32 = av1_fwd_txfm2d_64x32_neon;
@@ -2079,9 +1931,6 @@
   av1_fwd_txfm2d_8x16 = av1_fwd_txfm2d_8x16_c;
   if (flags & HAS_NEON)
     av1_fwd_txfm2d_8x16 = av1_fwd_txfm2d_8x16_neon;
-  av1_fwd_txfm2d_8x32 = av1_fwd_txfm2d_8x32_c;
-  if (flags & HAS_NEON)
-    av1_fwd_txfm2d_8x32 = av1_fwd_txfm2d_8x32_neon;
   av1_fwd_txfm2d_8x4 = av1_fwd_txfm2d_8x4_c;
   if (flags & HAS_NEON)
     av1_fwd_txfm2d_8x4 = av1_fwd_txfm2d_8x4_neon;
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm
index 8fbd732d..a6811f5 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2020, Alliance for Open Media. All rights reserved
+; Copyright (c) 2021, Alliance for Open Media. All rights reserved
 ;
 ; This source code is subject to the terms of the BSD 2 Clause License and
 ; the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -48,6 +48,7 @@
 CONFIG_PIC equ 0
 CONFIG_RD_DEBUG equ 0
 CONFIG_REALTIME_ONLY equ 1
+CONFIG_RT_ML_PARTITIONING equ 0
 CONFIG_RUNTIME_CPU_DETECT equ 0
 CONFIG_SHARED equ 0
 CONFIG_SHARP_SETTINGS equ 0
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h
index fa9522d..e0e191d 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm-neon/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 0
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 0
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/av1_rtcd.h b/third_party/libaom/source/config/linux/arm-neon/config/av1_rtcd.h
index 3351447b..c07b2fb 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm-neon/config/av1_rtcd.h
@@ -191,62 +191,6 @@
                              int k);
 #define av1_calc_indices_dim2 av1_calc_indices_dim2_c
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -543,18 +487,6 @@
                               int bd);
 #define av1_fwd_txfm2d_16x4 av1_fwd_txfm2d_16x4_neon
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_16x64_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-#define av1_fwd_txfm2d_16x64 av1_fwd_txfm2d_16x64_neon
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -603,30 +535,6 @@
                                int bd);
 #define av1_fwd_txfm2d_32x64 av1_fwd_txfm2d_32x64_neon
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_32x8_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_32x8 av1_fwd_txfm2d_32x8_neon
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_4x16_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_4x16 av1_fwd_txfm2d_4x16_neon
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -651,18 +559,6 @@
                              int bd);
 #define av1_fwd_txfm2d_4x8 av1_fwd_txfm2d_4x8_neon
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_64x16_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-#define av1_fwd_txfm2d_64x16 av1_fwd_txfm2d_64x16_neon
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -699,18 +595,6 @@
                               int bd);
 #define av1_fwd_txfm2d_8x16 av1_fwd_txfm2d_8x16_neon
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_8x32_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_8x32 av1_fwd_txfm2d_8x32_neon
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
diff --git a/third_party/libaom/source/config/linux/arm/config/aom_config.asm b/third_party/libaom/source/config/linux/arm/config/aom_config.asm
index 27aeefe9..fb3b6bd1 100644
--- a/third_party/libaom/source/config/linux/arm/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2020, Alliance for Open Media. All rights reserved
+; Copyright (c) 2021, Alliance for Open Media. All rights reserved
 ;
 ; This source code is subject to the terms of the BSD 2 Clause License and
 ; the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -48,6 +48,7 @@
 CONFIG_PIC equ 0
 CONFIG_RD_DEBUG equ 0
 CONFIG_REALTIME_ONLY equ 1
+CONFIG_RT_ML_PARTITIONING equ 0
 CONFIG_RUNTIME_CPU_DETECT equ 0
 CONFIG_SHARED equ 0
 CONFIG_SHARP_SETTINGS equ 0
diff --git a/third_party/libaom/source/config/linux/arm/config/aom_config.h b/third_party/libaom/source/config/linux/arm/config/aom_config.h
index 95d58ab..a742639 100644
--- a/third_party/libaom/source/config/linux/arm/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 0
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 0
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/linux/arm/config/av1_rtcd.h b/third_party/libaom/source/config/linux/arm/config/av1_rtcd.h
index cafb14e..a8e88f8 100644
--- a/third_party/libaom/source/config/linux/arm/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm/config/av1_rtcd.h
@@ -159,62 +159,6 @@
                              int k);
 #define av1_calc_indices_dim2 av1_calc_indices_dim2_c
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -398,13 +342,6 @@
                            int bd);
 #define av1_fwd_txfm2d_16x4 av1_fwd_txfm2d_16x4_c
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-#define av1_fwd_txfm2d_16x64 av1_fwd_txfm2d_16x64_c
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -433,20 +370,6 @@
                             int bd);
 #define av1_fwd_txfm2d_32x64 av1_fwd_txfm2d_32x64_c
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-#define av1_fwd_txfm2d_32x8 av1_fwd_txfm2d_32x8_c
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-#define av1_fwd_txfm2d_4x16 av1_fwd_txfm2d_4x16_c
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -461,13 +384,6 @@
                           int bd);
 #define av1_fwd_txfm2d_4x8 av1_fwd_txfm2d_4x8_c
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-#define av1_fwd_txfm2d_64x16 av1_fwd_txfm2d_64x16_c
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -489,13 +405,6 @@
                            int bd);
 #define av1_fwd_txfm2d_8x16 av1_fwd_txfm2d_8x16_c
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-#define av1_fwd_txfm2d_8x32 av1_fwd_txfm2d_8x32_c
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
diff --git a/third_party/libaom/source/config/linux/arm64/config/aom_config.asm b/third_party/libaom/source/config/linux/arm64/config/aom_config.asm
index 8fbd732d..a6811f5 100644
--- a/third_party/libaom/source/config/linux/arm64/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/arm64/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2020, Alliance for Open Media. All rights reserved
+; Copyright (c) 2021, Alliance for Open Media. All rights reserved
 ;
 ; This source code is subject to the terms of the BSD 2 Clause License and
 ; the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -48,6 +48,7 @@
 CONFIG_PIC equ 0
 CONFIG_RD_DEBUG equ 0
 CONFIG_REALTIME_ONLY equ 1
+CONFIG_RT_ML_PARTITIONING equ 0
 CONFIG_RUNTIME_CPU_DETECT equ 0
 CONFIG_SHARED equ 0
 CONFIG_SHARP_SETTINGS equ 0
diff --git a/third_party/libaom/source/config/linux/arm64/config/aom_config.h b/third_party/libaom/source/config/linux/arm64/config/aom_config.h
index fa9522d..e0e191d 100644
--- a/third_party/libaom/source/config/linux/arm64/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/arm64/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 0
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 0
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/linux/arm64/config/av1_rtcd.h b/third_party/libaom/source/config/linux/arm64/config/av1_rtcd.h
index 3351447b..c07b2fb 100644
--- a/third_party/libaom/source/config/linux/arm64/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm64/config/av1_rtcd.h
@@ -191,62 +191,6 @@
                              int k);
 #define av1_calc_indices_dim2 av1_calc_indices_dim2_c
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -543,18 +487,6 @@
                               int bd);
 #define av1_fwd_txfm2d_16x4 av1_fwd_txfm2d_16x4_neon
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_16x64_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-#define av1_fwd_txfm2d_16x64 av1_fwd_txfm2d_16x64_neon
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -603,30 +535,6 @@
                                int bd);
 #define av1_fwd_txfm2d_32x64 av1_fwd_txfm2d_32x64_neon
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_32x8_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_32x8 av1_fwd_txfm2d_32x8_neon
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_4x16_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_4x16 av1_fwd_txfm2d_4x16_neon
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -651,18 +559,6 @@
                              int bd);
 #define av1_fwd_txfm2d_4x8 av1_fwd_txfm2d_4x8_neon
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_64x16_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-#define av1_fwd_txfm2d_64x16 av1_fwd_txfm2d_64x16_neon
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -699,18 +595,6 @@
                               int bd);
 #define av1_fwd_txfm2d_8x16 av1_fwd_txfm2d_8x16_neon
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_8x32_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_8x32 av1_fwd_txfm2d_8x32_neon
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
diff --git a/third_party/libaom/source/config/linux/generic/config/aom_config.asm b/third_party/libaom/source/config/linux/generic/config/aom_config.asm
index a7bc0cb..70f3183 100644
--- a/third_party/libaom/source/config/linux/generic/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/generic/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2020, Alliance for Open Media. All rights reserved
+; Copyright (c) 2021, Alliance for Open Media. All rights reserved
 ;
 ; This source code is subject to the terms of the BSD 2 Clause License and
 ; the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -48,6 +48,7 @@
 CONFIG_PIC equ 0
 CONFIG_RD_DEBUG equ 0
 CONFIG_REALTIME_ONLY equ 1
+CONFIG_RT_ML_PARTITIONING equ 0
 CONFIG_RUNTIME_CPU_DETECT equ 1
 CONFIG_SHARED equ 0
 CONFIG_SHARP_SETTINGS equ 0
diff --git a/third_party/libaom/source/config/linux/generic/config/aom_config.h b/third_party/libaom/source/config/linux/generic/config/aom_config.h
index fe497f6..64a33fa4d 100644
--- a/third_party/libaom/source/config/linux/generic/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/generic/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 0
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 1
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/linux/generic/config/av1_rtcd.h b/third_party/libaom/source/config/linux/generic/config/av1_rtcd.h
index d86aa81..2f7ce5b 100644
--- a/third_party/libaom/source/config/linux/generic/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/generic/config/av1_rtcd.h
@@ -159,62 +159,6 @@
                              int k);
 #define av1_calc_indices_dim2 av1_calc_indices_dim2_c
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -398,13 +342,6 @@
                            int bd);
 #define av1_fwd_txfm2d_16x4 av1_fwd_txfm2d_16x4_c
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-#define av1_fwd_txfm2d_16x64 av1_fwd_txfm2d_16x64_c
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -433,20 +370,6 @@
                             int bd);
 #define av1_fwd_txfm2d_32x64 av1_fwd_txfm2d_32x64_c
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-#define av1_fwd_txfm2d_32x8 av1_fwd_txfm2d_32x8_c
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-#define av1_fwd_txfm2d_4x16 av1_fwd_txfm2d_4x16_c
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -461,13 +384,6 @@
                           int bd);
 #define av1_fwd_txfm2d_4x8 av1_fwd_txfm2d_4x8_c
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-#define av1_fwd_txfm2d_64x16 av1_fwd_txfm2d_64x16_c
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -489,13 +405,6 @@
                            int bd);
 #define av1_fwd_txfm2d_8x16 av1_fwd_txfm2d_8x16_c
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-#define av1_fwd_txfm2d_8x32 av1_fwd_txfm2d_8x32_c
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
diff --git a/third_party/libaom/source/config/linux/ia32/config/aom_config.asm b/third_party/libaom/source/config/linux/ia32/config/aom_config.asm
index e04a488..6a7fff6 100644
--- a/third_party/libaom/source/config/linux/ia32/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/ia32/config/aom_config.asm
@@ -38,6 +38,7 @@
 %define CONFIG_PIC 1
 %define CONFIG_RD_DEBUG 0
 %define CONFIG_REALTIME_ONLY 1
+%define CONFIG_RT_ML_PARTITIONING 0
 %define CONFIG_RUNTIME_CPU_DETECT 1
 %define CONFIG_SHARED 0
 %define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/linux/ia32/config/aom_config.h b/third_party/libaom/source/config/linux/ia32/config/aom_config.h
index 0e1e154..0092a4c 100644
--- a/third_party/libaom/source/config/linux/ia32/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/ia32/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 1
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 1
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/linux/ia32/config/av1_rtcd.h b/third_party/libaom/source/config/linux/ia32/config/av1_rtcd.h
index 229e3c1..02b5a66 100644
--- a/third_party/libaom/source/config/linux/ia32/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/ia32/config/av1_rtcd.h
@@ -271,62 +271,6 @@
                                           int n,
                                           int k);
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -876,22 +820,6 @@
                                         TX_TYPE tx_type,
                                         int bd);
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_16x64_sse4_1(const int16_t* input,
-                                 int32_t* output,
-                                 int stride,
-                                 TX_TYPE tx_type,
-                                 int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_16x64)(const int16_t* input,
-                                         int32_t* output,
-                                         int stride,
-                                         TX_TYPE tx_type,
-                                         int bd);
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -966,38 +894,6 @@
                                          TX_TYPE tx_type,
                                          int bd);
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_32x8_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_32x8)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_4x16_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_4x16)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -1030,22 +926,6 @@
                                        TX_TYPE tx_type,
                                        int bd);
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_64x16_sse4_1(const int16_t* input,
-                                 int32_t* output,
-                                 int stride,
-                                 TX_TYPE tx_type,
-                                 int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_64x16)(const int16_t* input,
-                                         int32_t* output,
-                                         int stride,
-                                         TX_TYPE tx_type,
-                                         int bd);
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -1104,22 +984,6 @@
                                         TX_TYPE tx_type,
                                         int bd);
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_8x32_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_8x32)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -2267,9 +2131,6 @@
   av1_fwd_txfm2d_16x4 = av1_fwd_txfm2d_16x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_16x4 = av1_fwd_txfm2d_16x4_sse4_1;
-  av1_fwd_txfm2d_16x64 = av1_fwd_txfm2d_16x64_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_16x64 = av1_fwd_txfm2d_16x64_sse4_1;
   av1_fwd_txfm2d_16x8 = av1_fwd_txfm2d_16x8_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_16x8 = av1_fwd_txfm2d_16x8_sse4_1;
@@ -2286,21 +2147,12 @@
   av1_fwd_txfm2d_32x64 = av1_fwd_txfm2d_32x64_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_32x64 = av1_fwd_txfm2d_32x64_sse4_1;
-  av1_fwd_txfm2d_32x8 = av1_fwd_txfm2d_32x8_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_32x8 = av1_fwd_txfm2d_32x8_sse4_1;
-  av1_fwd_txfm2d_4x16 = av1_fwd_txfm2d_4x16_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_4x16 = av1_fwd_txfm2d_4x16_sse4_1;
   av1_fwd_txfm2d_4x4 = av1_fwd_txfm2d_4x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_4x4 = av1_fwd_txfm2d_4x4_sse4_1;
   av1_fwd_txfm2d_4x8 = av1_fwd_txfm2d_4x8_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_4x8 = av1_fwd_txfm2d_4x8_sse4_1;
-  av1_fwd_txfm2d_64x16 = av1_fwd_txfm2d_64x16_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_64x16 = av1_fwd_txfm2d_64x16_sse4_1;
   av1_fwd_txfm2d_64x32 = av1_fwd_txfm2d_64x32_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_64x32 = av1_fwd_txfm2d_64x32_sse4_1;
@@ -2314,9 +2166,6 @@
     av1_fwd_txfm2d_8x16 = av1_fwd_txfm2d_8x16_sse4_1;
   if (flags & HAS_AVX2)
     av1_fwd_txfm2d_8x16 = av1_fwd_txfm2d_8x16_avx2;
-  av1_fwd_txfm2d_8x32 = av1_fwd_txfm2d_8x32_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_8x32 = av1_fwd_txfm2d_8x32_sse4_1;
   av1_fwd_txfm2d_8x4 = av1_fwd_txfm2d_8x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_8x4 = av1_fwd_txfm2d_8x4_sse4_1;
diff --git a/third_party/libaom/source/config/linux/x64/config/aom_config.asm b/third_party/libaom/source/config/linux/x64/config/aom_config.asm
index 212b282..7199a6b 100644
--- a/third_party/libaom/source/config/linux/x64/config/aom_config.asm
+++ b/third_party/libaom/source/config/linux/x64/config/aom_config.asm
@@ -38,6 +38,7 @@
 %define CONFIG_PIC 0
 %define CONFIG_RD_DEBUG 0
 %define CONFIG_REALTIME_ONLY 1
+%define CONFIG_RT_ML_PARTITIONING 0
 %define CONFIG_RUNTIME_CPU_DETECT 1
 %define CONFIG_SHARED 0
 %define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/linux/x64/config/aom_config.h b/third_party/libaom/source/config/linux/x64/config/aom_config.h
index 592d1f7..e0ed6a5a 100644
--- a/third_party/libaom/source/config/linux/x64/config/aom_config.h
+++ b/third_party/libaom/source/config/linux/x64/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 0
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 1
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/linux/x64/config/av1_rtcd.h b/third_party/libaom/source/config/linux/x64/config/av1_rtcd.h
index 2f2ca6d..316f26a 100644
--- a/third_party/libaom/source/config/linux/x64/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/linux/x64/config/av1_rtcd.h
@@ -271,62 +271,6 @@
                                           int n,
                                           int k);
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -876,22 +820,6 @@
                                         TX_TYPE tx_type,
                                         int bd);
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_16x64_sse4_1(const int16_t* input,
-                                 int32_t* output,
-                                 int stride,
-                                 TX_TYPE tx_type,
-                                 int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_16x64)(const int16_t* input,
-                                         int32_t* output,
-                                         int stride,
-                                         TX_TYPE tx_type,
-                                         int bd);
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -966,38 +894,6 @@
                                          TX_TYPE tx_type,
                                          int bd);
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_32x8_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_32x8)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_4x16_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_4x16)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -1030,22 +926,6 @@
                                        TX_TYPE tx_type,
                                        int bd);
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_64x16_sse4_1(const int16_t* input,
-                                 int32_t* output,
-                                 int stride,
-                                 TX_TYPE tx_type,
-                                 int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_64x16)(const int16_t* input,
-                                         int32_t* output,
-                                         int stride,
-                                         TX_TYPE tx_type,
-                                         int bd);
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -1104,22 +984,6 @@
                                         TX_TYPE tx_type,
                                         int bd);
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_8x32_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_8x32)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -2300,9 +2164,6 @@
   av1_fwd_txfm2d_16x4 = av1_fwd_txfm2d_16x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_16x4 = av1_fwd_txfm2d_16x4_sse4_1;
-  av1_fwd_txfm2d_16x64 = av1_fwd_txfm2d_16x64_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_16x64 = av1_fwd_txfm2d_16x64_sse4_1;
   av1_fwd_txfm2d_16x8 = av1_fwd_txfm2d_16x8_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_16x8 = av1_fwd_txfm2d_16x8_sse4_1;
@@ -2319,21 +2180,12 @@
   av1_fwd_txfm2d_32x64 = av1_fwd_txfm2d_32x64_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_32x64 = av1_fwd_txfm2d_32x64_sse4_1;
-  av1_fwd_txfm2d_32x8 = av1_fwd_txfm2d_32x8_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_32x8 = av1_fwd_txfm2d_32x8_sse4_1;
-  av1_fwd_txfm2d_4x16 = av1_fwd_txfm2d_4x16_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_4x16 = av1_fwd_txfm2d_4x16_sse4_1;
   av1_fwd_txfm2d_4x4 = av1_fwd_txfm2d_4x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_4x4 = av1_fwd_txfm2d_4x4_sse4_1;
   av1_fwd_txfm2d_4x8 = av1_fwd_txfm2d_4x8_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_4x8 = av1_fwd_txfm2d_4x8_sse4_1;
-  av1_fwd_txfm2d_64x16 = av1_fwd_txfm2d_64x16_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_64x16 = av1_fwd_txfm2d_64x16_sse4_1;
   av1_fwd_txfm2d_64x32 = av1_fwd_txfm2d_64x32_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_64x32 = av1_fwd_txfm2d_64x32_sse4_1;
@@ -2347,9 +2199,6 @@
     av1_fwd_txfm2d_8x16 = av1_fwd_txfm2d_8x16_sse4_1;
   if (flags & HAS_AVX2)
     av1_fwd_txfm2d_8x16 = av1_fwd_txfm2d_8x16_avx2;
-  av1_fwd_txfm2d_8x32 = av1_fwd_txfm2d_8x32_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_8x32 = av1_fwd_txfm2d_8x32_sse4_1;
   av1_fwd_txfm2d_8x4 = av1_fwd_txfm2d_8x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_8x4 = av1_fwd_txfm2d_8x4_sse4_1;
diff --git a/third_party/libaom/source/config/win/arm64/config/aom_config.asm b/third_party/libaom/source/config/win/arm64/config/aom_config.asm
index 8fbd732d..a6811f5 100644
--- a/third_party/libaom/source/config/win/arm64/config/aom_config.asm
+++ b/third_party/libaom/source/config/win/arm64/config/aom_config.asm
@@ -1,5 +1,5 @@
 ;
-; Copyright (c) 2020, Alliance for Open Media. All rights reserved
+; Copyright (c) 2021, Alliance for Open Media. All rights reserved
 ;
 ; This source code is subject to the terms of the BSD 2 Clause License and
 ; the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -48,6 +48,7 @@
 CONFIG_PIC equ 0
 CONFIG_RD_DEBUG equ 0
 CONFIG_REALTIME_ONLY equ 1
+CONFIG_RT_ML_PARTITIONING equ 0
 CONFIG_RUNTIME_CPU_DETECT equ 0
 CONFIG_SHARED equ 0
 CONFIG_SHARP_SETTINGS equ 0
diff --git a/third_party/libaom/source/config/win/arm64/config/aom_config.h b/third_party/libaom/source/config/win/arm64/config/aom_config.h
index c5a0784..6765acfc 100644
--- a/third_party/libaom/source/config/win/arm64/config/aom_config.h
+++ b/third_party/libaom/source/config/win/arm64/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 0
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 0
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/win/arm64/config/av1_rtcd.h b/third_party/libaom/source/config/win/arm64/config/av1_rtcd.h
index 3351447b..c07b2fb 100644
--- a/third_party/libaom/source/config/win/arm64/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/win/arm64/config/av1_rtcd.h
@@ -191,62 +191,6 @@
                              int k);
 #define av1_calc_indices_dim2 av1_calc_indices_dim2_c
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -543,18 +487,6 @@
                               int bd);
 #define av1_fwd_txfm2d_16x4 av1_fwd_txfm2d_16x4_neon
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_16x64_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-#define av1_fwd_txfm2d_16x64 av1_fwd_txfm2d_16x64_neon
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -603,30 +535,6 @@
                                int bd);
 #define av1_fwd_txfm2d_32x64 av1_fwd_txfm2d_32x64_neon
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_32x8_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_32x8 av1_fwd_txfm2d_32x8_neon
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_4x16_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_4x16 av1_fwd_txfm2d_4x16_neon
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -651,18 +559,6 @@
                              int bd);
 #define av1_fwd_txfm2d_4x8 av1_fwd_txfm2d_4x8_neon
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_64x16_neon(const int16_t* input,
-                               int32_t* output,
-                               int stride,
-                               TX_TYPE tx_type,
-                               int bd);
-#define av1_fwd_txfm2d_64x16 av1_fwd_txfm2d_64x16_neon
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -699,18 +595,6 @@
                               int bd);
 #define av1_fwd_txfm2d_8x16 av1_fwd_txfm2d_8x16_neon
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_8x32_neon(const int16_t* input,
-                              int32_t* output,
-                              int stride,
-                              TX_TYPE tx_type,
-                              int bd);
-#define av1_fwd_txfm2d_8x32 av1_fwd_txfm2d_8x32_neon
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
diff --git a/third_party/libaom/source/config/win/ia32/config/aom_config.asm b/third_party/libaom/source/config/win/ia32/config/aom_config.asm
index 41bb8582..b5f9fc6 100644
--- a/third_party/libaom/source/config/win/ia32/config/aom_config.asm
+++ b/third_party/libaom/source/config/win/ia32/config/aom_config.asm
@@ -38,6 +38,7 @@
 %define CONFIG_PIC 1
 %define CONFIG_RD_DEBUG 0
 %define CONFIG_REALTIME_ONLY 1
+%define CONFIG_RT_ML_PARTITIONING 0
 %define CONFIG_RUNTIME_CPU_DETECT 1
 %define CONFIG_SHARED 0
 %define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/win/ia32/config/aom_config.h b/third_party/libaom/source/config/win/ia32/config/aom_config.h
index 239e6a8d4..a2fb0330 100644
--- a/third_party/libaom/source/config/win/ia32/config/aom_config.h
+++ b/third_party/libaom/source/config/win/ia32/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 1
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 1
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/win/ia32/config/av1_rtcd.h b/third_party/libaom/source/config/win/ia32/config/av1_rtcd.h
index 229e3c1..02b5a66 100644
--- a/third_party/libaom/source/config/win/ia32/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/win/ia32/config/av1_rtcd.h
@@ -271,62 +271,6 @@
                                           int n,
                                           int k);
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -876,22 +820,6 @@
                                         TX_TYPE tx_type,
                                         int bd);
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_16x64_sse4_1(const int16_t* input,
-                                 int32_t* output,
-                                 int stride,
-                                 TX_TYPE tx_type,
-                                 int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_16x64)(const int16_t* input,
-                                         int32_t* output,
-                                         int stride,
-                                         TX_TYPE tx_type,
-                                         int bd);
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -966,38 +894,6 @@
                                          TX_TYPE tx_type,
                                          int bd);
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_32x8_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_32x8)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_4x16_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_4x16)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -1030,22 +926,6 @@
                                        TX_TYPE tx_type,
                                        int bd);
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_64x16_sse4_1(const int16_t* input,
-                                 int32_t* output,
-                                 int stride,
-                                 TX_TYPE tx_type,
-                                 int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_64x16)(const int16_t* input,
-                                         int32_t* output,
-                                         int stride,
-                                         TX_TYPE tx_type,
-                                         int bd);
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -1104,22 +984,6 @@
                                         TX_TYPE tx_type,
                                         int bd);
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_8x32_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_8x32)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -2267,9 +2131,6 @@
   av1_fwd_txfm2d_16x4 = av1_fwd_txfm2d_16x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_16x4 = av1_fwd_txfm2d_16x4_sse4_1;
-  av1_fwd_txfm2d_16x64 = av1_fwd_txfm2d_16x64_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_16x64 = av1_fwd_txfm2d_16x64_sse4_1;
   av1_fwd_txfm2d_16x8 = av1_fwd_txfm2d_16x8_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_16x8 = av1_fwd_txfm2d_16x8_sse4_1;
@@ -2286,21 +2147,12 @@
   av1_fwd_txfm2d_32x64 = av1_fwd_txfm2d_32x64_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_32x64 = av1_fwd_txfm2d_32x64_sse4_1;
-  av1_fwd_txfm2d_32x8 = av1_fwd_txfm2d_32x8_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_32x8 = av1_fwd_txfm2d_32x8_sse4_1;
-  av1_fwd_txfm2d_4x16 = av1_fwd_txfm2d_4x16_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_4x16 = av1_fwd_txfm2d_4x16_sse4_1;
   av1_fwd_txfm2d_4x4 = av1_fwd_txfm2d_4x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_4x4 = av1_fwd_txfm2d_4x4_sse4_1;
   av1_fwd_txfm2d_4x8 = av1_fwd_txfm2d_4x8_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_4x8 = av1_fwd_txfm2d_4x8_sse4_1;
-  av1_fwd_txfm2d_64x16 = av1_fwd_txfm2d_64x16_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_64x16 = av1_fwd_txfm2d_64x16_sse4_1;
   av1_fwd_txfm2d_64x32 = av1_fwd_txfm2d_64x32_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_64x32 = av1_fwd_txfm2d_64x32_sse4_1;
@@ -2314,9 +2166,6 @@
     av1_fwd_txfm2d_8x16 = av1_fwd_txfm2d_8x16_sse4_1;
   if (flags & HAS_AVX2)
     av1_fwd_txfm2d_8x16 = av1_fwd_txfm2d_8x16_avx2;
-  av1_fwd_txfm2d_8x32 = av1_fwd_txfm2d_8x32_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_8x32 = av1_fwd_txfm2d_8x32_sse4_1;
   av1_fwd_txfm2d_8x4 = av1_fwd_txfm2d_8x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_8x4 = av1_fwd_txfm2d_8x4_sse4_1;
diff --git a/third_party/libaom/source/config/win/x64/config/aom_config.asm b/third_party/libaom/source/config/win/x64/config/aom_config.asm
index 36b02a9..dc6a3091 100644
--- a/third_party/libaom/source/config/win/x64/config/aom_config.asm
+++ b/third_party/libaom/source/config/win/x64/config/aom_config.asm
@@ -38,6 +38,7 @@
 %define CONFIG_PIC 0
 %define CONFIG_RD_DEBUG 0
 %define CONFIG_REALTIME_ONLY 1
+%define CONFIG_RT_ML_PARTITIONING 0
 %define CONFIG_RUNTIME_CPU_DETECT 1
 %define CONFIG_SHARED 0
 %define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/win/x64/config/aom_config.h b/third_party/libaom/source/config/win/x64/config/aom_config.h
index 1920d594..21f96a0 100644
--- a/third_party/libaom/source/config/win/x64/config/aom_config.h
+++ b/third_party/libaom/source/config/win/x64/config/aom_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
  *
  * This source code is subject to the terms of the BSD 2 Clause License and
  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
@@ -50,6 +50,7 @@
 #define CONFIG_PIC 0
 #define CONFIG_RD_DEBUG 0
 #define CONFIG_REALTIME_ONLY 1
+#define CONFIG_RT_ML_PARTITIONING 0
 #define CONFIG_RUNTIME_CPU_DETECT 1
 #define CONFIG_SHARED 0
 #define CONFIG_SHARP_SETTINGS 0
diff --git a/third_party/libaom/source/config/win/x64/config/av1_rtcd.h b/third_party/libaom/source/config/win/x64/config/av1_rtcd.h
index 2f2ca6d..316f26a 100644
--- a/third_party/libaom/source/config/win/x64/config/av1_rtcd.h
+++ b/third_party/libaom/source/config/win/x64/config/av1_rtcd.h
@@ -271,62 +271,6 @@
                                           int n,
                                           int k);
 
-void av1_cnn_activate_c(float** input,
-                        int channels,
-                        int width,
-                        int height,
-                        int stride,
-                        ACTIVATION layer_activation);
-#define av1_cnn_activate av1_cnn_activate_c
-
-void av1_cnn_add_c(float** input,
-                   int channels,
-                   int width,
-                   int height,
-                   int stride,
-                   const float** add);
-#define av1_cnn_add av1_cnn_add_c
-
-void av1_cnn_batchnorm_c(float** image,
-                         int channels,
-                         int width,
-                         int height,
-                         int stride,
-                         const float* gamma,
-                         const float* beta,
-                         const float* mean,
-                         const float* std);
-#define av1_cnn_batchnorm av1_cnn_batchnorm_c
-
-void av1_cnn_convolve_c(const float** input,
-                        int in_width,
-                        int in_height,
-                        int in_stride,
-                        const CNN_LAYER_CONFIG* layer_config,
-                        float** output,
-                        int out_stride,
-                        int start_idx,
-                        int step);
-#define av1_cnn_convolve av1_cnn_convolve_c
-
-void av1_cnn_deconvolve_c(const float** input,
-                          int in_width,
-                          int in_height,
-                          int in_stride,
-                          const CNN_LAYER_CONFIG* layer_config,
-                          float** output,
-                          int out_stride);
-#define av1_cnn_deconvolve av1_cnn_deconvolve_c
-
-void av1_cnn_predict_c(const float** input,
-                       int in_width,
-                       int in_height,
-                       int in_stride,
-                       const CNN_CONFIG* cnn_config,
-                       const CNN_THREAD_DATA* thread_data,
-                       CNN_MULTI_OUT* output_struct);
-#define av1_cnn_predict av1_cnn_predict_c
-
 double av1_compute_cross_correlation_c(unsigned char* im1,
                                        int stride1,
                                        int x1,
@@ -876,22 +820,6 @@
                                         TX_TYPE tx_type,
                                         int bd);
 
-void av1_fwd_txfm2d_16x64_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_16x64_sse4_1(const int16_t* input,
-                                 int32_t* output,
-                                 int stride,
-                                 TX_TYPE tx_type,
-                                 int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_16x64)(const int16_t* input,
-                                         int32_t* output,
-                                         int stride,
-                                         TX_TYPE tx_type,
-                                         int bd);
-
 void av1_fwd_txfm2d_16x8_c(const int16_t* input,
                            int32_t* output,
                            int stride,
@@ -966,38 +894,6 @@
                                          TX_TYPE tx_type,
                                          int bd);
 
-void av1_fwd_txfm2d_32x8_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_32x8_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_32x8)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
-void av1_fwd_txfm2d_4x16_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_4x16_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_4x16)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
 void av1_fwd_txfm2d_4x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -1030,22 +926,6 @@
                                        TX_TYPE tx_type,
                                        int bd);
 
-void av1_fwd_txfm2d_64x16_c(const int16_t* input,
-                            int32_t* output,
-                            int stride,
-                            TX_TYPE tx_type,
-                            int bd);
-void av1_fwd_txfm2d_64x16_sse4_1(const int16_t* input,
-                                 int32_t* output,
-                                 int stride,
-                                 TX_TYPE tx_type,
-                                 int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_64x16)(const int16_t* input,
-                                         int32_t* output,
-                                         int stride,
-                                         TX_TYPE tx_type,
-                                         int bd);
-
 void av1_fwd_txfm2d_64x32_c(const int16_t* input,
                             int32_t* output,
                             int stride,
@@ -1104,22 +984,6 @@
                                         TX_TYPE tx_type,
                                         int bd);
 
-void av1_fwd_txfm2d_8x32_c(const int16_t* input,
-                           int32_t* output,
-                           int stride,
-                           TX_TYPE tx_type,
-                           int bd);
-void av1_fwd_txfm2d_8x32_sse4_1(const int16_t* input,
-                                int32_t* output,
-                                int stride,
-                                TX_TYPE tx_type,
-                                int bd);
-RTCD_EXTERN void (*av1_fwd_txfm2d_8x32)(const int16_t* input,
-                                        int32_t* output,
-                                        int stride,
-                                        TX_TYPE tx_type,
-                                        int bd);
-
 void av1_fwd_txfm2d_8x4_c(const int16_t* input,
                           int32_t* output,
                           int stride,
@@ -2300,9 +2164,6 @@
   av1_fwd_txfm2d_16x4 = av1_fwd_txfm2d_16x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_16x4 = av1_fwd_txfm2d_16x4_sse4_1;
-  av1_fwd_txfm2d_16x64 = av1_fwd_txfm2d_16x64_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_16x64 = av1_fwd_txfm2d_16x64_sse4_1;
   av1_fwd_txfm2d_16x8 = av1_fwd_txfm2d_16x8_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_16x8 = av1_fwd_txfm2d_16x8_sse4_1;
@@ -2319,21 +2180,12 @@
   av1_fwd_txfm2d_32x64 = av1_fwd_txfm2d_32x64_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_32x64 = av1_fwd_txfm2d_32x64_sse4_1;
-  av1_fwd_txfm2d_32x8 = av1_fwd_txfm2d_32x8_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_32x8 = av1_fwd_txfm2d_32x8_sse4_1;
-  av1_fwd_txfm2d_4x16 = av1_fwd_txfm2d_4x16_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_4x16 = av1_fwd_txfm2d_4x16_sse4_1;
   av1_fwd_txfm2d_4x4 = av1_fwd_txfm2d_4x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_4x4 = av1_fwd_txfm2d_4x4_sse4_1;
   av1_fwd_txfm2d_4x8 = av1_fwd_txfm2d_4x8_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_4x8 = av1_fwd_txfm2d_4x8_sse4_1;
-  av1_fwd_txfm2d_64x16 = av1_fwd_txfm2d_64x16_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_64x16 = av1_fwd_txfm2d_64x16_sse4_1;
   av1_fwd_txfm2d_64x32 = av1_fwd_txfm2d_64x32_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_64x32 = av1_fwd_txfm2d_64x32_sse4_1;
@@ -2347,9 +2199,6 @@
     av1_fwd_txfm2d_8x16 = av1_fwd_txfm2d_8x16_sse4_1;
   if (flags & HAS_AVX2)
     av1_fwd_txfm2d_8x16 = av1_fwd_txfm2d_8x16_avx2;
-  av1_fwd_txfm2d_8x32 = av1_fwd_txfm2d_8x32_c;
-  if (flags & HAS_SSE4_1)
-    av1_fwd_txfm2d_8x32 = av1_fwd_txfm2d_8x32_sse4_1;
   av1_fwd_txfm2d_8x4 = av1_fwd_txfm2d_8x4_c;
   if (flags & HAS_SSE4_1)
     av1_fwd_txfm2d_8x4 = av1_fwd_txfm2d_8x4_sse4_1;
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath
index 66fa5ab..6524fe3 100644
--- a/tools/android/eclipse/.classpath
+++ b/tools/android/eclipse/.classpath
@@ -302,7 +302,6 @@
     <classpathentry kind="src" path="url/android/java/src"/>
     <classpathentry kind="src" path="url/android/javatests/src"/>
     <classpathentry kind="src" path="weblayer/browser/android/javatests/src"/>
-    <classpathentry kind="lib" path="../src/third_party/android_sdk/public/extras/google/gcm/gcm-client/dist/gcm.jar" sourcepath="../src/third_party/android_sdk/public/extras/google/gcm/gcm-client/src"/>
     <classpathentry kind="lib" path="../src/third_party/android_sdk/public/platforms/android-30/android.jar" sourcepath="../src/third_party/android_sdk/public/sources/">
         <attributes>
             <attribute name="javadoc_location" value="http://developer.android.com/reference/"/>
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index 01e1775..b8488f6 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -844,8 +844,9 @@
     metadata[models.METADATA_APK_FILENAME] = shorten_path(
         args.minimal_apks_file)
     if args.split_name and args.split_name != 'base':
-      metadata[models.METADATA_APK_SPLIT_NAME] = args.split_name
       metadata[models.METADATA_APK_SIZE] = os.path.getsize(args.apk_file)
+      metadata[models.METADATA_APK_SPLIT_NAME] = args.split_name
+      metadata[models.METADATA_APK_SPLIT_ON_DEMAND] = _IsOnDemand(args.apk_file)
     else:
       sizes_by_module = _CollectModuleSizes(args.minimal_apks_file)
       for name, size in sizes_by_module.items():
@@ -2210,8 +2211,6 @@
         with zip_util.UnzipToTemp(
             sub_args.minimal_apks_file,
             'splits/{}-master.apk'.format(module_name)) as temp:
-          if _IsOnDemand(temp):
-            continue
           module_sub_args = copy.copy(sub_args)
           module_sub_args.apk_file = temp
           module_sub_args.split_name = module_name
diff --git a/tools/binary_size/libsupersize/integration_test.py b/tools/binary_size/libsupersize/integration_test.py
index a24fe5f..90596b4 100755
--- a/tools/binary_size/libsupersize/integration_test.py
+++ b/tools/binary_size/libsupersize/integration_test.py
@@ -48,7 +48,9 @@
 # The following files are dynamically created.
 _TEST_ELF_PATH = os.path.join(_TEST_OUTPUT_DIR, 'elf')
 _TEST_APK_PATH = os.path.join(_TEST_OUTPUT_DIR, 'test.apk')
-_TEST_OTHER_SPLIT_APK_PATH = os.path.join(_TEST_OUTPUT_DIR, 'other.apk')
+_TEST_NOT_ON_DEMAND_SPLIT_APK_PATH = os.path.join(_TEST_OUTPUT_DIR,
+                                                  'not_on_demand.apk')
+_TEST_ON_DEMAND_SPLIT_APK_PATH = os.path.join(_TEST_OUTPUT_DIR, 'on_demand.apk')
 _TEST_MINIMAL_APKS_PATH = os.path.join(_TEST_OUTPUT_DIR, 'Bundle.minimal.apks')
 _TEST_SSARGS_PATH = os.path.join(_TEST_OUTPUT_DIR, 'test.ssargs')
 
@@ -152,29 +154,29 @@
       apk_file.writestr(
           _TEST_APK_DEX_PATH, IntegrationTest._CreateBlankData(23))
 
-    with zipfile.ZipFile(_TEST_OTHER_SPLIT_APK_PATH, 'w') as other_apk_zip:
-      other_apk_zip.write(_TEST_ALWAYS_INSTALLED_MANIFEST_PATH,
-                          'AndroidManifest.xml')
+    with zipfile.ZipFile(_TEST_NOT_ON_DEMAND_SPLIT_APK_PATH, 'w') as z:
+      z.write(_TEST_ALWAYS_INSTALLED_MANIFEST_PATH, 'AndroidManifest.xml')
+    with zipfile.ZipFile(_TEST_ON_DEMAND_SPLIT_APK_PATH, 'w') as z:
+      z.write(_TEST_ON_DEMAND_MANIFEST_PATH, 'AndroidManifest.xml')
 
     with zipfile.ZipFile(_TEST_MINIMAL_APKS_PATH, 'w') as apk_file:
+      apk_file.writestr('toc.pb', 'x' * 80)
       apk_file.write(_TEST_APK_PATH, 'splits/base-master.apk')
       apk_file.writestr('splits/base-en.apk', 'x' * 10)
-
-      with tempfile.NamedTemporaryFile(suffix='.apk') as vr_apk_file:
-        with zipfile.ZipFile(vr_apk_file.name, 'w') as vr_apk_zip:
-          vr_apk_zip.write(_TEST_ON_DEMAND_MANIFEST_PATH, 'AndroidManifest.xml')
-        apk_file.write(vr_apk_file.name, 'splits/vr-master.apk')
+      apk_file.write(_TEST_NOT_ON_DEMAND_SPLIT_APK_PATH,
+                     'splits/not_on_demand-master.apk')
+      apk_file.write(_TEST_ON_DEMAND_SPLIT_APK_PATH,
+                     'splits/on_demand-master.apk')
       apk_file.writestr('splits/vr-en.apk', 'x' * 40)
 
-      apk_file.write(_TEST_OTHER_SPLIT_APK_PATH, 'splits/other-master.apk')
-      apk_file.writestr('toc.pb', 'x' * 80)
 
   @classmethod
   def tearDownClass(cls):
     IntegrationTest._SafeRemoveFiles([
         _TEST_ELF_PATH,
         _TEST_APK_PATH,
-        _TEST_OTHER_SPLIT_APK_PATH,
+        _TEST_NOT_ON_DEMAND_SPLIT_APK_PATH,
+        _TEST_ON_DEMAND_SPLIT_APK_PATH,
         _TEST_MINIMAL_APKS_PATH,
     ])
 
@@ -262,20 +264,35 @@
         raw_symbols_list.append(raw_symbols)
         if use_minimal_apks:
           opts.analyze_native = False
-          args.split_name = 'other'
-          args.apk_file = _TEST_OTHER_SPLIT_APK_PATH
+          args.split_name = 'not_on_demand'
+          args.apk_file = _TEST_NOT_ON_DEMAND_SPLIT_APK_PATH
           args.elf_file = None
           args.map_file = None
           metadata = archive.CreateMetadata(args, None, build_config)
           container, raw_symbols = archive.CreateContainerAndSymbols(
               knobs=knobs,
               opts=opts,
-              container_name='{}/other.apk'.format(container_name),
+              container_name='{}/not_on_demand.apk'.format(container_name),
               metadata=metadata,
               tool_prefix=args.tool_prefix,
               output_directory=args.output_directory,
               source_directory=args.source_directory,
-              apk_path=_TEST_OTHER_SPLIT_APK_PATH,
+              apk_path=_TEST_NOT_ON_DEMAND_SPLIT_APK_PATH,
+              size_info_prefix=size_info_prefix)
+          container_list.append(container)
+          raw_symbols_list.append(raw_symbols)
+          args.split_name = 'on_demand'
+          args.apk_file = _TEST_ON_DEMAND_SPLIT_APK_PATH
+          metadata = archive.CreateMetadata(args, None, build_config)
+          container, raw_symbols = archive.CreateContainerAndSymbols(
+              knobs=knobs,
+              opts=opts,
+              container_name='{}/on_demand.apk'.format(container_name),
+              metadata=metadata,
+              tool_prefix=args.tool_prefix,
+              output_directory=args.output_directory,
+              source_directory=args.source_directory,
+              apk_path=_TEST_ON_DEMAND_SPLIT_APK_PATH,
               size_info_prefix=size_info_prefix)
           container_list.append(container)
           raw_symbols_list.append(raw_symbols)
diff --git a/tools/binary_size/libsupersize/models.py b/tools/binary_size/libsupersize/models.py
index c5674994..b74218cf 100644
--- a/tools/binary_size/libsupersize/models.py
+++ b/tools/binary_size/libsupersize/models.py
@@ -55,6 +55,7 @@
 METADATA_APK_FILENAME = 'apk_file_name'  # Path relative to output_directory.
 METADATA_APK_SIZE = 'apk_size'  # File size of apk in bytes.
 METADATA_APK_SPLIT_NAME = 'apk_split_name'  # Name of the split if applicable.
+METADATA_APK_SPLIT_ON_DEMAND = 'apk_split_on_demand'  # Split is onDemand.
 METADATA_ZIPALIGN_OVERHEAD = 'zipalign_padding'  # Overhead from zipalign.
 METADATA_SIGNING_BLOCK_SIZE = 'apk_signature_block_size'  # Size in bytes.
 METADATA_MAP_FILENAME = 'map_file_name'  # Path relative to output_directory.
@@ -997,6 +998,13 @@
   def WherePssBiggerThan(self, min_pss):
     return self.Filter(lambda s: s.pss >= min_pss)
 
+  def WhereIsOnDemand(self, value=True):
+    ret = self.Filter(lambda s: not s.container or s.container.metadata.get(
+        METADATA_APK_SPLIT_ON_DEMAND))
+    if not value:
+      ret = ret.Inverted()
+    return ret
+
   def WhereInSection(self, section, container=None):
     """|section| can be section_name ('.bss'), or section chars ('bdr')."""
     if section.startswith('.'):
diff --git a/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden b/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden
index def1dba1d..292596c 100644
--- a/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden
+++ b/tools/binary_size/libsupersize/testdata/Archive_MinimalApks.golden
@@ -4,8 +4,8 @@
 tool_prefix=tools/binary_size/libsupersize/testdata/mock_toolchain/
 apk_file_name=Bundle.minimal.apks
 apk_signature_block_size=0
-apk_size-other=696
-apk_size-vr=695
+apk_size-not_on_demand=696
+apk_size-on_demand=695
 apk_size=147858911
 elf_arch=arm
 elf_build_id=WhatAnAmazingBuildId
@@ -17,7 +17,14 @@
 apk_file_name=Bundle.minimal.apks
 apk_signature_block_size=0
 apk_size=696
-apk_split_name=other
+apk_split_name=not_on_demand
+apk_split_on_demand=False
+zipalign_padding=0
+apk_file_name=Bundle.minimal.apks
+apk_signature_block_size=0
+apk_size=695
+apk_split_name=on_demand
+apk_split_on_demand=True
 zipalign_padding=0
 Container <Bundle.minimal.apks/base.apk>
 Section .text: has 100.0% of 35982248 bytes accounted for from 22 symbols. 0 bytes are unaccounted for.
@@ -92,7 +99,7 @@
 * 0 symbols have shared ownership.
 * 1 symbols are from generated sources. Accounts for 4194304 bytes (4.4%).
 
-Container <Bundle.minimal.apks/other.apk>
+Container <Bundle.minimal.apks/not_on_demand.apk>
 Section .text: 0 bytes from 0 symbols.
 * Padding accounts for 0 bytes (0.0%)
 * 0 have source paths. Accounts for 0 bytes (0.0%).
@@ -145,6 +152,60 @@
 * 0 have a component assigned. Accounts for 0 bytes (0.0%).
 * 0 symbols have shared ownership.
 * 1 symbols are from generated sources. Accounts for 560 bytes (80.5%).
+
+Container <Bundle.minimal.apks/on_demand.apk>
+Section .text: 0 bytes from 0 symbols.
+* Padding accounts for 0 bytes (0.0%)
+* 0 have source paths. Accounts for 0 bytes (0.0%).
+* 0 have a component assigned. Accounts for 0 bytes (0.0%).
+* 0 symbols have shared ownership.
+Section .rodata: 0 bytes from 0 symbols.
+* Padding accounts for 0 bytes (0.0%)
+* 0 have source paths. Accounts for 0 bytes (0.0%).
+* 0 have a component assigned. Accounts for 0 bytes (0.0%).
+* 0 string literals exist. Accounts for 0 bytes (0.0%) padding is 0 bytes.
+* 0 symbols have shared ownership.
+Section .data.rel.ro: 0 bytes from 0 symbols.
+* Padding accounts for 0 bytes (0.0%)
+* 0 have source paths. Accounts for 0 bytes (0.0%).
+* 0 have a component assigned. Accounts for 0 bytes (0.0%).
+* 0 symbols have shared ownership.
+Section .data: 0 bytes from 0 symbols.
+* Padding accounts for 0 bytes (0.0%)
+* 0 have source paths. Accounts for 0 bytes (0.0%).
+* 0 have a component assigned. Accounts for 0 bytes (0.0%).
+* 0 symbols have shared ownership.
+Section .bss: 0 bytes from 0 symbols.
+* Padding accounts for 0 bytes (0.0%)
+* 0 have source paths. Accounts for 0 bytes (0.0%).
+* 0 have a component assigned. Accounts for 0 bytes (0.0%).
+* 0 symbols have shared ownership.
+Section .dex: has 0.0% of 0 bytes accounted for from 0 symbols. 0 bytes are unaccounted for.
+* Padding accounts for 0 bytes (0.0%)
+* 0 have source paths. Accounts for 0 bytes (0.0%).
+* 0 have a component assigned. Accounts for 0 bytes (0.0%).
+* 0 symbols have shared ownership.
+Section .dex.method: has 0.0% of 0 bytes accounted for from 0 symbols. 0 bytes are unaccounted for.
+* Padding accounts for 0 bytes (0.0%)
+* 0 have source paths. Accounts for 0 bytes (0.0%).
+* 0 have a component assigned. Accounts for 0 bytes (0.0%).
+* 0 symbols have shared ownership.
+Section .pak.translations: 0 bytes from 0 symbols.
+* Padding accounts for 0 bytes (0.0%)
+* 0 have source paths. Accounts for 0 bytes (0.0%).
+* 0 have a component assigned. Accounts for 0 bytes (0.0%).
+* 0 symbols have shared ownership.
+Section .pak.nontranslated: 0 bytes from 0 symbols.
+* Padding accounts for 0 bytes (0.0%)
+* 0 have source paths. Accounts for 0 bytes (0.0%).
+* 0 have a component assigned. Accounts for 0 bytes (0.0%).
+* 0 symbols have shared ownership.
+Section .other: has 100.0% of 695 bytes accounted for from 2 symbols. 0 bytes are unaccounted for.
+* Padding accounts for 136 bytes (19.6%)
+* 1 have source paths. Accounts for 559 bytes (80.4%).
+* 0 have a component assigned. Accounts for 0 bytes (0.0%).
+* 0 symbols have shared ownership.
+* 1 symbols are from generated sources. Accounts for 559 bytes (80.4%).
 <Bundle.minimal.apks/base.apk>.text@28d900(size_without_padding=16,padding=0,full_name=_GLOBAL__sub_I_page_allocator.cc,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={startup},num_aliases=1,component=Blink>Internal)
 <Bundle.minimal.apks/base.apk>.text@28d910(size_without_padding=56,padding=0,full_name=_GLOBAL__sub_I_bbr_sender.cc,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={startup},num_aliases=1,component=Blink>Internal)
 <Bundle.minimal.apks/base.apk>.text@28d948(size_without_padding=28,padding=0,full_name=_GLOBAL__sub_I_pacing_sender.cc,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={startup},num_aliases=1,component=Blink>Internal)
@@ -641,5 +702,7 @@
 <Bundle.minimal.apks/base.apk>.other@0(size_without_padding=17166112,padding=0,full_name=** ELF Section: .symtab,object_path=,source_path=,flags={},num_aliases=1,component=)
 <Bundle.minimal.apks/base.apk>.other@0(size_without_padding=0,padding=764,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1,component=)
 <Bundle.minimal.apks/base.apk>.other@0(size_without_padding=0,padding=33902635,full_name=Overhead: ELF file,object_path=,source_path=,flags={},num_aliases=1,component=)
-<Bundle.minimal.apks/other.apk>.other@0(size_without_padding=560,padding=0,full_name=AndroidManifest.xml,object_path=,source_path=$APK/AndroidManifest.xml,flags={gen},num_aliases=1,component=)
-<Bundle.minimal.apks/other.apk>.other@0(size_without_padding=0,padding=136,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1,component=)
+<Bundle.minimal.apks/not_on_demand.apk>.other@0(size_without_padding=560,padding=0,full_name=AndroidManifest.xml,object_path=,source_path=$APK/AndroidManifest.xml,flags={gen},num_aliases=1,component=)
+<Bundle.minimal.apks/not_on_demand.apk>.other@0(size_without_padding=0,padding=136,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1,component=)
+<Bundle.minimal.apks/on_demand.apk>.other@0(size_without_padding=559,padding=0,full_name=AndroidManifest.xml,object_path=,source_path=$APK/AndroidManifest.xml,flags={gen},num_aliases=1,component=)
+<Bundle.minimal.apks/on_demand.apk>.other@0(size_without_padding=0,padding=136,full_name=Overhead: APK file,object_path=,source_path=,flags={},num_aliases=1,component=)
diff --git a/tools/binary_size/libsupersize/testdata/Console.golden b/tools/binary_size/libsupersize/testdata/Console.golden
index 26f3e06..03a9f2b 100644
--- a/tools/binary_size/libsupersize/testdata/Console.golden
+++ b/tools/binary_size/libsupersize/testdata/Console.golden
@@ -4,7 +4,7 @@
 SizeInfo: ContainerForName, all_section_sizes, build_config, containers, metadata, metadata_legacy, native_symbols, pak_symbols, raw_symbols, size_path, symbols
 Symbol: FlagsString, IsBss, IsDelta, IsDex, IsGeneratedByToolchain, IsGroup, IsNameUnique, IsNative, IsOther, IsOverhead, IsPak, IsStringLiteral, IterLeafSymbols, SetName, address, aliases, component, container, container_name, container_short_name, end_address, flags, full_name, generated_source, is_anonymous, name, num_aliases, object_path, padding, padding_pss, pss, pss_without_padding, section, section_name, size, size_without_padding, source_path, template_name
 
-SymbolGroup (extends Symbol): CountUniqueSymbols, Filter, GroupedBy, GroupedByAliases, GroupedByComponent, GroupedByContainer, GroupedByContainerAndSectionName, GroupedByFullName, GroupedByName, GroupedByPath, GroupedBySectionName, Inverted, IterUniqueSymbols, Sorted, SortedByAddress, SortedByCount, SortedByName, WhereAddressInRange, WhereComponentMatches, WhereFullNameMatches, WhereGeneratedByToolchain, WhereHasAnyAttribution, WhereHasComponent, WhereHasFlag, WhereHasPath, WhereInSection, WhereIsDex, WhereIsGroup, WhereIsNative, WhereIsPak, WhereIsTemplate, WhereMatches, WhereNameMatches, WhereObjectPathMatches, WherePathMatches, WherePssBiggerThan, WhereSizeBiggerThan, WhereSourceIsGenerated, WhereSourcePathMatches, WhereTemplateNameMatches, index, is_default_sorted
+SymbolGroup (extends Symbol): CountUniqueSymbols, Filter, GroupedBy, GroupedByAliases, GroupedByComponent, GroupedByContainer, GroupedByContainerAndSectionName, GroupedByFullName, GroupedByName, GroupedByPath, GroupedBySectionName, Inverted, IterUniqueSymbols, Sorted, SortedByAddress, SortedByCount, SortedByName, WhereAddressInRange, WhereComponentMatches, WhereFullNameMatches, WhereGeneratedByToolchain, WhereHasAnyAttribution, WhereHasComponent, WhereHasFlag, WhereHasPath, WhereInSection, WhereIsDex, WhereIsGroup, WhereIsNative, WhereIsOnDemand, WhereIsPak, WhereIsTemplate, WhereMatches, WhereNameMatches, WhereObjectPathMatches, WherePathMatches, WherePssBiggerThan, WhereSizeBiggerThan, WhereSourceIsGenerated, WhereSourcePathMatches, WhereTemplateNameMatches, index, is_default_sorted
 
 DeltaSizeInfo: ContainerForName, after, all_section_sizes, before, build_config, containers, metadata, native_symbols, pak_symbols, raw_symbols, symbols
 DeltaSymbol (extends Symbol): after_symbol, before_symbol, diff_status
diff --git a/tools/binary_size/trybot_commit_size_checker.py b/tools/binary_size/trybot_commit_size_checker.py
index f47f19c..abd7839 100755
--- a/tools/binary_size/trybot_commit_size_checker.py
+++ b/tools/binary_size/trybot_commit_size_checker.py
@@ -102,6 +102,7 @@
 
 
 def _CreateMethodCountDelta(symbols):
+  symbols = symbols.WhereIsOnDemand(False)
   method_symbols = symbols.WhereInSection(models.SECTION_DEX_METHOD)
   method_lines, net_method_added = _SymbolDiffHelper('Methods', method_symbols)
   class_symbols = symbols.WhereInSection(
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 687760e..6a8f083 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -3073,7 +3073,7 @@
     },
 
     'lacros_on_linux': {
-      'gn_args': 'use_ozone=true ozone_platform_wayland=true ozone_platform="wayland" use_gtk=false chromeos_is_browser_only=true'
+      'gn_args': 'target_os="chromeos" use_ozone=true ozone_platform_wayland=true ozone_platform="wayland" use_gtk=false chromeos_is_browser_only=true'
     },
 
     'also_build_ash_chrome': {
diff --git a/tools/mb/mb_config_expectations/chromium.chromiumos.json b/tools/mb/mb_config_expectations/chromium.chromiumos.json
index 005ee8f..066c3d4 100644
--- a/tools/mb/mb_config_expectations/chromium.chromiumos.json
+++ b/tools/mb/mb_config_expectations/chromium.chromiumos.json
@@ -139,6 +139,7 @@
       "is_debug": false,
       "ozone_platform": "wayland",
       "ozone_platform_wayland": true,
+      "target_os": "chromeos",
       "use_goma": true,
       "use_gtk": false,
       "use_ozone": true
@@ -152,6 +153,7 @@
       "is_debug": false,
       "ozone_platform": "wayland",
       "ozone_platform_wayland": true,
+      "target_os": "chromeos",
       "use_goma": true,
       "use_gtk": false,
       "use_ozone": true
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index 00c7cd3..9f15432 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -623,6 +623,7 @@
       "is_debug": false,
       "ozone_platform": "wayland",
       "ozone_platform_wayland": true,
+      "target_os": "chromeos",
       "use_goma": true,
       "use_gtk": false,
       "use_ozone": true
@@ -636,6 +637,7 @@
       "is_debug": false,
       "ozone_platform": "wayland",
       "ozone_platform_wayland": true,
+      "target_os": "chromeos",
       "use_goma": true,
       "use_gtk": false,
       "use_ozone": true
diff --git a/tools/mb/mb_config_expectations/chromium.infra.codesearch.json b/tools/mb/mb_config_expectations/chromium.infra.codesearch.json
index f5f71f5..bf658df 100644
--- a/tools/mb/mb_config_expectations/chromium.infra.codesearch.json
+++ b/tools/mb/mb_config_expectations/chromium.infra.codesearch.json
@@ -44,6 +44,7 @@
       "ozone_platform": "wayland",
       "ozone_platform_wayland": true,
       "symbol_level": 1,
+      "target_os": "chromeos",
       "use_goma": true,
       "use_gtk": false,
       "use_ozone": true
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json b/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json
index 34d1cfa9..03b88a2 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json
@@ -160,6 +160,7 @@
       "ozone_platform": "wayland",
       "ozone_platform_wayland": true,
       "symbol_level": 1,
+      "target_os": "chromeos",
       "use_goma": true,
       "use_gtk": false,
       "use_ozone": true
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.codesearch.json b/tools/mb/mb_config_expectations/tryserver.chromium.codesearch.json
index c54105a..8f4f5131 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.codesearch.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.codesearch.json
@@ -44,6 +44,7 @@
       "ozone_platform": "wayland",
       "ozone_platform_wayland": true,
       "symbol_level": 1,
+      "target_os": "chromeos",
       "use_goma": true,
       "use_gtk": false,
       "use_ozone": true
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
index 3ef0189..2ee6e838 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
@@ -449,6 +449,7 @@
       "ozone_platform": "wayland",
       "ozone_platform_wayland": true,
       "symbol_level": 1,
+      "target_os": "chromeos",
       "use_goma": true,
       "use_gtk": false,
       "use_ozone": true
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index c8ffb380..ccf53ebc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -13296,6 +13296,14 @@
   <int value="2" label="External non-200 HTTP error"/>
 </enum>
 
+<enum name="ConversionStorageSqlInitStatus">
+  <int value="0" label="Success"/>
+  <int value="1" label="Failed to open DB in memory"/>
+  <int value="2" label="Failed to open DB file"/>
+  <int value="3" label="Failed to create directory"/>
+  <int value="4" label="Failed to initialize schema"/>
+</enum>
+
 <enum name="CookieCommitProblem">
   <int value="0" label="Entry encryption failed"/>
   <int value="1" label="Adding cookie to DB failed."/>
diff --git a/tools/metrics/histograms/histograms_xml/ash/histograms.xml b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
index 18b05b8a..29fa9b2 100644
--- a/tools/metrics/histograms/histograms_xml/ash/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
@@ -950,7 +950,7 @@
 </histogram>
 
 <histogram name="Ash.DragWindowFromShelf.PresentationTime" units="ms"
-    expires_after="2021-02-03">
+    expires_after="2021-12-31">
   <owner>tbarzic@chromium.org</owner>
   <owner>xdai@chromium.org</owner>
   <summary>
@@ -960,7 +960,7 @@
 </histogram>
 
 <histogram name="Ash.DragWindowFromShelf.PresentationTime.MaxLatency"
-    units="ms" expires_after="2021-06-20">
+    units="ms" expires_after="2021-12-31">
   <owner>tbarzic@chromium.org</owner>
   <owner>xdai@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/notifications/histograms.xml b/tools/metrics/histograms/histograms_xml/notifications/histograms.xml
index 852027b0..5085a4c 100644
--- a/tools/metrics/histograms/histograms_xml/notifications/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/notifications/histograms.xml
@@ -263,6 +263,18 @@
   </summary>
 </histogram>
 
+<histogram name="Notifications.Database.ExpiredNotificationCount"
+    units="notifications" expires_after="M94">
+  <owner>knollr@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <summary>
+    Records the number of expired notifications that were stored in the database
+    and therefore got removed and closed on the system. Logged once at startup
+    and every time we synchronize notifications after that (e.g. to schedule the
+    next notification trigger event).
+  </summary>
+</histogram>
+
 <histogram name="Notifications.Database.OldestNotificationTimeInMinutes"
     units="minutes" expires_after="2021-06-20">
   <owner>knollr@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 25963bd..7e977e983 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -3277,6 +3277,16 @@
   </summary>
 </histogram>
 
+<histogram name="Conversions.Storage.Sql.InitStatus"
+    enum="ConversionStorageSqlInitStatus" expires_after="2021-12-12">
+  <owner>johnidel@chromium.org</owner>
+  <owner>csharrison@chromium.org</owner>
+  <summary>
+    Records initialisation statuses of ConversionStorageSql. Note: currently
+    ConversionStorageSql is initialised lazily.
+  </summary>
+</histogram>
+
 <histogram name="Conversions.TimeFromConversionToReportSend" units="hours"
     expires_after="M95">
   <owner>johnidel@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index b813364..c8781ad0 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -2,15 +2,15 @@
     "trace_processor_shell": {
         "win": {
             "hash": "15f9e69440eee271420ffe8cb70f04779163c246",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/a74aaf03d7c1d247452bb92b166cd432190e7eaf/trace_processor_shell.exe"
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/4825074456917de8843b42e96abea018a7237b75/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "00cb959b2ba9ee787a9088e969bc7f7786ddf050",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/a74aaf03d7c1d247452bb92b166cd432190e7eaf/trace_processor_shell"
+            "hash": "f2a7837b9050229eaba591e9407581c2295c6314",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/91cc5be54402afc43605c15f07548e53741e0430/trace_processor_shell"
         },
         "linux": {
             "hash": "23a050cd7812d362911768fbe6825a7ce145888e",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/4825074456917de8843b42e96abea018a7237b75/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/91cc5be54402afc43605c15f07548e53741e0430/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/models/menu_model.cc b/ui/base/models/menu_model.cc
index 3207075..d73c88e 100644
--- a/ui/base/models/menu_model.cc
+++ b/ui/base/models/menu_model.cc
@@ -68,6 +68,10 @@
   return ImageModel();
 }
 
+bool MenuModel::MayHaveMnemonicsAt(int index) const {
+  return true;
+}
+
 const gfx::FontList* MenuModel::GetLabelFontListAt(int index) const {
   return nullptr;
 }
diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h
index c746b37..bf7988c 100644
--- a/ui/base/models/menu_model.h
+++ b/ui/base/models/menu_model.h
@@ -84,6 +84,10 @@
   // updated each time the menu is shown.
   virtual bool IsItemDynamicAt(int index) const = 0;
 
+  // Returns whether the menu item at the specified index has a user-set title,
+  // in which case it should always be treated as plain text.
+  virtual bool MayHaveMnemonicsAt(int index) const;
+
   // Returns the font list used for the label at the specified index.
   // If NULL, then the default font list should be used.
   virtual const gfx::FontList* GetLabelFontListAt(int index) const;
diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc
index 0c37f6e2..7db4ef9 100644
--- a/ui/base/models/simple_menu_model.cc
+++ b/ui/base/models/simple_menu_model.cc
@@ -325,6 +325,11 @@
   items_[ValidateItemIndex(index)].is_new_feature = is_new_feature;
 }
 
+void SimpleMenuModel::SetMayHaveMnemonicsAt(int index,
+                                            bool may_have_mnemonics) {
+  items_[ValidateItemIndex(index)].may_have_mnemonics = may_have_mnemonics;
+}
+
 void SimpleMenuModel::Clear() {
   items_.clear();
   MenuItemsChanged();
@@ -452,6 +457,10 @@
   return items_[ValidateItemIndex(index)].is_new_feature;
 }
 
+bool SimpleMenuModel::MayHaveMnemonicsAt(int index) const {
+  return items_[ValidateItemIndex(index)].may_have_mnemonics;
+}
+
 void SimpleMenuModel::ActivatedAt(int index) {
   ActivatedAt(index, 0);
 }
diff --git a/ui/base/models/simple_menu_model.h b/ui/base/models/simple_menu_model.h
index 3166989..7cefe1d 100644
--- a/ui/base/models/simple_menu_model.h
+++ b/ui/base/models/simple_menu_model.h
@@ -169,6 +169,9 @@
   // Sets whether the item at |index| is new.
   void SetIsNewFeatureAt(int index, bool is_new_feature);
 
+  // Sets whether the item at |index| is may have mnemonics.
+  void SetMayHaveMnemonicsAt(int index, bool may_have_mnemonics);
+
   // Clears all items. Note that it does not free MenuModel of submenu.
   void Clear();
 
@@ -195,6 +198,7 @@
   bool IsVisibleAt(int index) const override;
   bool IsAlertedAt(int index) const override;
   bool IsNewFeatureAt(int index) const override;
+  bool MayHaveMnemonicsAt(int index) const override;
   void ActivatedAt(int index) override;
   void ActivatedAt(int index, int event_flags) override;
   MenuModel* GetSubmenuModelAt(int index) const override;
@@ -229,6 +233,7 @@
     bool enabled = true;
     bool visible = true;
     bool is_new_feature = false;
+    bool may_have_mnemonics = true;
   };
 
   typedef std::vector<Item> ItemVector;
diff --git a/ui/gfx/geometry/axis_transform2d.h b/ui/gfx/geometry/axis_transform2d.h
index 6aec0e5..57b45ac 100644
--- a/ui/gfx/geometry/axis_transform2d.h
+++ b/ui/gfx/geometry/axis_transform2d.h
@@ -84,21 +84,21 @@
   Vector2dF translation_;
 };
 
-static inline AxisTransform2d PreScaleAxisTransform2d(const AxisTransform2d& t,
-                                                      float scale) {
+inline AxisTransform2d PreScaleAxisTransform2d(const AxisTransform2d& t,
+                                               float scale) {
   AxisTransform2d result(t);
   result.PreScale(scale);
   return result;
 }
 
-static inline AxisTransform2d PostScaleAxisTransform2d(const AxisTransform2d& t,
-                                                       float scale) {
+inline AxisTransform2d PostScaleAxisTransform2d(const AxisTransform2d& t,
+                                                float scale) {
   AxisTransform2d result(t);
   result.PostScale(scale);
   return result;
 }
 
-static inline AxisTransform2d PreTranslateAxisTransform2d(
+inline AxisTransform2d PreTranslateAxisTransform2d(
     const AxisTransform2d& t,
     const Vector2dF& translation) {
   AxisTransform2d result(t);
@@ -106,7 +106,7 @@
   return result;
 }
 
-static inline AxisTransform2d PostTranslateAxisTransform2d(
+inline AxisTransform2d PostTranslateAxisTransform2d(
     const AxisTransform2d& t,
     const Vector2dF& translation) {
   AxisTransform2d result(t);
@@ -114,15 +114,14 @@
   return result;
 }
 
-static inline AxisTransform2d ConcatAxisTransform2d(
-    const AxisTransform2d& post,
-    const AxisTransform2d& pre) {
+inline AxisTransform2d ConcatAxisTransform2d(const AxisTransform2d& post,
+                                             const AxisTransform2d& pre) {
   AxisTransform2d result(post);
   result.PreConcat(pre);
   return result;
 }
 
-static inline AxisTransform2d InvertAxisTransform2d(const AxisTransform2d& t) {
+inline AxisTransform2d InvertAxisTransform2d(const AxisTransform2d& t) {
   AxisTransform2d result = t;
   result.Invert();
   return result;
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc
index 79d2719..06199d4d 100644
--- a/ui/gtk/gtk_ui.cc
+++ b/ui/gtk/gtk_ui.cc
@@ -1041,9 +1041,9 @@
   if (resolution > 0)
     scale *= resolution / kDefaultDPI;
 
-  // Round to 2 decimal places to address problems with some Linux desktop
-  // environments that result in scales of 1.002... and the like.
-  scale = roundf(scale * 100.0) / 100;
+  // Round to the nearest 64th so that UI can losslessly multiply and divide
+  // the scale factor.
+  scale = roundf(scale * 64) / 64;
 
   return scale;
 }
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 3c532ce..5928be5 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -52,6 +52,10 @@
 #include "ui/views/views_features.h"
 #include "ui/views/widget/widget.h"
 
+#if defined(OS_APPLE)
+#include "ui/views/accessibility/view_accessibility.h"
+#endif  //  defined(OS_APPLE)
+
 namespace views {
 
 namespace {
@@ -182,6 +186,8 @@
   switch (type_) {
     case Type::kSubMenu:
     case Type::kActionableSubMenu:
+      // Note: This is neither necessary nor sufficient for macOS. See
+      // CreateSubmenu() for virtual child creation and explanation.
       node_data->SetHasPopup(ax::mojom::HasPopup::kMenu);
       break;
     case Type::kCheckbox:
@@ -378,6 +384,18 @@
   if (!submenu_) {
     submenu_ = new SubmenuView(this);
 
+#if defined(OS_APPLE)
+    // All MenuItemViews of Type kSubMenu have a respective SubmenuView.
+    // However, in the Views hierarchy, this SubmenuView is not a child of the
+    // MenuItemView. This confuses VoiceOver, because it expects the submenu
+    // itself to be a child of the menu item. To allow VoiceOver to recognize
+    // submenu items, we create a virtual child of type Menu.
+    std::unique_ptr<AXVirtualView> virtual_child =
+        std::make_unique<AXVirtualView>();
+    virtual_child->GetCustomData().role = ax::mojom::Role::kMenu;
+    GetViewAccessibility().AddVirtualChildView(std::move(virtual_child));
+#endif  //  defined(OS_APPLE)
+
     // Initialize the submenu indicator icon (arrow).
     submenu_arrow_image_view_ = AddChildView(std::make_unique<ImageView>());
   }
@@ -567,7 +585,7 @@
 
 base::char16 MenuItemView::GetMnemonic() {
   if (!GetRootMenuItem()->has_mnemonics_ ||
-      !MenuConfig::instance().use_mnemonics) {
+      !MenuConfig::instance().use_mnemonics || !may_have_mnemonics()) {
     return 0;
   }
 
@@ -871,7 +889,7 @@
   else
     flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
 
-  if (GetRootMenuItem()->has_mnemonics_) {
+  if (GetRootMenuItem()->has_mnemonics_ && may_have_mnemonics()) {
     if (MenuConfig::instance().show_mnemonics ||
         GetRootMenuItem()->show_mnemonics_) {
       flags |= gfx::Canvas::SHOW_PREFIX;
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h
index 5408f114..3c07176 100644
--- a/ui/views/controls/menu/menu_item_view.h
+++ b/ui/views/controls/menu/menu_item_view.h
@@ -279,6 +279,11 @@
   void set_is_new(bool is_new) { is_new_ = is_new; }
   bool is_new() const { return is_new_; }
 
+  void set_may_have_mnemonics(bool may_have_mnemonics) {
+    may_have_mnemonics_ = may_have_mnemonics;
+  }
+  bool may_have_mnemonics() const { return may_have_mnemonics_; }
+
   // Paints the menu item.
   void OnPaint(gfx::Canvas* canvas) override;
 
@@ -532,6 +537,9 @@
   // a way to highlight a new feature for users.
   bool is_new_ = false;
 
+  // Whether the menu item contains user-created text.
+  bool may_have_mnemonics_ = true;
+
   // Submenu, created via CreateSubmenu.
   SubmenuView* submenu_ = nullptr;
 
diff --git a/ui/views/controls/menu/menu_model_adapter.cc b/ui/views/controls/menu/menu_model_adapter.cc
index b0b1cf9..5dd4a9b 100644
--- a/ui/views/controls/menu/menu_model_adapter.cc
+++ b/ui/views/controls/menu/menu_model_adapter.cc
@@ -122,6 +122,8 @@
   if (model->IsAlertedAt(model_index))
     menu_item_view->SetAlerted();
   menu_item_view->set_is_new(model->IsNewFeatureAt(model_index));
+  menu_item_view->set_may_have_mnemonics(
+      model->MayHaveMnemonicsAt(model_index));
 
   return menu_item_view;
 }
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc
index 98a32c4..1438982 100644
--- a/ui/views/window/dialog_client_view.cc
+++ b/ui/views/window/dialog_client_view.cc
@@ -160,7 +160,9 @@
 bool DialogClientView::AcceleratorPressed(const ui::Accelerator& accelerator) {
   DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE);
 
-  GetWidget()->CloseWithReason(Widget::ClosedReason::kEscKeyPressed);
+  if (DialogDelegate* delegate = GetDialogDelegate())
+    delegate->CancelDialog();
+
   return true;
 }
 
diff --git a/ui/views/window/dialog_delegate_unittest.cc b/ui/views/window/dialog_delegate_unittest.cc
index 426cfde..d054cb2 100644
--- a/ui/views/window/dialog_delegate_unittest.cc
+++ b/ui/views/window/dialog_delegate_unittest.cc
@@ -162,10 +162,10 @@
   EXPECT_TRUE(accepted_);
 }
 
-TEST_F(DialogTest, EscButtonCloses) {
-  EXPECT_FALSE(closed_);
+TEST_F(DialogTest, EscButtonCancels) {
+  EXPECT_FALSE(cancelled_);
   SimulateKeyPress(ui::VKEY_ESCAPE);
-  EXPECT_TRUE(closed_);
+  EXPECT_TRUE(cancelled_);
 }
 
 TEST_F(DialogTest, ReturnDirectedToOkButtonPlatformStyle) {
diff --git a/ui/webui/resources/css/BUILD.gn b/ui/webui/resources/css/BUILD.gn
index 22cdf83d..b35d51f 100644
--- a/ui/webui/resources/css/BUILD.gn
+++ b/ui/webui/resources/css/BUILD.gn
@@ -26,6 +26,7 @@
     "action_link.css",
     "apps/common.css",
     "apps/topbutton_bar.css",
+    "bubble_button.css",
     "bubble.css",
     "butter_bar.css",
     "chrome_shared.css",
diff --git a/chrome/browser/resources/chromeos/arc_support/bubble_button.css b/ui/webui/resources/css/bubble_button.css
similarity index 100%
rename from chrome/browser/resources/chromeos/arc_support/bubble_button.css
rename to ui/webui/resources/css/bubble_button.css
diff --git a/ui/webui/resources/js/cr/ui/BUILD.gn b/ui/webui/resources/js/cr/ui/BUILD.gn
index e7bd3bf..7825de6 100644
--- a/ui/webui/resources/js/cr/ui/BUILD.gn
+++ b/ui/webui/resources/js/cr/ui/BUILD.gn
@@ -37,6 +37,7 @@
   out_manifest = "$target_gen_dir/$preprocess_src_manifest"
   in_files = [
     "array_data_model.js",
+    "bubble_button.js",
     "bubble.js",
     "command.js",
     "context_menu_handler.js",
diff --git a/chrome/browser/resources/chromeos/arc_support/bubble_button.js b/ui/webui/resources/js/cr/ui/bubble_button.js
similarity index 100%
rename from chrome/browser/resources/chromeos/arc_support/bubble_button.js
rename to ui/webui/resources/js/cr/ui/bubble_button.js
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index 366d882a..e387534 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -279,8 +279,6 @@
     "browser/url_bar/page_info_delegate_impl.h",
     "browser/url_bar/url_bar_controller_impl.cc",
     "browser/url_bar/url_bar_controller_impl.h",
-    "browser/user_agent.cc",
-    "browser/user_agent.h",
     "browser/web_contents_view_delegate_impl.cc",
     "browser/web_contents_view_delegate_impl.h",
     "browser/weblayer_browser_interface_binders.cc",
@@ -388,6 +386,7 @@
     "//components/download/content/factory",
     "//components/download/content/public",
     "//components/embedder_support",
+    "//components/embedder_support:browser_util",
     "//components/embedder_support/origin_trials",
     "//components/error_page/common",
     "//components/error_page/content/browser",
diff --git a/weblayer/README.md b/weblayer/README.md
index ca653b1c..87432f79 100644
--- a/weblayer/README.md
+++ b/weblayer/README.md
@@ -123,6 +123,26 @@
 Passing in `-vvvv` may be useful if you want to see loads of information about
 test execution.
 
+A list of known test failures is in [`WeblayerWPTExpectations`](https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/web_tests/android/WeblayerWPTExpectations).
+The values between the brackets at the end of each line list the expected
+result types that test can have. For example, a test marked as "[ Failure ]" is
+expected to fail, while a test marked as "[ Failure Pass ]" is expected to be
+flaky.
+
+Any failing tests should be removed from `WeblayerWPTExpectations` file once
+fixed.
+
+### Tips
+
+While many WPT tests fail due to features not being implemented in WebLayer,
+some may fail due to Features that aren't getting enabled or switches that
+aren't getting passed to the test as they would be for Clank. If a test is
+failing due to a missing Feature, check the test FieldTrial configuration
+in [`fieldtrial_testing_config.json`](https://source.chromium.org/chromium/chromium/src/+/master:testing/variations/fieldtrial_testing_config.json).
+A missing switch could have several causes, but the flags that get passed
+to the test originate from [`third_party/wpt_tools/wpt/tools/wpt/run.py`](https://source.chromium.org/chromium/chromium/src/+/master:third_party/wpt_tools/wpt/tools/wpt/run.py).
+
+
 ## Telemetry
 
 Telemetry is run against WebLayer, currently on the bot
diff --git a/weblayer/browser/browser_process.cc b/weblayer/browser/browser_process.cc
index dc94fa2..d161789b 100644
--- a/weblayer/browser/browser_process.cc
+++ b/weblayer/browser/browser_process.cc
@@ -9,6 +9,7 @@
 #include "base/path_service.h"
 #include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/network_time/network_time_tracker.h"
 #include "components/prefs/pref_service.h"
 #include "components/subresource_filter/content/browser/ruleset_service.h"
@@ -16,7 +17,6 @@
 #include "content/public/browser/network_service_instance.h"
 #include "services/network/public/cpp/network_quality_tracker.h"
 #include "weblayer/browser/system_network_context_manager.h"
-#include "weblayer/browser/user_agent.h"
 #include "weblayer/common/weblayer_paths.h"
 
 #if defined(OS_ANDROID)
@@ -126,7 +126,7 @@
     // Create and initialize safe_browsing_service on first get.
     // Note: Initialize() needs to happen on UI thread.
     safe_browsing_service_ =
-        std::make_unique<SafeBrowsingService>(GetUserAgent());
+        std::make_unique<SafeBrowsingService>(embedder_support::GetUserAgent());
     safe_browsing_service_->Initialize();
   }
   return safe_browsing_service_.get();
diff --git a/weblayer/browser/client_hints_factory.cc b/weblayer/browser/client_hints_factory.cc
index 2060d148..a3ac815 100644
--- a/weblayer/browser/client_hints_factory.cc
+++ b/weblayer/browser/client_hints_factory.cc
@@ -5,10 +5,10 @@
 #include "weblayer/browser/client_hints_factory.h"
 
 #include "components/client_hints/browser/client_hints.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/prefs/pref_service.h"
 #include "weblayer/browser/browser_process.h"
-#include "weblayer/browser/content_browser_client_impl.h"
 #include "weblayer/browser/host_content_settings_map_factory.h"
 
 class PrefService;
@@ -43,7 +43,7 @@
   return new client_hints::ClientHints(
       context, BrowserProcess::GetInstance()->GetNetworkQualityTracker(),
       HostContentSettingsMapFactory::GetForBrowserContext(context),
-      GetUserAgentMetadata(), local_state);
+      embedder_support::GetUserAgentMetadata(), local_state);
 }
 
 content::BrowserContext* ClientHintsFactory::GetBrowserContextToUse(
diff --git a/weblayer/browser/content_browser_client_impl.cc b/weblayer/browser/content_browser_client_impl.cc
index e0dc2f8..fba8aa1 100644
--- a/weblayer/browser/content_browser_client_impl.cc
+++ b/weblayer/browser/content_browser_client_impl.cc
@@ -21,6 +21,7 @@
 #include "components/blocked_content/popup_blocker.h"
 #include "components/captive_portal/core/buildflags.h"
 #include "components/embedder_support/switches.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/error_page/content/browser/net_error_auto_reloader.h"
 #include "components/network_time/network_time_tracker.h"
 #include "components/no_state_prefetch/browser/no_state_prefetch_manager.h"
@@ -92,7 +93,6 @@
 #include "weblayer/browser/signin_url_loader_throttle.h"
 #include "weblayer/browser/system_network_context_manager.h"
 #include "weblayer/browser/tab_impl.h"
-#include "weblayer/browser/user_agent.h"
 #include "weblayer/browser/web_contents_view_delegate_impl.h"
 #include "weblayer/browser/weblayer_browser_interface_binders.h"
 #include "weblayer/browser/weblayer_security_blocking_page_factory.h"
@@ -273,8 +273,6 @@
 
 ContentBrowserClientImpl::ContentBrowserClientImpl(MainParams* params)
     : params_(params) {
-  if (!SystemNetworkContextManager::HasInstance())
-    SystemNetworkContextManager::CreateInstance(GetUserAgent());
 }
 
 ContentBrowserClientImpl::~ContentBrowserClientImpl() = default;
@@ -329,15 +327,15 @@
 }
 
 std::string ContentBrowserClientImpl::GetProduct() {
-  return weblayer::GetProduct();
+  return embedder_support::GetProduct();
 }
 
 std::string ContentBrowserClientImpl::GetUserAgent() {
-  return weblayer::GetUserAgent();
+  return embedder_support::GetUserAgent();
 }
 
 blink::UserAgentMetadata ContentBrowserClientImpl::GetUserAgentMetadata() {
-  return weblayer::GetUserAgentMetadata();
+  return embedder_support::GetUserAgentMetadata();
 }
 
 void ContentBrowserClientImpl::OverrideWebkitPrefs(
@@ -388,6 +386,9 @@
 
 void ContentBrowserClientImpl::OnNetworkServiceCreated(
     network::mojom::NetworkService* network_service) {
+  if (!SystemNetworkContextManager::HasInstance())
+    SystemNetworkContextManager::CreateInstance(
+        embedder_support::GetUserAgent());
 // TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
 // complete.
 #if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -839,6 +840,8 @@
   local_state_ = CreateLocalState();
   feature_list_creator_ =
       std::make_unique<FeatureListCreator>(local_state_.get());
+  if (!SystemNetworkContextManager::HasInstance())
+    SystemNetworkContextManager::CreateInstance(GetUserAgent());
   feature_list_creator_->SetSystemNetworkContextManager(
       SystemNetworkContextManager::GetInstance());
   feature_list_creator_->CreateFeatureListAndFieldTrials();
diff --git a/weblayer/browser/content_browser_client_impl.h b/weblayer/browser/content_browser_client_impl.h
index 4f177f6..6255ffc7 100644
--- a/weblayer/browser/content_browser_client_impl.h
+++ b/weblayer/browser/content_browser_client_impl.h
@@ -24,8 +24,6 @@
 class SafeBrowsingService;
 struct MainParams;
 
-blink::UserAgentMetadata GetUserAgentMetadata();
-
 class ContentBrowserClientImpl : public content::ContentBrowserClient {
  public:
   explicit ContentBrowserClientImpl(MainParams* params);
diff --git a/weblayer/browser/profile_impl.cc b/weblayer/browser/profile_impl.cc
index 9f09fdb..a24b858 100644
--- a/weblayer/browser/profile_impl.cc
+++ b/weblayer/browser/profile_impl.cc
@@ -55,7 +55,6 @@
 #include "weblayer/browser/browser_process.h"
 #include "weblayer/browser/java/jni/ProfileImpl_jni.h"
 #include "weblayer/browser/safe_browsing/safe_browsing_service.h"
-#include "weblayer/browser/user_agent.h"
 #endif
 
 #if defined(OS_POSIX)
diff --git a/weblayer/browser/tab_impl.cc b/weblayer/browser/tab_impl.cc
index cfbeedd..d03f6c6 100644
--- a/weblayer/browser/tab_impl.cc
+++ b/weblayer/browser/tab_impl.cc
@@ -24,7 +24,7 @@
 #include "components/blocked_content/popup_tracker.h"
 #include "components/captive_portal/core/buildflags.h"
 #include "components/content_settings/browser/page_specific_content_settings.h"
-#include "components/embedder_support/android/util/user_agent_utils.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/find_in_page/find_tab_helper.h"
 #include "components/find_in_page/find_types.h"
 #include "components/js_injection/browser/js_communication_host.h"
@@ -79,7 +79,6 @@
 #include "weblayer/browser/profile_impl.h"
 #include "weblayer/browser/subresource_filter_client_impl.h"
 #include "weblayer/browser/translate_client_impl.h"
-#include "weblayer/browser/user_agent.h"
 #include "weblayer/browser/weblayer_features.h"
 #include "weblayer/common/isolated_world_ids.h"
 #include "weblayer/public/fullscreen_delegate.h"
@@ -835,8 +834,8 @@
 
   // Reset state that an earlier call to Navigation::SetUserAgentString()
   // could have modified.
-  embedder_support::SetDesktopUserAgentOverride(web_contents_.get(),
-                                                GetUserAgentMetadata());
+  embedder_support::SetDesktopUserAgentOverride(
+      web_contents_.get(), embedder_support::GetUserAgentMetadata());
   web_contents_->SetRendererInitiatedUserAgentOverrideOption(
       content::NavigationController::UA_OVERRIDE_INHERIT);
 
diff --git a/weblayer/browser/user_agent.cc b/weblayer/browser/user_agent.cc
deleted file mode 100644
index 96c288f4..0000000
--- a/weblayer/browser/user_agent.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2020 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 "weblayer/browser/user_agent.h"
-
-#include "base/command_line.h"
-#include "components/version_info/version_info.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/common/user_agent.h"
-#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
-
-namespace weblayer {
-
-std::string GetProduct() {
-  return version_info::GetProductNameAndVersionForUserAgent();
-}
-
-std::string GetUserAgent() {
-  std::string product = GetProduct();
-
-  const base::CommandLine& command_line =
-      *base::CommandLine::ForCurrentProcess();
-
-  // On android, content adds this switch automatically if the right conditions
-  // are met.
-  if (command_line.HasSwitch(switches::kUseMobileUserAgent))
-    product += " Mobile";
-
-  return content::BuildUserAgentFromProduct(product);
-}
-
-blink::UserAgentMetadata GetUserAgentMetadata() {
-  blink::UserAgentMetadata metadata;
-
-  std::string major_version = version_info::GetMajorVersionNumber();
-  metadata.brand_version_list.push_back({"Chromium", major_version});
-
-  // The CHROMIUM_BRANDING build flag is not available in //weblayer so we're
-  // going to assume it's a derivative.
-  metadata.brand_version_list.push_back(
-      {version_info::GetProductName(), major_version});
-
-  metadata.full_version = version_info::GetVersionNumber();
-  metadata.platform = version_info::GetOSType();
-  metadata.architecture = content::BuildCpuInfo();
-  metadata.model = content::BuildModelInfo();
-  metadata.mobile = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kUseMobileUserAgent);
-  return metadata;
-}
-
-}  // namespace weblayer
diff --git a/weblayer/browser/user_agent.h b/weblayer/browser/user_agent.h
deleted file mode 100644
index 58921df..0000000
--- a/weblayer/browser/user_agent.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2020 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 WEBLAYER_BROWSER_USER_AGENT_H_
-#define WEBLAYER_BROWSER_USER_AGENT_H_
-
-#include <string>
-
-namespace blink {
-struct UserAgentMetadata;
-}
-
-namespace weblayer {
-
-// Returns the product used in building the user-agent.
-std::string GetProduct();
-
-std::string GetUserAgent();
-
-blink::UserAgentMetadata GetUserAgentMetadata();
-
-}  // namespace weblayer
-
-#endif  // WEBLAYER_BROWSER_USER_AGENT_H_
diff --git a/weblayer/browser/weblayer_impl_android.cc b/weblayer/browser/weblayer_impl_android.cc
index e6423f3..7f124da 100644
--- a/weblayer/browser/weblayer_impl_android.cc
+++ b/weblayer/browser/weblayer_impl_android.cc
@@ -8,13 +8,13 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "components/crash/core/common/crash_key.h"
+#include "components/embedder_support/user_agent_utils.h"
 #include "components/page_info/android/page_info_client.h"
 #include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h"
 #include "weblayer/browser/default_search_engine.h"
 #include "weblayer/browser/devtools_server_android.h"
 #include "weblayer/browser/java/jni/WebLayerImpl_jni.h"
 #include "weblayer/browser/url_bar/page_info_client_impl.h"
-#include "weblayer/browser/user_agent.h"
 #include "weblayer/common/crash_reporter/crash_keys.h"
 
 using base::android::JavaParamRef;
@@ -40,7 +40,7 @@
 static base::android::ScopedJavaLocalRef<jstring>
 JNI_WebLayerImpl_GetUserAgentString(JNIEnv* env) {
   return base::android::ConvertUTF8ToJavaString(
-      base::android::AttachCurrentThread(), GetUserAgent());
+      base::android::AttachCurrentThread(), embedder_support::GetUserAgent());
 }
 
 static void JNI_WebLayerImpl_RegisterExternalExperimentIDs(