diff --git a/DEPS b/DEPS
index 5d8236a..c00c128 100644
--- a/DEPS
+++ b/DEPS
@@ -235,15 +235,15 @@
   # 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': 'b23372df9ce751f1c2a30c3c0f24099aa21d3c2c',
+  'skia_revision': 'e5a06afeaedd8a6d15c73747f9a9f9d799c9763c',
   # 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': 'b7ea16e261758f75cdcd09a7cfa76235dd5c7337',
+  'v8_revision': '3910e888902506c07ac4f131cb12307723d5fbe3',
   # 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': '9ba5dcf604e7552be79a74a5e36cd73da7f70e4d',
+  'angle_revision': '4349703b4c90633d70655850a0b86dd9eabeebf8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -302,7 +302,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '89d8a1b2c541992b209d84381bdf9eeb6694c372',
+  'catapult_revision': '876bab7910dc34d647002e2c2d649ca04b82d037',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -350,7 +350,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '34206ec07bfcc6a5942402caea0385ddffb25d9b',
+  'dawn_revision': '6fd28ba4cb990953dc97ff22d3fbbd9b19a5f504',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -378,7 +378,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': 'dceb1e216bdd22daf37f71026db3d93124841cd4',
+  'nearby_revision': '51a19d24c1644cccdf33d56a48db4dad3e418447',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -1030,7 +1030,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a0c67a3eab72e9524ef8b90bf2bf838597a4d1bc',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '4a06fb543238f49f674a44be355dde429286fa86',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1695,7 +1695,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f6a31866f14e8d38a9ecd72cac92f12c1ca20e6f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c1e1b2b1e121156dd48689ba26656f61e1335bba',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 4c17dae1..a89a451 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -393,14 +393,15 @@
 }
 
 void AppListControllerImpl::PublishSearchResults(
-    std::vector<std::unique_ptr<SearchResultMetadata>> results) {
+    std::vector<std::unique_ptr<SearchResultMetadata>> results,
+    const std::vector<ash::AppListSearchResultCategory>& categories) {
   std::vector<std::unique_ptr<SearchResult>> new_results;
   for (auto& result_metadata : results) {
     std::unique_ptr<SearchResult> result = std::make_unique<SearchResult>();
     result->SetMetadata(std::move(result_metadata));
     new_results.push_back(std::move(result));
   }
-  search_model_.PublishResults(std::move(new_results));
+  search_model_.PublishResults(std::move(new_results), categories);
 }
 
 void AppListControllerImpl::SetItemMetadata(
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index b58f59c2..b1ce50a 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -112,7 +112,8 @@
   void UpdateSearchBox(const std::u16string& text,
                        bool initiated_by_user) override;
   void PublishSearchResults(
-      std::vector<std::unique_ptr<SearchResultMetadata>> results) override;
+      std::vector<std::unique_ptr<SearchResultMetadata>> results,
+      const std::vector<ash::AppListSearchResultCategory>& categories) override;
   void SetItemMetadata(const std::string& id,
                        std::unique_ptr<AppListItemMetadata> data) override;
   void SetItemIconVersion(const std::string& id, int icon_version) override;
diff --git a/ash/app_list/model/search/search_model.cc b/ash/app_list/model/search/search_model.cc
index ee74a5c..cd970744 100644
--- a/ash/app_list/model/search/search_model.cc
+++ b/ash/app_list/model/search/search_model.cc
@@ -60,7 +60,8 @@
 }
 
 void SearchModel::PublishResults(
-    std::vector<std::unique_ptr<SearchResult>> new_results) {
+    std::vector<std::unique_ptr<SearchResult>> new_results,
+    const std::vector<ash::AppListSearchResultCategory>& categories) {
   // The following algorithm is used:
   // 1. Transform the |results_| list into an unordered map from result ID
   // to item.
@@ -115,7 +116,8 @@
 }
 
 void SearchModel::DeleteAllResults() {
-  PublishResults(std::vector<std::unique_ptr<SearchResult>>());
+  PublishResults(std::vector<std::unique_ptr<SearchResult>>(),
+                 std::vector<ash::AppListSearchResultCategory>());
 }
 
 void SearchModel::DeleteResultById(const std::string& id) {
diff --git a/ash/app_list/model/search/search_model.h b/ash/app_list/model/search/search_model.h
index 133bddc..ebc1bf5f 100644
--- a/ash/app_list/model/search/search_model.h
+++ b/ash/app_list/model/search/search_model.h
@@ -62,7 +62,9 @@
   SearchBoxModel* search_box() { return search_box_.get(); }
   SearchResults* results() { return results_.get(); }
 
-  void PublishResults(std::vector<std::unique_ptr<SearchResult>> new_results);
+  void PublishResults(
+      std::vector<std::unique_ptr<SearchResult>> new_results,
+      const std::vector<ash::AppListSearchResultCategory>& categories);
 
   SearchResult* FindSearchResult(const std::string& id);
 
diff --git a/ash/frame/header_view.cc b/ash/frame/header_view.cc
index 57adb329..e1a68fd 100644
--- a/ash/frame/header_view.cc
+++ b/ash/frame/header_view.cc
@@ -343,10 +343,12 @@
   auto* center_button = frame_header_->GetCenterButton();
   if (!center_button)
     return;
-  if (is_center_button_visible && !center_button->parent()) {
-    AddChildView(center_button);
-  } else if (!is_center_button_visible && center_button->parent()) {
-    RemoveChildView(center_button);
+  if (is_center_button_visible) {
+    if (!center_button->parent())
+      AddChildView(center_button);
+    center_button->SetVisible(true);
+  } else {
+    center_button->SetVisible(false);
   }
 }
 
diff --git a/ash/public/cpp/app_list/app_list_config.cc b/ash/public/cpp/app_list/app_list_config.cc
index 8a3baa5..99c1fd4 100644
--- a/ash/public/cpp/app_list/app_list_config.cc
+++ b/ash/public/cpp/app_list/app_list_config.cc
@@ -203,15 +203,16 @@
 int SharedAppListConfig::GetPreferredIconDimension(
     SearchResultDisplayType display_type) const {
   switch (display_type) {
+    case SearchResultDisplayType::kList:
+      return search_list_icon_dimension_;
     case SearchResultDisplayType::kTile:
       return search_tile_icon_dimension_;
     case SearchResultDisplayType::kChip:
       return suggestion_chip_icon_dimension_;
-    case SearchResultDisplayType::kList:
-      return search_list_icon_dimension_;
+    case SearchResultDisplayType::kContinue:
+      return suggestion_chip_icon_dimension_;
     case SearchResultDisplayType::kNone:  // Falls through.
-    case SearchResultDisplayType::kCard:
-      return 0;
+    case SearchResultDisplayType::kCard:  // Falls through.
     case SearchResultDisplayType::kLast:
       return 0;
   }
diff --git a/ash/public/cpp/app_list/app_list_controller.h b/ash/public/cpp/app_list/app_list_controller.h
index 351a8cb..f8f6122a 100644
--- a/ash/public/cpp/app_list/app_list_controller.h
+++ b/ash/public/cpp/app_list/app_list_controller.h
@@ -67,9 +67,14 @@
   virtual void UpdateSearchBox(const std::u16string& text,
                                bool initiated_by_user) = 0;
 
-  // Publishes search results to Ash to render them.
+  // Publishes search results to Ash to render them. The order of the
+  // |categories| vector is the order cateogories should be displayed in. Each
+  // result in |results| contains a category as a member, which is guaranteed
+  // to exist in |categories|. However, some values in |categories| may have
+  // no results associated with them.
   virtual void PublishSearchResults(
-      std::vector<std::unique_ptr<SearchResultMetadata>> results) = 0;
+      std::vector<std::unique_ptr<SearchResultMetadata>> results,
+      const std::vector<ash::AppListSearchResultCategory>& categories) = 0;
 
   // Updates an item's metadata (e.g. name, position, etc).
   virtual void SetItemMetadata(const std::string& id,
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index d935820..8c9afd5 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -155,6 +155,9 @@
 // Type of the search result, which is set in Chrome. These values are persisted
 // to logs. Entries should not be renumbered and numeric values should never be
 // reused.
+//
+// TODO(crbug.com/1258415): kFileChip and kDriveChip can be deprecated once the
+// new launcher is launched.
 enum class AppListSearchResultType {
   kUnknown,       // Unknown type. Don't use over IPC
   kInstalledApp,  // Installed apps.
@@ -202,13 +205,16 @@
 
 // Which UI container(s) the result should be displayed in.
 // Do not change the order of these as they are used for metrics.
+//
+// TODO(1258415): kChip can be deprecated once the new launcher is launched.
 enum SearchResultDisplayType {
   kNone = 0,
   kList = 1,  // Displays in search list
   kTile = 2,  // Displays in search tiles
   // kRecommendation = 3  // No longer used, split between kTile and kChip
-  kCard = 4,  // Displays in answer cards
-  kChip = 5,  // Displays in suggestion chips
+  kCard = 4,      // Displays in answer cards
+  kChip = 5,      // Displays in suggestion chips
+  kContinue = 6,  // Displays in the Continue section
   // Add new values here
   kLast,  // Don't use over IPC
 };
diff --git a/ash/public/cpp/arc_resize_lock_type.h b/ash/public/cpp/arc_resize_lock_type.h
index d983a454..d539ce9 100644
--- a/ash/public/cpp/arc_resize_lock_type.h
+++ b/ash/public/cpp/arc_resize_lock_type.h
@@ -8,17 +8,20 @@
 namespace ash {
 
 // Represents how strictly resize operations are limited for a window.
+// This class must strictly corresponds to the resize_lock_type enum in the
+// wayland remote surface protocol.
 enum class ArcResizeLockType {
-  // The resizability is not restricted by the resize lock feature.
-  RESIZABLE = 0,
+  // ResizeLock is disabled and the window follows normal resizability.
+  NONE = 0,
 
-  // The app is only allowed to be resized via limited operations such as via
-  // the compatibility mode dialog.
-  RESIZE_LIMITED = 1,
+  // Resizing is enabled and resize lock type is togglable.
+  RESIZE_ENABLED_TOGGLABLE = 1,
 
-  // No explicit resize operation is allowed. (The window can still be resized,
-  // e.g. when the display scale factor changes.)
-  FULLY_LOCKED = 2,
+  // Resizing is disabled and resize lock type is togglable.
+  RESIZE_DISABLED_TOGGLABLE = 2,
+
+  // Resizing is disabled and resize lock type is not togglable.
+  RESIZE_DISABLED_NONTOGGLABLE = 3,
 };
 
 }  // namespace ash
diff --git a/ash/public/cpp/window_properties.cc b/ash/public/cpp/window_properties.cc
index 04b2341..4cd341bf 100644
--- a/ash/public/cpp/window_properties.cc
+++ b/ash/public/cpp/window_properties.cc
@@ -28,7 +28,7 @@
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kArcPackageNameKey, nullptr)
 DEFINE_UI_CLASS_PROPERTY_KEY(ArcResizeLockType,
                              kArcResizeLockTypeKey,
-                             ArcResizeLockType::RESIZABLE)
+                             ArcResizeLockType::NONE)
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(WindowBackdrop, kWindowBackdropKey, nullptr)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kCanAttachToAnotherWindowKey, true)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kCanConsumeSystemKeysKey, false)
diff --git a/ash/system/holding_space/holding_space_item_view.cc b/ash/system/holding_space/holding_space_item_view.cc
index d1a97a6..ed98965 100644
--- a/ash/system/holding_space/holding_space_item_view.cc
+++ b/ash/system/holding_space/holding_space_item_view.cc
@@ -133,6 +133,7 @@
 
   // Accessibility.
   GetViewAccessibility().OverrideName(item->GetAccessibleName());
+  GetViewAccessibility().OverrideDescription(base::EmptyString16());
   GetViewAccessibility().OverrideRole(ax::mojom::Role::kListItem);
 
   // Layer.
diff --git a/ash/webui/telemetry_extension_ui/mojom/probe_service.mojom b/ash/webui/telemetry_extension_ui/mojom/probe_service.mojom
index 0b712ea..d87c4b2 100644
--- a/ash/webui/telemetry_extension_ui/mojom/probe_service.mojom
+++ b/ash/webui/telemetry_extension_ui/mojom/probe_service.mojom
@@ -88,6 +88,8 @@
 // probing telemetry information.
 [Extensible]
 enum ErrorType {
+  // Default value.
+  kUnknown,
   // An error reading a system file.
   kFileReadError,
   // An error parsing data into a consumable form.
diff --git a/ash/webui/telemetry_extension_ui/resources/trusted.js b/ash/webui/telemetry_extension_ui/resources/trusted.js
index fe8d580..4054b64 100644
--- a/ash/webui/telemetry_extension_ui/resources/trusted.js
+++ b/ash/webui/telemetry_extension_ui/resources/trusted.js
@@ -618,6 +618,7 @@
      * @const
      */
     this.errorTypeToString_ = new Map([
+      [errorEnum.kUnknown, 'unknown-error'],
       [errorEnum.kFileReadError, 'file-read-error'],
       [errorEnum.kParseError, 'parse-error'],
       [errorEnum.kSystemUtilityError, 'system-utility-error'],
diff --git a/ash/webui/telemetry_extension_ui/services/probe_service_converters.cc b/ash/webui/telemetry_extension_ui/services/probe_service_converters.cc
index 93d2fe7d..99ea99e 100644
--- a/ash/webui/telemetry_extension_ui/services/probe_service_converters.cc
+++ b/ash/webui/telemetry_extension_ui/services/probe_service_converters.cc
@@ -333,6 +333,8 @@
 
 health::mojom::ErrorType Convert(cros_healthd::mojom::ErrorType input) {
   switch (input) {
+    case cros_healthd::mojom::ErrorType::kUnknown:
+      return health::mojom::ErrorType::kUnknown;
     case cros_healthd::mojom::ErrorType::kFileReadError:
       return health::mojom::ErrorType::kFileReadError;
     case cros_healthd::mojom::ErrorType::kParseError:
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 6c66c072..34a637f 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3546,7 +3546,6 @@
       "allocator/partition_allocator/hardening_unittest.cc",
       "allocator/partition_allocator/memory_reclaimer_unittest.cc",
       "allocator/partition_allocator/page_allocator_unittest.cc",
-      "allocator/partition_allocator/partition_address_space_unittest.cc",
       "allocator/partition_allocator/partition_alloc_unittest.cc",
       "allocator/partition_allocator/partition_lock_unittest.cc",
       "allocator/partition_allocator/starscan/pcscan_scheduling_unittest.cc",
diff --git a/base/allocator/partition_allocator/partition_address_space.cc b/base/allocator/partition_allocator/partition_address_space.cc
index 7146152..3c01dbd 100644
--- a/base/allocator/partition_allocator/partition_address_space.cc
+++ b/base/allocator/partition_allocator/partition_address_space.cc
@@ -21,59 +21,65 @@
 
 #if defined(PA_HAS_64_BITS_POINTERS)
 
-constexpr std::array<size_t, 2> PartitionAddressSpace::kGigaCagePoolSizes;
-
 alignas(64) PartitionAddressSpace::GigaCageSetup PartitionAddressSpace::setup_;
 
 void PartitionAddressSpace::Init() {
   if (IsInitialized())
     return;
 
-  GigaCageProperties properties =
-      CalculateGigaCageProperties(kGigaCagePoolSizes);
+  setup_.non_brp_pool_base_address_ = reinterpret_cast<uintptr_t>(
+      AllocPages(nullptr, kNonBRPPoolSize, kNonBRPPoolSize,
+                 base::PageInaccessible, PageTag::kPartitionAlloc));
+  PA_CHECK(setup_.non_brp_pool_base_address_);
 
-  setup_.reserved_base_address_ =
-      reinterpret_cast<uintptr_t>(AllocPagesWithAlignOffset(
-          nullptr, properties.size, properties.alignment,
-          properties.alignment_offset, base::PageInaccessible,
-          PageTag::kPartitionAlloc));
-  PA_CHECK(setup_.reserved_base_address_);
-
-  uintptr_t current = setup_.reserved_base_address_;
-
-  setup_.non_brp_pool_base_address_ = current;
   PA_DCHECK(!(setup_.non_brp_pool_base_address_ & (kNonBRPPoolSize - 1)));
   setup_.non_brp_pool_ = internal::AddressPoolManager::GetInstance()->Add(
-      current, kNonBRPPoolSize);
+      setup_.non_brp_pool_base_address_, kNonBRPPoolSize);
   PA_CHECK(setup_.non_brp_pool_ == kNonBRPPoolHandle);
-  PA_DCHECK(!IsInNonBRPPool(reinterpret_cast<void*>(current - 1)));
-  PA_DCHECK(IsInNonBRPPool(reinterpret_cast<void*>(current)));
-  current += kNonBRPPoolSize;
-  PA_DCHECK(IsInNonBRPPool(reinterpret_cast<void*>(current - 1)));
-  PA_DCHECK(!IsInNonBRPPool(reinterpret_cast<void*>(current)));
+  PA_DCHECK(!IsInNonBRPPool(
+      reinterpret_cast<void*>(setup_.non_brp_pool_base_address_ - 1)));
+  PA_DCHECK(IsInNonBRPPool(
+      reinterpret_cast<void*>(setup_.non_brp_pool_base_address_)));
+  PA_DCHECK(IsInNonBRPPool(reinterpret_cast<void*>(
+      setup_.non_brp_pool_base_address_ + kNonBRPPoolSize - 1)));
+  PA_DCHECK(!IsInNonBRPPool(reinterpret_cast<void*>(
+      setup_.non_brp_pool_base_address_ + kNonBRPPoolSize)));
 
-  setup_.brp_pool_base_address_ = current;
+  // Reserve an extra allocation granularity unit before the BRP pool, but keep
+  // the pool aligned at kBRPPoolSize. A pointer immediately past an allocation
+  // is a valid pointer, and having a "forbidden zone" before the BRP pool
+  // prevents such a pointer from "sneaking into" the pool.
+  const size_t kForbiddenZoneSize = PageAllocationGranularity();
+  setup_.brp_pool_base_address_ =
+      reinterpret_cast<uintptr_t>(AllocPagesWithAlignOffset(
+          nullptr, kBRPPoolSize + kForbiddenZoneSize, kBRPPoolSize,
+          kBRPPoolSize - kForbiddenZoneSize, base::PageInaccessible,
+          PageTag::kPartitionAlloc)) +
+      kForbiddenZoneSize;
+  PA_CHECK(setup_.brp_pool_base_address_);
   PA_DCHECK(!(setup_.brp_pool_base_address_ & (kBRPPoolSize - 1)));
-  setup_.brp_pool_ =
-      internal::AddressPoolManager::GetInstance()->Add(current, kBRPPoolSize);
+  setup_.brp_pool_ = internal::AddressPoolManager::GetInstance()->Add(
+      setup_.brp_pool_base_address_, kBRPPoolSize);
   PA_CHECK(setup_.brp_pool_ == kBRPPoolHandle);
-  PA_DCHECK(!IsInBRPPool(reinterpret_cast<void*>(current - 1)));
-  PA_DCHECK(IsInBRPPool(reinterpret_cast<void*>(current)));
-  current += kBRPPoolSize;
-  PA_DCHECK(IsInBRPPool(reinterpret_cast<void*>(current - 1)));
-  PA_DCHECK(!IsInBRPPool(reinterpret_cast<void*>(current)));
+  PA_DCHECK(
+      !IsInBRPPool(reinterpret_cast<void*>(setup_.brp_pool_base_address_ - 1)));
+  PA_DCHECK(
+      IsInBRPPool(reinterpret_cast<void*>(setup_.brp_pool_base_address_)));
+  PA_DCHECK(IsInBRPPool(reinterpret_cast<void*>(setup_.brp_pool_base_address_ +
+                                                kBRPPoolSize - 1)));
+  PA_DCHECK(!IsInBRPPool(
+      reinterpret_cast<void*>(setup_.brp_pool_base_address_ + kBRPPoolSize)));
 
 #if PA_STARSCAN_USE_CARD_TABLE
   // Reserve memory for PCScan quarantine card table.
-  void* requested_address = reinterpret_cast<void*>(non_brp_pool_base_address_);
+  void* requested_address =
+      reinterpret_cast<void*>(setup_.non_brp_pool_base_address_);
   char* actual_address = internal::AddressPoolManager::GetInstance()->Reserve(
       non_brp_pool_, requested_address, kSuperPageSize);
   PA_CHECK(requested_address == actual_address)
       << "QuarantineCardTable is required to be allocated in the beginning of "
          "the non-BRP pool";
 #endif  // PA_STARSCAN_USE_CARD_TABLE
-
-  PA_DCHECK(setup_.reserved_base_address_ + properties.size == current);
 }
 
 void PartitionAddressSpace::InitConfigurablePool(void* address, size_t size) {
@@ -96,12 +102,16 @@
 }
 
 void PartitionAddressSpace::UninitForTesting() {
-  GigaCageProperties properties =
-      CalculateGigaCageProperties(kGigaCagePoolSizes);
-
-  FreePages(reinterpret_cast<void*>(setup_.reserved_base_address_),
-            properties.size);
-  setup_.reserved_base_address_ = 0;
+  FreePages(reinterpret_cast<void*>(setup_.non_brp_pool_base_address_),
+            kNonBRPPoolSize);
+  // For BRP pool, the allocation region includes a "forbidden zone" before the
+  // pool.
+  const size_t kForbiddenZoneSize = PageAllocationGranularity();
+  FreePages(reinterpret_cast<void*>(setup_.brp_pool_base_address_ -
+                                    kForbiddenZoneSize),
+            kBRPPoolSize + kForbiddenZoneSize);
+  // Do not free pages for the configurable pool, because its memory is owned
+  // by someone else, but deinitialize it nonetheless.
   setup_.non_brp_pool_base_address_ = kNonBRPPoolOffsetMask;
   setup_.brp_pool_base_address_ = kBRPPoolOffsetMask;
   setup_.configurable_pool_base_address_ = kConfigurablePoolOffsetMask;
diff --git a/base/allocator/partition_allocator/partition_address_space.h b/base/allocator/partition_allocator/partition_address_space.h
index a756b322..6cc8429 100644
--- a/base/allocator/partition_allocator/partition_address_space.h
+++ b/base/allocator/partition_allocator/partition_address_space.h
@@ -11,6 +11,7 @@
 
 #include "base/allocator/buildflags.h"
 #include "base/allocator/partition_allocator/address_pool_manager_types.h"
+#include "base/allocator/partition_allocator/page_allocator_constants.h"
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
@@ -29,47 +30,6 @@
 // The feature is not applicable to 32-bit address space.
 #if defined(PA_HAS_64_BITS_POINTERS)
 
-struct GigaCageProperties {
-  size_t size;
-  size_t alignment;
-  size_t alignment_offset;
-};
-
-template <size_t N>
-GigaCageProperties CalculateGigaCageProperties(
-    const std::array<size_t, N>& pool_sizes) {
-  size_t size_sum = 0;
-  size_t alignment = 0;
-  size_t alignment_offset;
-  // The goal is to find properties such that each pool's start address is
-  // aligned to its own size. To achieve that, the largest pool will serve
-  // as an anchor (the first one, if there are more) and it'll be used to
-  // determine the core alignment. The sizes of pools before the anchor will
-  // determine the offset within the core alignment at which the GigaCage will
-  // start.
-  // If this algorithm doesn't find the proper alignment, it means such an
-  // alignment doesn't exist.
-  for (size_t pool_size : pool_sizes) {
-    PA_CHECK(bits::IsPowerOfTwo(pool_size));
-    if (pool_size > alignment) {
-      alignment = pool_size;
-      // This may underflow, leading to a very high value, so use modulo
-      // |alignment| to bring it down.
-      alignment_offset = (alignment - size_sum) & (alignment - 1);
-    }
-    size_sum += pool_size;
-  }
-  // Use PA_CHECK because we can't correctly proceed if any pool's start address
-  // isn't aligned to its own size. Exact initial value of |sample_address|
-  // doesn't matter as long as |address % alignment == alignment_offset|.
-  uintptr_t sample_address = alignment_offset + 7 * alignment;
-  for (size_t pool_size : pool_sizes) {
-    PA_CHECK(!(sample_address & (pool_size - 1)));
-    sample_address += pool_size;
-  }
-  return GigaCageProperties{size_sum, alignment, alignment_offset};
-}
-
 // Reserves address space for PartitionAllocator.
 class BASE_EXPORT PartitionAddressSpace {
  public:
@@ -107,7 +67,7 @@
 #if BUILDFLAG(USE_BACKUP_REF_PTR)
     } else if (IsInBRPPool(address)) {
       pool = GetBRPPool();
-      base = brp_pool_base_address_;
+      base = setup_.brp_pool_base_address_;
 #endif  // BUILDFLAG(USE_BACKUP_REF_PTR)
     } else if (IsInConfigurablePool(address)) {
       pool = GetConfigurablePool();
@@ -134,13 +94,13 @@
   static void UninitForTesting();
 
   static ALWAYS_INLINE bool IsInitialized() {
-    if (setup_.reserved_base_address_) {
-      PA_DCHECK(setup_.non_brp_pool_ != 0);
+    // Either neither or both non-BRP and BRP pool are initialized. The
+    // configurable pool is initialized separately.
+    if (setup_.non_brp_pool_) {
       PA_DCHECK(setup_.brp_pool_ != 0);
       return true;
     }
 
-    PA_DCHECK(setup_.non_brp_pool_ == 0);
     PA_DCHECK(setup_.brp_pool_ == 0);
     return false;
   }
@@ -187,15 +147,10 @@
   void* operator new(size_t, void*) = delete;
 
  private:
-  // On 64-bit systems, GigaCage is split into two pools, one with allocations
-  // that have a BRP ref-count, and one with allocations that don't.
-  //   +----------------+ reserved_base_address_ (8GiB aligned)
-  //   |    non-BRP     |     == non_brp_pool_base_address_
-  //   |      pool      |
-  //   +----------------+ reserved_base_address_ + 8GiB
-  //   |      BRP       |     == brp_pool_base_address_
-  //   |      pool      |
-  //   +----------------+ reserved_base_address_ + 16GiB
+  // On 64-bit systems, GigaCage is split into disjoint pools. The BRP pool, is
+  // where all allocations have a BRP ref-count, thus pointers pointing there
+  // can use a BRP protection against UaF. Allocations in the non-BRP pool don't
+  // have that.
   //
   // Pool sizes have to be the power of two. Each pool will be aligned at its
   // own size boundary.
@@ -203,17 +158,8 @@
   // NOTE! The BRP pool must be preceded by a reserved region, where allocations
   // are forbidden. This is to prevent a pointer immediately past a non-GigaCage
   // allocation from falling into the BRP pool, thus triggering BRP mechanism
-  // and likely crashing. One way to implement this is to place another
-  // PartitionAlloc pool right before, because trailing guard pages there will
-  // fulfill this guarantee. Alternatively, it could be any region that
-  // guarantess to not have allocations extending to its very end. But it's just
-  // easier to have non-BRP pool there.
-  //
-  // If more than 2 consecutive pools are ever needed, care will have to be
-  // taken when choosing sizes. For example, for sizes [8GiB,4GiB,8GiB], it'd be
-  // impossible to align each pool at its own size boundary while keeping them
-  // next to each other. CalculateGigaCageProperties() has non-debug, run-time
-  // checks to assert that.
+  // and likely crashing. This "forbidden zone" can be as small as 1B, but it's
+  // simpler to just reserve an allocation granularity unit.
   //
   // The ConfigurablePool is an optional Pool that can be created inside an
   // existing mapping by the embedder, and so will be outside of the GigaCage.
@@ -222,8 +168,6 @@
   // memory cage, which requires that ArrayBuffers be located inside of it.
   static constexpr size_t kNonBRPPoolSize = kPoolMaxSize;
   static constexpr size_t kBRPPoolSize = kPoolMaxSize;
-  static constexpr std::array<size_t, 2> kGigaCagePoolSizes = {kNonBRPPoolSize,
-                                                               kBRPPoolSize};
   static constexpr size_t kConfigurablePoolSize = 4 * kGiB;
   static_assert(
       kConfigurablePoolSize <= kPoolMaxSize,
@@ -246,8 +190,6 @@
       ~kConfigurablePoolOffsetMask;
 
   struct GigaCageSetup {
-    uintptr_t reserved_base_address_ = 0;
-
     // Before PartitionAddressSpace::Init(), no allocation are allocated from a
     // reserved address space. Therefore, set *_pool_base_address_ initially to
     // k*PoolOffsetMask, so that PartitionAddressSpace::IsIn*Pool() always
@@ -260,7 +202,7 @@
     pool_handle brp_pool_ = 0;
     pool_handle configurable_pool_ = 0;
 
-    char padding_[20] = {};
+    char padding_[28] = {};
   };
   static_assert(sizeof(GigaCageSetup) % 64 == 0,
                 "GigaCageSetup has to fill a cacheline(s)");
diff --git a/base/allocator/partition_allocator/partition_address_space_unittest.cc b/base/allocator/partition_allocator/partition_address_space_unittest.cc
deleted file mode 100644
index 44c509a..0000000
--- a/base/allocator/partition_allocator/partition_address_space_unittest.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// 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 "base/allocator/partition_allocator/partition_address_space.h"
-
-#include <array>
-
-#include "base/allocator/partition_allocator/partition_alloc_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace internal {
-
-#if defined(PA_HAS_64_BITS_POINTERS)
-
-TEST(PartitionAllocAddressSpaceTest, CalculateGigaCageProperties) {
-  GigaCageProperties props;
-
-  props = CalculateGigaCageProperties(std::array<size_t, 1>{1});
-  EXPECT_EQ(1u, props.size);
-  EXPECT_EQ(1u, props.alignment);
-  EXPECT_EQ(0u, props.alignment_offset);
-
-  props = CalculateGigaCageProperties(std::array<size_t, 2>{2, 1});
-  EXPECT_EQ(3u, props.size);
-  EXPECT_EQ(2u, props.alignment);
-  EXPECT_EQ(0u, props.alignment_offset);
-
-  props = CalculateGigaCageProperties(std::array<size_t, 2>{1, 2});
-  EXPECT_EQ(3u, props.size);
-  EXPECT_EQ(2u, props.alignment);
-  EXPECT_EQ(1u, props.alignment_offset);
-
-  props = CalculateGigaCageProperties(std::array<size_t, 4>{8, 4, 2, 1});
-  EXPECT_EQ(15u, props.size);
-  EXPECT_EQ(8u, props.alignment);
-  EXPECT_EQ(0u, props.alignment_offset);
-
-  props = CalculateGigaCageProperties(std::array<size_t, 4>{1, 2, 4, 8});
-  EXPECT_EQ(15u, props.size);
-  EXPECT_EQ(8u, props.alignment);
-  EXPECT_EQ(1u, props.alignment_offset);
-
-  props =
-      CalculateGigaCageProperties(std::array<size_t, 7>{1, 2, 4, 2, 2, 4, 8});
-  EXPECT_EQ(23u, props.size);
-  EXPECT_EQ(8u, props.alignment);
-  EXPECT_EQ(1u, props.alignment_offset);
-
-  props = CalculateGigaCageProperties(std::array<size_t, 3>{8, 4, 4});
-  EXPECT_EQ(16u, props.size);
-  EXPECT_EQ(8u, props.alignment);
-  EXPECT_EQ(0u, props.alignment_offset);
-
-  props = CalculateGigaCageProperties(std::array<size_t, 3>{4, 4, 8});
-  EXPECT_EQ(16u, props.size);
-  EXPECT_EQ(8u, props.alignment);
-  EXPECT_EQ(0u, props.alignment_offset);
-
-  props = CalculateGigaCageProperties(std::array<size_t, 4>{8, 4, 4, 8});
-  EXPECT_EQ(24u, props.size);
-  EXPECT_EQ(8u, props.alignment);
-  EXPECT_EQ(0u, props.alignment_offset);
-
-  size_t GiB = 1024ull * 1024 * 1024;
-  props = CalculateGigaCageProperties(
-      std::array<size_t, 6>{GiB, GiB, GiB, GiB, GiB, GiB});
-  EXPECT_EQ(6 * GiB, props.size);
-  EXPECT_EQ(GiB, props.alignment);
-  EXPECT_EQ(0u, props.alignment_offset);
-}
-
-TEST(PartitionAllocAddressSpaceTest, CalculateGigaCagePropertiesImpossible) {
-  EXPECT_DEATH_IF_SUPPORTED(
-      CalculateGigaCageProperties(std::array<size_t, 1>{0}), "");
-  EXPECT_DEATH_IF_SUPPORTED(
-      CalculateGigaCageProperties(std::array<size_t, 1>{3}), "");
-  EXPECT_DEATH_IF_SUPPORTED(
-      CalculateGigaCageProperties(std::array<size_t, 3>{8, 4, 8}), "");
-  EXPECT_DEATH_IF_SUPPORTED(
-      CalculateGigaCageProperties(std::array<size_t, 7>{1, 2, 4, 1, 2, 4, 8}),
-      "");
-}
-
-#endif  // defined(PA_HAS_64_BITS_POINTERS)
-
-}  // namespace internal
-}  // namespace base
diff --git a/chrome/VERSION b/chrome/VERSION
index d291e495..249d4f7 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=97
 MINOR=0
-BUILD=4667
+BUILD=4668
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 5c51e82..c096695 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -339,7 +339,6 @@
     "//chrome/browser/banners/android:java",
     "//chrome/browser/browser_controls/android:java",
     "//chrome/browser/commerce/merchant_viewer/android:java",
-    "//chrome/browser/commerce/price_tracking/proto:proto_java",
     "//chrome/browser/commerce/shopping_list/android:java",
     "//chrome/browser/consent_auditor/android:java",
     "//chrome/browser/contextmenu:java",
@@ -779,7 +778,6 @@
     "//chrome/browser/android/browserservices/metrics:jni_headers",
     "//chrome/browser/android/browserservices/verification:jni_headers",
     "//chrome/browser/commerce/merchant_viewer/android:jni_headers",
-    "//chrome/browser/commerce/price_tracking/android:jni_headers",
     "//chrome/browser/commerce/subscriptions/android:jni_headers",
     "//chrome/browser/contextmenu:jni_headers",
     "//chrome/browser/download/android:jni_headers",
@@ -936,7 +934,6 @@
     "//chrome/browser/browser_controls/android:java",
     "//chrome/browser/browser_controls/android:junit",
     "//chrome/browser/commerce/merchant_viewer/android:junit",
-    "//chrome/browser/commerce/price_tracking/proto:proto_java",
     "//chrome/browser/contextmenu:java",
     "//chrome/browser/continuous_search:junit",
     "//chrome/browser/continuous_search/internal:junit",
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 4a6200d..24cca08 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -11971,6 +11971,13 @@
     <message name="IDS_LENS_REGION_SEARCH_BUBBLE_TEXT" desc="Text that is shown in the Lens Region Search education bubble when starting the feature. Informs the user to drag over the screen to select a region to search with Google Lens.">
       Drag over any image to search
     </message>
+    <!-- ChromeLabs Thumbnail Tab Strip -->
+    <message name="IDS_THUMBNAIL_TAB_STRIP_EXPERIMENT_NAME" desc="Name for Thumbnail Tab Strip experiment">
+      Thumbnail tab strip for tablet mode
+    </message>
+    <message name="IDS_THUMBNAIL_TAB_STRIP_EXPERIMENT_DESCRIPTION" desc="Description for Thumbnail Tab Strip experiment">
+      In tablet mode, tap on the tab counter toolbar button to open the new tab strip that shows thumbnails of each tab.
+    </message>
 
     <if expr="is_cfm">
       <message name="IDS_CFM_NETWORK_SETTINGS_TITLE" desc="Title for CFM Network Settings dialog">
diff --git a/chrome/app/generated_resources_grd/IDS_THUMBNAIL_TAB_STRIP_EXPERIMENT_DESCRIPTION.png.sha1 b/chrome/app/generated_resources_grd/IDS_THUMBNAIL_TAB_STRIP_EXPERIMENT_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..f7f8057
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_THUMBNAIL_TAB_STRIP_EXPERIMENT_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+e456532664b2976795b83b70b5334b55af9073f6
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_THUMBNAIL_TAB_STRIP_EXPERIMENT_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_THUMBNAIL_TAB_STRIP_EXPERIMENT_NAME.png.sha1
new file mode 100644
index 0000000..f7f8057
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_THUMBNAIL_TAB_STRIP_EXPERIMENT_NAME.png.sha1
@@ -0,0 +1 @@
+e456532664b2976795b83b70b5334b55af9073f6
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index c42f5d10..2c7581e 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3002,8 +3002,6 @@
       "chrome_browser_main_android.cc",
       "chrome_browser_main_android.h",
       "commerce/merchant_viewer/web_contents_helper.cc",
-      "commerce/price_tracking/android/price_tracking_notification_bridge.cc",
-      "commerce/price_tracking/android/price_tracking_notification_bridge.h",
       "component_updater/desktop_sharing_hub_component_remover.cc",
       "component_updater/desktop_sharing_hub_component_remover.h",
       "content_creation/notes/internal/android/note_service_bridge_factory.cc",
@@ -3337,7 +3335,7 @@
       "//chrome/browser/commerce/merchant_viewer:merchant_signal_db_content_proto",
       "//chrome/browser/commerce/merchant_viewer:merchant_viewer_data_manager",
       "//chrome/browser/commerce/merchant_viewer/android:jni_headers",
-      "//chrome/browser/commerce/price_tracking/proto:proto_java",
+      "//chrome/browser/commerce/price_tracking/proto:notifications_proto",
       "//chrome/browser/commerce/subscriptions:commerce_subscription_db",
       "//chrome/browser/commerce/subscriptions:commerce_subscription_db_content_proto",
       "//chrome/browser/commerce/subscriptions/android:jni_headers",
@@ -4771,12 +4769,12 @@
       "shell_integration_chromeos.cc",
       "signin/signin_status_metrics_provider_chromeos.cc",
       "signin/signin_status_metrics_provider_chromeos.h",
-      "speech/crosapi_tts_engine_delegate_ash.cc",
-      "speech/crosapi_tts_engine_delegate_ash.h",
       "speech/cros_speech_recognition_service.cc",
       "speech/cros_speech_recognition_service.h",
       "speech/cros_speech_recognition_service_factory.cc",
       "speech/cros_speech_recognition_service_factory.h",
+      "speech/crosapi_tts_engine_delegate_ash.cc",
+      "speech/crosapi_tts_engine_delegate_ash.h",
       "speech/network_speech_recognizer.cc",
       "speech/network_speech_recognizer.h",
       "speech/on_device_speech_recognizer.cc",
@@ -4897,6 +4895,7 @@
       "//components/metrics/structured:neutrino_logging",
       "//components/metrics/structured:neutrino_logging_util",
       "//components/services/app_service/public/cpp:instance_update",
+      "//components/services/app_service/public/cpp:permission_utils",
       "//components/services/font:lib",
       "//components/services/font/public/mojom",
       "//components/user_manager",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 5e7c79449..84b04cba 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3004,7 +3004,8 @@
      flag_descriptions::kTopChromeTouchUiDescription, kOsDesktop,
      MULTI_VALUE_TYPE(kTopChromeTouchUiChoices)},
 #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
-    {"webui-tab-strip", flag_descriptions::kWebUITabStripName,
+    {flag_descriptions::kWebUITabStripFlagId,
+     flag_descriptions::kWebUITabStripName,
      flag_descriptions::kWebUITabStripDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kWebUITabStrip)},
 
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.cc b/chrome/browser/apps/app_service/publishers/arc_apps.cc
index 6cdda259..0777de7 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/arc_apps.cc
@@ -51,6 +51,7 @@
 #include "components/arc/mojom/file_system.mojom.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
+#include "components/services/app_service/public/cpp/permission_utils.h"
 #include "components/services/app_service/public/cpp/types_util.h"
 #include "extensions/grit/extensions_browser_resources.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -191,8 +192,8 @@
     auto permission = apps::mojom::Permission::New();
     permission->permission_type =
         GetAppServicePermissionType(new_permission.first);
-    permission->value_type = apps::mojom::PermissionValueType::kBool;
-    permission->value = static_cast<uint32_t>(new_permission.second->granted);
+    permission->value = apps::mojom::PermissionValue::New();
+    permission->value->set_bool_value(new_permission.second->granted);
     permission->is_managed = new_permission.second->managed;
 
     permissions->push_back(std::move(permission));
@@ -893,7 +894,7 @@
     return;
   }
 
-  if (permission->value) {
+  if (apps_util::IsPermissionEnabled(permission->value)) {
     auto* permissions_instance = ARC_GET_INSTANCE_FOR_METHOD(
         arc_service_manager->arc_bridge_service()->app_permissions(),
         GrantPermission);
diff --git a/chrome/browser/apps/app_service/publishers/borealis_apps.cc b/chrome/browser/apps/app_service/publishers/borealis_apps.cc
index c0f437d8..2561e7f 100644
--- a/chrome/browser/apps/app_service/publishers/borealis_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/borealis_apps.cc
@@ -22,6 +22,7 @@
 #include "chrome/grit/chrome_unscaled_resources.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
+#include "components/services/app_service/public/cpp/permission_utils.h"
 #include "components/services/app_service/public/cpp/publisher_base.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -82,9 +83,9 @@
   for (const PermissionInfo& info : permission_infos) {
     auto permission = apps::mojom::Permission::New();
     permission->permission_type = info.permission;
-    permission->value_type = apps::mojom::PermissionValueType::kBool;
-    permission->value =
-        static_cast<uint32_t>(profile->GetPrefs()->GetBoolean(info.pref_name));
+    permission->value = apps::mojom::PermissionValue::New();
+    permission->value->set_bool_value(
+        profile->GetPrefs()->GetBoolean(info.pref_name));
     permission->is_managed = false;
     app->permissions.push_back(std::move(permission));
   }
@@ -215,7 +216,8 @@
   if (!pref_name) {
     return;
   }
-  profile_->GetPrefs()->SetBoolean(pref_name, permission_ptr->value);
+  profile_->GetPrefs()->SetBoolean(
+      pref_name, apps_util::IsPermissionEnabled(permission_ptr->value));
 }
 
 void BorealisApps::Uninstall(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/publishers/plugin_vm_apps.cc b/chrome/browser/apps/app_service/publishers/plugin_vm_apps.cc
index 36bdf74..9d390d6 100644
--- a/chrome/browser/apps/app_service/publishers/plugin_vm_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/plugin_vm_apps.cc
@@ -24,6 +24,7 @@
 #include "chrome/grit/chrome_unscaled_resources.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
+#include "components/services/app_service/public/cpp/permission_utils.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -79,9 +80,9 @@
   for (const PermissionInfo& info : permission_infos) {
     auto permission = apps::mojom::Permission::New();
     permission->permission_type = info.permission;
-    permission->value_type = apps::mojom::PermissionValueType::kBool;
-    permission->value =
-        static_cast<uint32_t>(profile->GetPrefs()->GetBoolean(info.pref_name));
+    permission->value = apps::mojom::PermissionValue::New();
+    permission->value->set_bool_value(
+        profile->GetPrefs()->GetBoolean(info.pref_name));
     permission->is_managed = false;
     app->permissions.push_back(std::move(permission));
   }
@@ -212,7 +213,8 @@
     return;
   }
 
-  profile_->GetPrefs()->SetBoolean(pref_name, permission_ptr->value);
+  profile_->GetPrefs()->SetBoolean(
+      pref_name, apps_util::IsPermissionEnabled(permission_ptr->value));
 }
 
 void PluginVmApps::Uninstall(const std::string& app_id,
diff --git a/chrome/browser/ash/app_restore/full_restore_app_launch_handler_browsertest.cc b/chrome/browser/ash/app_restore/full_restore_app_launch_handler_browsertest.cc
index 79ab9341..19b494b 100644
--- a/chrome/browser/ash/app_restore/full_restore_app_launch_handler_browsertest.cc
+++ b/chrome/browser/ash/app_restore/full_restore_app_launch_handler_browsertest.cc
@@ -1109,7 +1109,7 @@
   auto window_info = ::full_restore::GetWindowInfo(window1);
   ASSERT_TRUE(window_info);
   EXPECT_TRUE(window_info->activation_index.has_value());
-  EXPECT_EQ(INT32_MIN, window_info->activation_index.value());
+  EXPECT_EQ(INT32_MAX, window_info->activation_index.value());
 
   app_window2 = CreateAppWindow(browser()->profile(), extension);
   ASSERT_TRUE(app_window2);
@@ -1120,7 +1120,7 @@
   window_info = ::full_restore::GetWindowInfo(window2);
   ASSERT_TRUE(window_info);
   EXPECT_TRUE(window_info->activation_index.has_value());
-  EXPECT_EQ(INT32_MIN, window_info->activation_index.value());
+  EXPECT_EQ(INT32_MAX, window_info->activation_index.value());
 
   // Create a new window, verity the restore window id is 0.
   auto* app_window = CreateAppWindow(browser()->profile(), extension);
diff --git a/chrome/browser/ash/crosapi/browser_util_unittest.cc b/chrome/browser/ash/crosapi/browser_util_unittest.cc
index 9e5e6e6ea..81c6aa0 100644
--- a/chrome/browser/ash/crosapi/browser_util_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_util_unittest.cc
@@ -107,10 +107,22 @@
   ScopedTestingLocalState local_state_;
 };
 
-TEST_F(BrowserUtilTest, LacrosEnabledByFlag) {
+class LacrosSupportBrowserUtilTest : public BrowserUtilTest {
+ public:
+  LacrosSupportBrowserUtilTest() {
+    scoped_feature_list_.InitAndDisableFeature(
+        chromeos::features::kLacrosSupport);
+  }
+  ~LacrosSupportBrowserUtilTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(LacrosSupportBrowserUtilTest, LacrosEnabledByFlag) {
   AddRegularUser("user@test.com");
 
-  // Lacros is disabled because the feature isn't enabled by default.
+  // Lacros is initially disabled.
   EXPECT_FALSE(browser_util::IsLacrosEnabled());
 
   // Enabling the flag enables Lacros.
@@ -202,7 +214,7 @@
   EXPECT_FALSE(browser_util::IsLacrosEnabled(Channel::UNKNOWN));
 }
 
-TEST_F(BrowserUtilTest, AshWebBrowserEnabled) {
+TEST_F(LacrosSupportBrowserUtilTest, AshWebBrowserEnabled) {
   base::test::ScopedFeatureList feature_list;
   AddRegularUser("user@managedchrome.com");
   testing_profile_.GetProfilePolicyConnector()->OverrideIsManagedForTesting(
@@ -278,7 +290,7 @@
   EXPECT_FALSE(browser_util::IsAshWebBrowserEnabled(Channel::STABLE));
 }
 
-TEST_F(BrowserUtilTest, LacrosPrimaryBrowserByFlags) {
+TEST_F(LacrosSupportBrowserUtilTest, LacrosPrimaryBrowserByFlags) {
   AddRegularUser("user@test.com");
   { EXPECT_FALSE(browser_util::IsLacrosPrimaryBrowser()); }
 
diff --git a/chrome/browser/ash/crostini/crostini_terminal.cc b/chrome/browser/ash/crostini/crostini_terminal.cc
index fd542c7c..12e8bd3 100644
--- a/chrome/browser/ash/crostini/crostini_terminal.cc
+++ b/chrome/browser/ash/crostini/crostini_terminal.cc
@@ -85,10 +85,21 @@
                     const ContainerId& container_id,
                     const std::string& cwd,
                     const std::vector<std::string>& terminal_args) {
-  crostini::RecordAppLaunchHistogram(
-      crostini::CrostiniAppLaunchAppType::kTerminal);
   GURL vsh_in_crosh_url =
       GenerateVshInCroshUrl(profile, container_id, cwd, terminal_args);
+  LaunchTerminalWithUrl(profile, display_id, vsh_in_crosh_url);
+}
+
+void LaunchTerminalWithUrl(Profile* profile,
+                           int64_t display_id,
+                           const GURL& url) {
+  if (url.GetOrigin() != chrome::kChromeUIUntrustedTerminalURL) {
+    LOG(ERROR) << "Trying to launch terminal with an invalid url: " << url;
+    return;
+  }
+
+  crostini::RecordAppLaunchHistogram(
+      crostini::CrostiniAppLaunchAppType::kTerminal);
   auto params = web_app::CreateSystemWebAppLaunchParams(
       profile, web_app::SystemAppType::TERMINAL, display_id);
   if (!params.has_value()) {
@@ -109,7 +120,7 @@
   // System Web Apps managed by Web App publisher should call
   // LaunchSystemWebAppAsync.
   web_app::LaunchSystemWebAppImpl(profile, web_app::SystemAppType::TERMINAL,
-                                  vsh_in_crosh_url, *params);
+                                  url, *params);
 }
 
 void LaunchTerminalSettings(Profile* profile, int64_t display_id) {
diff --git a/chrome/browser/ash/crostini/crostini_terminal.h b/chrome/browser/ash/crostini/crostini_terminal.h
index e7e1781a..4da286ed 100644
--- a/chrome/browser/ash/crostini/crostini_terminal.h
+++ b/chrome/browser/ash/crostini/crostini_terminal.h
@@ -103,6 +103,10 @@
                     const std::string& cwd = "",
                     const std::vector<std::string>& terminal_args = {});
 
+void LaunchTerminalWithUrl(Profile* profile,
+                           int64_t display_id,
+                           const GURL& url);
+
 // Launches the terminal settings popup window.
 void LaunchTerminalSettings(Profile* profile,
                             int64_t display_id = display::kInvalidDisplayId);
diff --git a/chrome/browser/banners/app_banner_manager_browsertest_base.cc b/chrome/browser/banners/app_banner_manager_browsertest_base.cc
index f8afcc5..a9f96c1 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest_base.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest_base.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/banners/app_banner_manager_browsertest_base.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/base/url_util.h"
@@ -21,6 +23,8 @@
 
   os_hooks_suppress_ =
       web_app::OsIntegrationManager::ScopedSuppressOsHooksForTesting();
+  web_app::test::WaitUntilReady(
+      web_app::WebAppProvider::GetForTest(browser()->profile()));
 }
 
 GURL AppBannerManagerBrowserTestBase::GetBannerURL() {
diff --git a/chrome/browser/cart/cart_service.h b/chrome/browser/cart/cart_service.h
index 8b4e2d9..60902b4a 100644
--- a/chrome/browser/cart/cart_service.h
+++ b/chrome/browser/cart/cart_service.h
@@ -130,6 +130,7 @@
   friend class CartServiceBrowserDiscountTest;
   friend class CartServiceDiscountFetchTest;
   friend class CartServiceCouponTest;
+  friend class FetchDiscountWorkerBrowserTest;
   FRIEND_TEST_ALL_PREFIXES(CartHandlerNtpModuleFakeDataTest,
                            TestEnableFakeData);
 
diff --git a/chrome/browser/cart/fetch_discount_worker.cc b/chrome/browser/cart/fetch_discount_worker.cc
index b62d526b..559d6f3e 100644
--- a/chrome/browser/cart/fetch_discount_worker.cc
+++ b/chrome/browser/cart/fetch_discount_worker.cc
@@ -250,6 +250,7 @@
     if (!discounts.count(cart_url)) {
       cart_discount_proto->clear_discount_text();
       cart_discount_proto->clear_rule_discount_info();
+      cart_discount_proto->clear_has_coupons();
       cart_service_delegate_->UpdateCart(cart_url, std::move(cart_proto),
                                          is_tester);
       continue;
diff --git a/chrome/browser/cart/fetch_discount_worker_browsertest.cc b/chrome/browser/cart/fetch_discount_worker_browsertest.cc
new file mode 100644
index 0000000..818d98c
--- /dev/null
+++ b/chrome/browser/cart/fetch_discount_worker_browsertest.cc
@@ -0,0 +1,345 @@
+// 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/test/base/in_process_browser_test.h"
+
+#include "base/callback_list.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_timeouts.h"
+#include "chrome/browser/cart/cart_db_content.pb.h"
+#include "chrome/browser/cart/cart_service.h"
+#include "chrome/browser/commerce/commerce_feature_list.h"
+#include "chrome/browser/persisted_state_db/profile_proto_db.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/common/pref_names.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
+#include "components/prefs/pref_service.h"
+#include "components/search/ntp_features.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "content/public/test/browser_test.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+namespace {
+std::string BuildPartnerMerchantPattern(
+    std::vector<std::string>& parter_merchant_list) {
+  std::string pattern = "(";
+  pattern += base::JoinString(parter_merchant_list, "|");
+  pattern += ")";
+  return pattern;
+}
+
+std::unique_ptr<net::test_server::HttpResponse> BasicResponse(
+    bool return_empty_response,
+    const net::test_server::HttpRequest& request) {
+  if (request.relative_url == "/coupons/fl_codeless_discounts.json" &&
+      !return_empty_response) {
+    return nullptr;
+  }
+  auto response = std::make_unique<net::test_server::BasicHttpResponse>();
+  response->set_content("{}");
+  response->set_content_type("application/json; charset=UTF-8");
+  return response;
+}
+
+cart_db::ChromeCartContentProto BuildCartProto(const char* domain,
+                                               const char* merchant_url) {
+  cart_db::ChromeCartContentProto proto;
+  proto.set_key(domain);
+  proto.set_merchant_cart_url(merchant_url);
+  proto.set_timestamp(base::Time::Now().ToDoubleT());
+  return proto;
+}
+
+cart_db::ChromeCartContentProto BuildCartProtoWithCoupon(
+    const char* domain,
+    const char* merchant_url) {
+  cart_db::ChromeCartContentProto proto = BuildCartProto(domain, merchant_url);
+  proto.mutable_discount_info()->set_has_coupons(true);
+  return proto;
+}
+
+using ShoppingCarts =
+    std::vector<ProfileProtoDB<cart_db::ChromeCartContentProto>::KeyAndValue>;
+}  // namespace
+
+class FetchDiscountWorkerBrowserTest : public InProcessBrowserTest {
+ public:
+  void SetUpInProcessBrowserTestFixture() override {
+    create_services_subscription_ =
+        BrowserContextDependencyManager::GetInstance()
+            ->RegisterCreateServicesCallbackForTesting(
+                base::BindRepeating(&FetchDiscountWorkerBrowserTest::
+                                        OnWillCreateBrowserContextServices,
+                                    base::Unretained(this)));
+  }
+
+  void SetUpOnMainThread() override {
+    Profile* profile = browser()->profile();
+    identity_test_environment_adaptor_ =
+        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile);
+    service_ = CartServiceFactory::GetForProfile(profile);
+
+    host_resolver()->AddRule("*", "127.0.0.1");
+    embedded_test_server()->ServeFilesFromSourceDirectory(
+        "chrome/test/data/cart");
+    embedded_test_server()->RegisterDefaultHandler(
+        base::BindRepeating(&BasicResponse, false /*return_empty_response*/));
+
+    // Simulate discount consent is accepted.
+    profile->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, true);
+
+    SignIn();
+  }
+
+  void TearDownOnMainThread() override {
+    identity_test_environment_adaptor_.reset();
+  }
+
+  void CheckFLCodelessDiscounts(base::OnceClosure closure,
+                                bool contained_coupon,
+                                std::string expected_discount_text,
+                                bool success,
+                                ShoppingCarts found) {
+    EXPECT_TRUE(success);
+    EXPECT_EQ(1U, found.size());
+
+    EXPECT_TRUE(found[0].second.has_discount_info());
+    EXPECT_EQ(contained_coupon, found[0].second.discount_info().has_coupons());
+    EXPECT_EQ(expected_discount_text,
+              found[0].second.discount_info().discount_text());
+
+    std::move(closure).Run();
+  }
+
+ protected:
+  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+    IdentityTestEnvironmentProfileAdaptor::
+        SetIdentityTestEnvironmentFactoriesOnBrowserContext(context);
+  }
+
+  void SignIn() {
+    identity_test_environment_adaptor_->identity_test_env()
+        ->MakePrimaryAccountAvailable("user@gmail.com",
+                                      signin::ConsentLevel::kSync);
+    identity_test_environment_adaptor_->identity_test_env()
+        ->SetAutomaticIssueOfAccessTokens(true);
+  }
+
+  void CreateCart(const std::string& domain,
+                  cart_db::ChromeCartContentProto cart_proto) {
+    CartDB* cart_db = service_->GetDB();
+    base::RunLoop run_loop;
+
+    cart_db->AddCart(
+        domain, cart_proto,
+        base::BindOnce(&FetchDiscountWorkerBrowserTest::OnCartAdded,
+                       base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  void OnCartAdded(base::OnceClosure closure, bool success) {
+    EXPECT_TRUE(success);
+    std::move(closure).Run();
+  }
+
+  void StartGettingDiscount() { service_->StartGettingDiscount(); }
+
+  void waitForDiscounts(const std::string& cart_domain) {
+    satisfied_ = false;
+    while (true) {
+      base::RunLoop().RunUntilIdle();
+      base::RunLoop run_loop;
+      service_->LoadCart(
+          cart_domain,
+          base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckLastFetchTime,
+                         base::Unretained(this), run_loop.QuitClosure()));
+      run_loop.Run();
+      if (satisfied_)
+        break;
+      base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+    }
+  }
+
+  void CheckLastFetchTime(base::OnceClosure closure,
+                          bool success,
+                          ShoppingCarts found) {
+    EXPECT_TRUE(success);
+    EXPECT_EQ(1U, found.size());
+
+    if (found[0].second.has_discount_info()) {
+      if (found[0].second.discount_info().last_fetched_timestamp() != 0) {
+        satisfied_ = true;
+      } else {
+        VLOG(2) << "last_fetched_timestamp not set";
+      }
+
+    } else {
+      VLOG(2) << "Not contain discount_info";
+    }
+
+    std::move(closure).Run();
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
+      identity_test_environment_adaptor_;
+  base::CallbackListSubscription create_services_subscription_;
+  CartService* service_;
+  bool satisfied_;
+};
+
+class FetchFLCodelessDiscountWorkerBrowserTest
+    : public FetchDiscountWorkerBrowserTest {
+ public:
+  FetchFLCodelessDiscountWorkerBrowserTest() {
+    parter_merchant_list_.push_back("merchant0.com");
+    parter_merchant_list_.push_back("merchant1.com");
+    parter_merchant_list_.push_back("merchant2.com");
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+
+    std::vector<base::test::ScopedFeatureList::FeatureAndParams>
+        enabled_features;
+    base::FieldTrialParams cart_params, coupon_params;
+    cart_params[ntp_features::kNtpChromeCartModuleAbandonedCartDiscountParam] =
+        "true";
+    cart_params["CartDiscountFetcherEndpointParam"] =
+        embedded_test_server()
+            ->GetURL("/coupons/fl_codeless_discounts.json")
+            .spec();
+    enabled_features.emplace_back(ntp_features::kNtpChromeCartModule,
+                                  cart_params);
+    coupon_params["coupon-partner-merchant-pattern"] =
+        BuildPartnerMerchantPattern(parter_merchant_list_);
+    enabled_features.emplace_back(commerce::kRetailCoupons, coupon_params);
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        enabled_features,
+        /*disabled_features*/ {
+            optimization_guide::features::kOptimizationHints});
+  }
+
+ protected:
+  std::vector<std::string> parter_merchant_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(FetchFLCodelessDiscountWorkerBrowserTest,
+                       SimplePercentOffTest) {
+  embedded_test_server()->StartAcceptingConnections();
+
+  CreateCart("merchant1.com",
+             BuildCartProto("merchant1.com", "https://www.merchant1.com/cart"));
+
+  StartGettingDiscount();
+  waitForDiscounts("merchant1.com");
+
+  // Verify discounts.
+  base::RunLoop run_loop;
+  std::string expected_discount_text = "10% off";
+  service_->LoadCart(
+      "merchant1.com",
+      base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
+                     base::Unretained(this), run_loop.QuitClosure(), true,
+                     expected_discount_text));
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_F(FetchFLCodelessDiscountWorkerBrowserTest,
+                       SimpleDollarOffTest) {
+  embedded_test_server()->StartAcceptingConnections();
+
+  CreateCart("merchant2.com",
+             BuildCartProto("merchant2.com", "https://www.merchant2.com/cart"));
+
+  StartGettingDiscount();
+  waitForDiscounts("merchant2.com");
+
+  // Verify discounts.
+  base::RunLoop run_loop;
+  std::string expected_discount_text = "$2 off";
+  service_->LoadCart(
+      "merchant2.com",
+      base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
+                     base::Unretained(this), run_loop.QuitClosure(), true,
+                     expected_discount_text));
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_F(FetchFLCodelessDiscountWorkerBrowserTest,
+                       NoDiscountForThisMerchantTest) {
+  embedded_test_server()->StartAcceptingConnections();
+
+  CreateCart("merchant0.com",
+             BuildCartProto("merchant0.com", "https://www.merchant0.com/cart"));
+
+  StartGettingDiscount();
+  waitForDiscounts("merchant0.com");
+
+  // Verify discounts.
+  base::RunLoop run_loop;
+  service_->LoadCart(
+      "merchant0.com",
+      base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
+                     base::Unretained(this), run_loop.QuitClosure(), false,
+                     ""));
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_F(FetchFLCodelessDiscountWorkerBrowserTest,
+                       TwoCartsOneWithDiscountOneWithoutDiscount) {
+  embedded_test_server()->StartAcceptingConnections();
+
+  CreateCart("merchant0.com",
+             BuildCartProto("merchant0.com", "https://www.merchant0.com/cart"));
+  CreateCart("merchant1.com",
+             BuildCartProto("merchant1.com", "https://www.merchant1.com/cart"));
+
+  StartGettingDiscount();
+  waitForDiscounts("merchant0.com");
+  waitForDiscounts("merchant1.com");
+
+  // Verify discounts.
+  base::RunLoop run_loop[2];
+  service_->LoadCart(
+      "merchant0.com",
+      base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
+                     base::Unretained(this), run_loop[0].QuitClosure(), false,
+                     ""));
+  run_loop[0].Run();
+  service_->LoadCart(
+      "merchant1.com",
+      base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
+                     base::Unretained(this), run_loop[1].QuitClosure(), true,
+                     "10% off" /*expected_discount_text*/));
+  run_loop[1].Run();
+}
+
+IN_PROC_BROWSER_TEST_F(FetchFLCodelessDiscountWorkerBrowserTest,
+                       CartDiscountBecomeUnavailable) {
+  CreateCart("merchant1.com",
+             BuildCartProtoWithCoupon("merchant1.com",
+                                      "https://www.merchant1.com/cart"));
+
+  // Config server to return empty response.
+  embedded_test_server()->RegisterRequestHandler(
+      base::BindRepeating(&BasicResponse, true /*return_empty_response*/));
+  embedded_test_server()->StartAcceptingConnections();
+
+  StartGettingDiscount();
+  waitForDiscounts("merchant1.com");
+
+  // Verify cart has no coupon discount.
+  base::RunLoop run_loop;
+  service_->LoadCart(
+      "merchant1.com",
+      base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
+                     base::Unretained(this), run_loop.QuitClosure(), false,
+                     ""));
+  run_loop.Run();
+}
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_stream_md5_digester.cc b/chrome/browser/chromeos/extensions/file_manager/file_stream_md5_digester.cc
index 4c6eb40..3397c42 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_stream_md5_digester.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_stream_md5_digester.cc
@@ -42,8 +42,7 @@
 void FileStreamMd5Digester::ReadNextChunk() {
   const int result =
       reader_->Read(buffer_.get(), kMd5DigestBufferSize,
-                    base::BindOnce(&FileStreamMd5Digester::OnChunkRead,
-                                   base::Unretained(this)));
+                    base::BindOnce(&FileStreamMd5Digester::OnChunkRead, this));
   if (result != net::ERR_IO_PENDING)
     OnChunkRead(result);
 }
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_stream_md5_digester.h b/chrome/browser/chromeos/extensions/file_manager/file_stream_md5_digester.h
index dd0dd43..9516182 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_stream_md5_digester.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_stream_md5_digester.h
@@ -22,7 +22,8 @@
 
 // Computes the (base-16 encoded) MD5 digest of data extracted from a file
 // stream.
-class FileStreamMd5Digester {
+class FileStreamMd5Digester
+    : public base::RefCountedThreadSafe<FileStreamMd5Digester> {
  public:
   using ResultCallback = base::OnceCallback<void(std::string)>;
 
@@ -31,8 +32,6 @@
   FileStreamMd5Digester(const FileStreamMd5Digester&) = delete;
   FileStreamMd5Digester& operator=(const FileStreamMd5Digester&) = delete;
 
-  ~FileStreamMd5Digester();
-
   // Computes an MD5 digest of data read from the given |streamReader|.  The
   // work occurs asynchronously, and the resulting hash is returned via the
   // |callback|.  If an error occurs, |callback| is called with an empty string.
@@ -42,6 +41,9 @@
                     ResultCallback callback);
 
  private:
+  friend class base::RefCountedThreadSafe<FileStreamMd5Digester>;
+  ~FileStreamMd5Digester();
+
   // Kicks off a read of the next chunk from the stream.
   void ReadNextChunk();
   // Handles the incoming chunk of data from a stream read.
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
index 6c0c823..92f9255 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
@@ -1093,7 +1093,7 @@
 
 FileManagerPrivateInternalComputeChecksumFunction::
     FileManagerPrivateInternalComputeChecksumFunction()
-    : digester_(new drive::util::FileStreamMd5Digester()) {}
+    : digester_(base::MakeRefCounted<drive::util::FileStreamMd5Digester>()) {}
 
 FileManagerPrivateInternalComputeChecksumFunction::
     ~FileManagerPrivateInternalComputeChecksumFunction() = default;
@@ -1129,8 +1129,7 @@
           &FileManagerPrivateInternalComputeChecksumFunction::RespondWith,
           this));
   content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(&FileStreamMd5Digester::GetMd5Digest,
-                                base::Unretained(digester_.get()),
+      FROM_HERE, base::BindOnce(&FileStreamMd5Digester::GetMd5Digest, digester_,
                                 std::move(reader), std::move(result_callback)));
 
   return RespondLater();
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
index 2ca6bc5..ad9e7c9 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
@@ -348,7 +348,7 @@
   ResponseAction Run() override;
 
  private:
-  std::unique_ptr<drive::util::FileStreamMd5Digester> digester_;
+  scoped_refptr<drive::util::FileStreamMd5Digester> digester_;
 
   void RespondWith(std::string hash);
 };
diff --git a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.cc b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.cc
index 4d7881a..3e64abe 100644
--- a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager.cc
@@ -593,6 +593,8 @@
                 IDS_DEVICE_UNSUPPORTED_MESSAGE,
                 base::UTF8ToUTF16(volume.drive_label()));
           }
+          RecordDeviceNotificationMetric(
+              DeviceNotificationUmaType::DEVICE_FAIL);
         } else {
           if (volume.drive_label().empty()) {
             message =
@@ -606,6 +608,11 @@
             // Give a format device button on the notification.
             notification_buttons.push_back(message_center::ButtonInfo(
                 l10n_util::GetStringUTF16(IDS_DEVICE_UNKNOWN_BUTTON_LABEL)));
+            RecordDeviceNotificationMetric(
+                DeviceNotificationUmaType::DEVICE_FAIL_UNKNOWN);
+          } else {
+            RecordDeviceNotificationMetric(
+                DeviceNotificationUmaType::DEVICE_FAIL_UNKNOWN_READONLY);
           }
         }
         break;
@@ -620,6 +627,7 @@
               IDS_MULTIPART_DEVICE_UNSUPPORTED_MESSAGE,
               base::UTF8ToUTF16(volume.drive_label()));
         }
+        RecordDeviceNotificationMetric(DeviceNotificationUmaType::DEVICE_FAIL);
         break;
       default:
         DLOG(WARNING) << "Unhandled mount status for "
diff --git a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc
index cc7a03b..6cb07f9 100644
--- a/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/system_notification_manager_unittest.cc
@@ -551,6 +551,7 @@
 // method. Both parent and child unknown volume filesystems generate
 // the same nofication.
 TEST_F(SystemNotificationManagerTest, DeviceUnsupportedDefault) {
+  base::HistogramTester histogram_tester;
   std::unique_ptr<Volume> volume(Volume::CreateForTesting(
       base::FilePath(FILE_PATH_LITERAL("/mount/path1")),
       VolumeType::VOLUME_TYPE_TESTING, chromeos::DeviceType::DEVICE_TYPE_USB,
@@ -580,11 +581,16 @@
   EXPECT_EQ(
       notification_strings.message,
       u"Sorry, your external storage device is not supported at this time.");
+  // Check that the correct UMA was emitted.
+  histogram_tester.ExpectUniqueSample(kNotificationShowHistogramName,
+                                      DeviceNotificationUmaType::DEVICE_FAIL,
+                                      1);
 }
 
 // The named version of the device unsupported notification is
 // generated when the device includes a device label.
 TEST_F(SystemNotificationManagerTest, DeviceUnsupportedNamed) {
+  base::HistogramTester histogram_tester;
   std::unique_ptr<Volume> volume(Volume::CreateForTesting(
       base::FilePath(FILE_PATH_LITERAL("/mount/path1")),
       VolumeType::VOLUME_TYPE_TESTING, chromeos::DeviceType::DEVICE_TYPE_USB,
@@ -613,6 +619,10 @@
   EXPECT_EQ(notification_strings.title, kRemovableDeviceTitle);
   EXPECT_EQ(notification_strings.message,
             u"Sorry, the device MyUSB is not supported at this time.");
+  // Check that the correct UMA was emitted.
+  histogram_tester.ExpectUniqueSample(kNotificationShowHistogramName,
+                                      DeviceNotificationUmaType::DEVICE_FAIL,
+                                      1);
 }
 
 // Multipart device unsupported notifications are generated when there is
@@ -622,6 +632,7 @@
 //       1) A device navigation notification for the supported file system
 //       2) The multipart device unsupported notification.
 TEST_F(SystemNotificationManagerTest, MultipartDeviceUnsupportedDefault) {
+  base::HistogramTester histogram_tester;
   // Build a supported file system volume and mount it.
   std::unique_ptr<Volume> volume1(Volume::CreateForTesting(
       base::FilePath(FILE_PATH_LITERAL("/mount/path1")),
@@ -675,12 +686,17 @@
   EXPECT_EQ(notification_strings.message,
             u"Sorry, at least one partition on your external storage device "
             u"could not be mounted.");
+  // A DEVICE_NAVIGATION UMA is emitted during the setup so just check for the
+  // occurrence of the DEVICE_FAIL sample instead.
+  histogram_tester.ExpectBucketCount(kNotificationShowHistogramName,
+                                     DeviceNotificationUmaType::DEVICE_FAIL, 1);
 }
 
 // The named version of the multipart device unsupported notification is
 // generated when the device label exists and at least one partition can be
 // mounted on a device with an unsupported file system on another partition.
 TEST_F(SystemNotificationManagerTest, MultipartDeviceUnsupportedNamed) {
+  base::HistogramTester histogram_tester;
   // Build a supported file system volume and mount it.
   std::unique_ptr<Volume> volume1(Volume::CreateForTesting(
       base::FilePath(FILE_PATH_LITERAL("/mount/path1")),
@@ -720,6 +736,10 @@
   EXPECT_EQ(notification_strings.message,
             u"Sorry, at least one partition on the device MyUSB could not be "
             u"mounted.");
+  // A DEVICE_NAVIGATION UMA is emitted during the setup so just check for the
+  // occurrence of the DEVICE_FAIL sample instead.
+  histogram_tester.ExpectBucketCount(kNotificationShowHistogramName,
+                                     DeviceNotificationUmaType::DEVICE_FAIL, 1);
 }
 
 // Device fail unknown notifications are generated when the type of filesystem
@@ -729,6 +749,7 @@
 // These notifications are similar to the device unsupported notifications,
 // the difference being an unknown vs. unsupported file system.
 TEST_F(SystemNotificationManagerTest, DeviceFailUnknownDefault) {
+  base::HistogramTester histogram_tester;
   std::unique_ptr<Volume> volume(Volume::CreateForTesting(
       base::FilePath(FILE_PATH_LITERAL("/mount/path1")),
       VolumeType::VOLUME_TYPE_TESTING, chromeos::DeviceType::DEVICE_TYPE_USB,
@@ -759,11 +780,15 @@
             u"Sorry, your external storage device could not be recognized.");
   EXPECT_EQ(notification_strings.buttons.size(), 1);
   EXPECT_EQ(notification_strings.buttons[0], u"Format this device");
+  histogram_tester.ExpectUniqueSample(
+      kNotificationShowHistogramName,
+      DeviceNotificationUmaType::DEVICE_FAIL_UNKNOWN, 1);
 }
 
 // The named version of the device fail unknown notification is
 // generated when the device includes a device label.
 TEST_F(SystemNotificationManagerTest, DeviceFailUnknownNamed) {
+  base::HistogramTester histogram_tester;
   std::unique_ptr<Volume> volume(Volume::CreateForTesting(
       base::FilePath(FILE_PATH_LITERAL("/mount/path1")),
       VolumeType::VOLUME_TYPE_TESTING, chromeos::DeviceType::DEVICE_TYPE_USB,
@@ -794,6 +819,9 @@
             u"Sorry, the device MyUSB could not be recognized.");
   EXPECT_EQ(notification_strings.buttons.size(), 1);
   EXPECT_EQ(notification_strings.buttons[0], u"Format this device");
+  histogram_tester.ExpectUniqueSample(
+      kNotificationShowHistogramName,
+      DeviceNotificationUmaType::DEVICE_FAIL_UNKNOWN, 1);
 }
 
 // Device fail unknown read only notifications are generated when
@@ -801,6 +829,7 @@
 // The default notification message is generated when there is
 // no device label.
 TEST_F(SystemNotificationManagerTest, DeviceFailUnknownReadOnlyDefault) {
+  base::HistogramTester histogram_tester;
   std::unique_ptr<Volume> volume(Volume::CreateForTesting(
       base::FilePath(FILE_PATH_LITERAL("/mount/path1")),
       VolumeType::VOLUME_TYPE_TESTING, chromeos::DeviceType::DEVICE_TYPE_USB,
@@ -831,11 +860,15 @@
             u"Sorry, your external storage device could not be recognized.");
   // Device is read-only, expect no buttons present.
   EXPECT_EQ(notification_strings.buttons.size(), 0);
+  histogram_tester.ExpectUniqueSample(
+      kNotificationShowHistogramName,
+      DeviceNotificationUmaType::DEVICE_FAIL_UNKNOWN_READONLY, 1);
 }
 
 // The named version of the read only device fail unknown notification is
 // generated when the device includes a device label.
 TEST_F(SystemNotificationManagerTest, DeviceFailUnknownReadOnlyNamed) {
+  base::HistogramTester histogram_tester;
   std::unique_ptr<Volume> volume(Volume::CreateForTesting(
       base::FilePath(FILE_PATH_LITERAL("/mount/path1")),
       VolumeType::VOLUME_TYPE_TESTING, chromeos::DeviceType::DEVICE_TYPE_USB,
@@ -864,6 +897,9 @@
   EXPECT_EQ(notification_strings.title, kRemovableDeviceTitle);
   EXPECT_EQ(notification_strings.message,
             u"Sorry, the device MyUSB could not be recognized.");
+  histogram_tester.ExpectUniqueSample(
+      kNotificationShowHistogramName,
+      DeviceNotificationUmaType::DEVICE_FAIL_UNKNOWN_READONLY, 1);
 }
 
 TEST_F(SystemNotificationManagerTest, TestCopyEvents) {
diff --git a/chrome/browser/commerce/price_tracking/android/BUILD.gn b/chrome/browser/commerce/price_tracking/android/BUILD.gn
deleted file mode 100644
index d9a37ffc..0000000
--- a/chrome/browser/commerce/price_tracking/android/BUILD.gn
+++ /dev/null
@@ -1,9 +0,0 @@
-# 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.
-
-import("//build/config/android/rules.gni")
-
-generate_jni("jni_headers") {
-  sources = [ "java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridge.java" ]
-}
diff --git a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifier.java b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifier.java
index 28e83d4a..fe9d3cb 100644
--- a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifier.java
+++ b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifier.java
@@ -89,7 +89,7 @@
     }
 
     private final Context mContext;
-    private ImageFetcher mImageFetcher;
+    private final ImageFetcher mImageFetcher;
     private final NotificationWrapperBuilder mNotificationBuilder;
     private final NotificationManagerProxy mNotificationManagerProxy;
     private final PriceDropNotificationManager mPriceDropNotificationManager;
@@ -99,19 +99,24 @@
      * @param context The Android context.
      */
     public static PriceDropNotifier create(Context context) {
+        ImageFetcher imageFetcher =
+                ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.NETWORK_ONLY,
+                        Profile.getLastUsedRegularProfile().getProfileKey());
         NotificationWrapperBuilder notificationBuilder =
                 NotificationWrapperBuilderFactory.createNotificationWrapperBuilder(
                         ChannelId.PRICE_DROP,
                         new NotificationMetadata(SystemNotificationType.PRICE_DROP_ALERTS,
                                 NOTIFICATION_TAG, NOTIFICATION_ID));
-        return new PriceDropNotifier(
-                context, notificationBuilder, new NotificationManagerProxyImpl(context));
+        return new PriceDropNotifier(context, imageFetcher, notificationBuilder,
+                new NotificationManagerProxyImpl(context));
     }
 
     @VisibleForTesting
-    PriceDropNotifier(Context context, NotificationWrapperBuilder notificationBuilder,
+    PriceDropNotifier(Context context, ImageFetcher imageFetcher,
+            NotificationWrapperBuilder notificationBuilder,
             NotificationManagerProxy notificationManager) {
         mContext = context;
+        mImageFetcher = imageFetcher;
         mNotificationBuilder = notificationBuilder;
         mNotificationManagerProxy = notificationManager;
         mPriceDropNotificationManager =
@@ -126,15 +131,6 @@
         maybeFetchIcon(notificationData, bitmap -> { showWithIcon(notificationData, bitmap); });
     }
 
-    @VisibleForTesting
-    protected ImageFetcher getImageFetcher() {
-        if (mImageFetcher == null) {
-            mImageFetcher = ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.NETWORK_ONLY,
-                    Profile.getLastUsedRegularProfile().getProfileKey());
-        }
-        return mImageFetcher;
-    }
-
     private void maybeFetchIcon(
             final NotificationData notificationData, Callback<Bitmap> callback) {
         if (notificationData.iconUrl == null) {
@@ -144,7 +140,7 @@
 
         ImageFetcher.Params params = ImageFetcher.Params.create(
                 notificationData.iconUrl, ImageFetcher.PRICE_DROP_NOTIFICATION);
-        getImageFetcher().fetchImage(params, bitmap -> { callback.onResult(bitmap); });
+        mImageFetcher.fetchImage(params, bitmap -> { callback.onResult(bitmap); });
     }
 
     private void showWithIcon(NotificationData notificationData, @Nullable Bitmap icon) {
diff --git a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridge.java b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridge.java
deleted file mode 100644
index d2064df..0000000
--- a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridge.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// 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.
-
-package org.chromium.chrome.browser.price_tracking;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.google.protobuf.ByteString;
-import com.google.protobuf.InvalidProtocolBufferException;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.chrome.browser.price_tracking.PriceDropNotifier.ActionData;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.ChromeMessage;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.ChromeNotification;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.ChromeNotification.NotificationDataType;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.ExpandedView;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.PriceDropNotificationPayload;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class to show a price tracking notification. The Java object is owned by the native side
- * PriceTrackingNotificationBridge object through JNI bridge.
- */
-public class PriceTrackingNotificationBridge {
-    private static final String TAG = "PriceTrackNotif";
-    private final long mNativePriceTrackingNotificationBridge;
-    private final PriceDropNotifier mNotifier;
-
-    /**
-     * Construct a {@link PriceTrackingNotificationBridge} object from native code.
-     * @param nativePriceTrackingNotificationBridge The native JNI object pointer.
-     */
-    @VisibleForTesting
-    PriceTrackingNotificationBridge(
-            long nativePriceTrackingNotificationBridge, PriceDropNotifier notifier) {
-        mNativePriceTrackingNotificationBridge = nativePriceTrackingNotificationBridge;
-        mNotifier = notifier;
-    }
-
-    @CalledByNative
-    private static PriceTrackingNotificationBridge create(
-            long nativePriceTrackingNotificationBridge) {
-        return new PriceTrackingNotificationBridge(nativePriceTrackingNotificationBridge,
-                PriceDropNotifier.create(ContextUtils.getApplicationContext()));
-    }
-
-    @VisibleForTesting
-    @CalledByNative
-    void showNotification(byte[] payload) {
-        ChromeNotification chromeNotification;
-        try {
-            chromeNotification = ChromeNotification.parseFrom(payload);
-        } catch (InvalidProtocolBufferException e) {
-            Log.e(TAG, "Failed to parse ChromeNotification payload.");
-            return;
-        }
-
-        if (!chromeNotification.hasChromeMessage()) return;
-        ChromeMessage chromeMessage = chromeNotification.getChromeMessage();
-        if (!chromeMessage.hasDestinationUrl()) return;
-
-        // TODO(xingliu): Use client strings for display text, title, and action text.
-        // Show the notification.
-        PriceDropNotifier.NotificationData notificationData =
-                new PriceDropNotifier.NotificationData(chromeMessage.getDisplayTitle(),
-                        chromeMessage.getDisplayText(),
-                        chromeMessage.hasIconImageUrl() ? chromeMessage.getIconImageUrl() : null,
-                        chromeMessage.getDestinationUrl(), parseOfferId(chromeNotification),
-                        parseActions(chromeNotification));
-        mNotifier.showNotification(notificationData);
-    }
-
-    private static String parseOfferId(ChromeNotification chromeNotification) {
-        PriceDropNotificationPayload priceDropPayload = null;
-        Long offerId = null;
-        if (chromeNotification.hasNotificationData()
-                && chromeNotification.hasNotificationDataType()) {
-            if (chromeNotification.getNotificationDataType()
-                    == NotificationDataType.PRICE_DROP_NOTIFICATION) {
-                priceDropPayload = parsePriceDropPayload(chromeNotification.getNotificationData());
-            }
-        }
-        if (priceDropPayload != null && priceDropPayload.hasOfferId()) {
-            offerId = priceDropPayload.getOfferId();
-        }
-        // TODO(crbug.com/1257380): Figure out how to serialize offer id correctly.
-        return offerId != null ? String.valueOf(offerId) : null;
-    }
-
-    private static PriceDropNotificationPayload parsePriceDropPayload(ByteString payload) {
-        PriceDropNotificationPayload priceDropPayload = null;
-        try {
-            priceDropPayload = PriceDropNotificationPayload.parseFrom(payload);
-        } catch (InvalidProtocolBufferException e) {
-            Log.e(TAG, "Failed to parse PriceDropNotificationPayload.");
-        }
-        return priceDropPayload;
-    }
-
-    private static List<PriceDropNotifier.ActionData> parseActions(
-            ChromeNotification chromeNotification) {
-        List<PriceDropNotifier.ActionData> actions = new ArrayList<>();
-        if (!chromeNotification.hasChromeMessage()) return actions;
-        ChromeMessage chromeMessage = chromeNotification.getChromeMessage();
-        if (!chromeMessage.hasExpandedView()) return actions;
-        ExpandedView expandedView = chromeMessage.getExpandedView();
-        for (Notifications.Action action : expandedView.getActionList()) {
-            if (!action.hasActionId() || !action.hasText()) continue;
-            actions.add(new ActionData(action.getActionId(), action.getText()));
-        }
-        return actions;
-    }
-}
diff --git a/chrome/browser/commerce/price_tracking/android/java_sources.gni b/chrome/browser/commerce/price_tracking/android/java_sources.gni
index 5f3fb55..f9b2641 100644
--- a/chrome/browser/commerce/price_tracking/android/java_sources.gni
+++ b/chrome/browser/commerce/price_tracking/android/java_sources.gni
@@ -5,5 +5,4 @@
 price_tracking_java_sources = [
   "//chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManager.java",
   "//chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifier.java",
-  "//chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridge.java",
 ]
diff --git a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java b/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java
index e7bbab2c..259d4966 100644
--- a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java
+++ b/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java
@@ -12,7 +12,6 @@
 import static org.mockito.Mockito.when;
 
 import android.app.PendingIntent;
-import android.content.Context;
 import android.graphics.Bitmap;
 
 import androidx.annotation.Nullable;
@@ -96,22 +95,6 @@
         }
     }
 
-    static class TestPriceDropNotifier extends PriceDropNotifier {
-        private final ImageFetcher mMockImageFetcher;
-
-        TestPriceDropNotifier(Context context, ImageFetcher imageFetcher,
-                NotificationWrapperBuilder notificationBuilder,
-                NotificationManagerProxy notificationManager) {
-            super(context, notificationBuilder, notificationManager);
-            mMockImageFetcher = imageFetcher;
-        }
-
-        @Override
-        protected ImageFetcher getImageFetcher() {
-            return mMockImageFetcher;
-        }
-    }
-
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
@@ -137,7 +120,7 @@
     @Before
     public void setUp() {
         ShadowLog.stream = System.out;
-        mPriceDropNotifier = new TestPriceDropNotifier(ContextUtils.getApplicationContext(),
+        mPriceDropNotifier = new PriceDropNotifier(ContextUtils.getApplicationContext(),
                 mImageFetcher, mNotificationBuilder, mNotificationManagerProxy);
         ChromeBrowserInitializer.setForTesting(mChromeInitializer);
         when(mNotificationBuilder.buildNotificationWrapper()).thenReturn(mNotificationWrapper);
diff --git a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridgeUnitTest.java b/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridgeUnitTest.java
deleted file mode 100644
index 0a148e30..0000000
--- a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridgeUnitTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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.
-
-package org.chromium.chrome.browser.price_tracking;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowLog;
-
-import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.price_tracking.PriceDropNotifier.NotificationData;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.Action;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.ChromeMessage;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.ChromeNotification;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.ChromeNotification.NotificationDataType;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.ExpandedView;
-import org.chromium.chrome.browser.price_tracking.proto.Notifications.PriceDropNotificationPayload;
-
-/**
- * Unit test for {@link PriceDropNotifier}.
- */
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class PriceTrackingNotificationBridgeUnitTest {
-    private static final String TITLE = "title";
-    private static final String TEXT = "text";
-    private static final String ICON_URL = "http://www.example.com/icon";
-    private static final String DESTINATION_URL = "http://www.example.com/destination";
-    private static final long OFFER_ID = 10L;
-    private static final String ACTION_ID_0 = "action_id_0";
-    private static final String ACTION_ID_1 = "action_id_1";
-    private static final String ACTION_TEXT_0 = "action_text_0";
-    private static final String ACTION_TEXT_1 = "action_text_1";
-
-    private PriceTrackingNotificationBridge mPriceTrackingNotificationBridge;
-
-    @Rule
-    public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
-    @Mock
-    PriceDropNotifier mNotifier;
-
-    @Captor
-    ArgumentCaptor<PriceDropNotifier.NotificationData> mNotificationDataCaptor;
-
-    @Before
-    public void setUp() {
-        ShadowLog.stream = System.out;
-        mPriceTrackingNotificationBridge = new PriceTrackingNotificationBridge(0, mNotifier);
-    }
-
-    // Creates a ChromeNotification.Builder that sets a valid ChromeNotification proto.
-    private ChromeNotification.Builder createValidPayload() {
-        return createPayloadWithChromeMessage(createValidChromeMessage());
-    }
-
-    // Create a ChromeNotification.Builder with specific ChromeMessage.
-    private ChromeNotification.Builder createPayloadWithChromeMessage(
-            ChromeMessage.Builder chromeMessage) {
-        ChromeNotification.Builder builder = ChromeNotification.newBuilder();
-        builder.setNotificationDataType(NotificationDataType.PRICE_DROP_NOTIFICATION);
-
-        // Only offer id is used in unido code path.
-        PriceDropNotificationPayload.Builder priceDropPayload =
-                PriceDropNotificationPayload.newBuilder();
-        priceDropPayload.setOfferId(OFFER_ID);
-        builder.setNotificationData(priceDropPayload.build().toByteString());
-        builder.setChromeMessage(chromeMessage.build());
-        return builder;
-    }
-
-    // Create a valid ChromeMessage proto builder.
-    private ChromeMessage.Builder createValidChromeMessage() {
-        // Create ChromeMessage, some fields are duplicated to PriceDropNotificationPayload,
-        ChromeMessage.Builder message = ChromeMessage.newBuilder();
-        message.setDisplayTitle(TITLE);
-        message.setDisplayText(TEXT);
-        message.setIconImageUrl(ICON_URL);
-        message.setDestinationUrl(DESTINATION_URL);
-        ExpandedView.Builder expandedView = ExpandedView.newBuilder();
-        expandedView.addAction(Action.newBuilder().setActionId(ACTION_ID_0).setText(ACTION_TEXT_0));
-        expandedView.addAction(Action.newBuilder().setActionId(ACTION_ID_1).setText(ACTION_TEXT_1));
-        message.setExpandedView(expandedView.build());
-        return message;
-    }
-
-    @Test
-    public void testShowNotification_ValidProto() {
-        mPriceTrackingNotificationBridge.showNotification(
-                createValidPayload().build().toByteArray());
-        verify(mNotifier).showNotification(mNotificationDataCaptor.capture());
-        NotificationData data = mNotificationDataCaptor.getValue();
-        Assert.assertEquals(TITLE, data.title);
-        Assert.assertEquals(TEXT, data.text);
-        Assert.assertEquals(ICON_URL, data.iconUrl);
-        Assert.assertEquals(DESTINATION_URL, data.destinationUrl);
-        Assert.assertEquals(ACTION_ID_0, data.actions.get(0).actionId);
-        Assert.assertEquals(ACTION_TEXT_0, data.actions.get(0).text);
-        Assert.assertEquals(ACTION_ID_1, data.actions.get(1).actionId);
-        Assert.assertEquals(ACTION_TEXT_1, data.actions.get(1).text);
-        Assert.assertEquals(String.valueOf(OFFER_ID), data.offerId);
-    }
-
-    @Test
-    public void testShowNotification_NoChromeMessage() {
-        ChromeNotification.Builder builder = createValidPayload();
-        builder.clearChromeMessage();
-        mPriceTrackingNotificationBridge.showNotification(builder.build().toByteArray());
-        verify(mNotifier, times(0)).showNotification(any());
-    }
-
-    @Test
-    public void testShowNotification_NoDestinationURL() {
-        ChromeMessage.Builder chromeMessage = createValidChromeMessage();
-        chromeMessage.clearDestinationUrl();
-        ChromeNotification.Builder builder = createPayloadWithChromeMessage(chromeMessage);
-        mPriceTrackingNotificationBridge.showNotification(builder.build().toByteArray());
-        verify(mNotifier, times(0)).showNotification(any());
-    }
-}
diff --git a/chrome/browser/commerce/price_tracking/android/price_tracking_notification_bridge.cc b/chrome/browser/commerce/price_tracking/android/price_tracking_notification_bridge.cc
deleted file mode 100644
index 456c97b..0000000
--- a/chrome/browser/commerce/price_tracking/android/price_tracking_notification_bridge.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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/commerce/price_tracking/android/price_tracking_notification_bridge.h"
-
-#include "base/android/jni_array.h"
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/commerce/price_tracking/android/jni_headers/PriceTrackingNotificationBridge_jni.h"
-#include "content/public/browser/browser_context.h"
-
-using OptimizationType = optimization_guide::proto::OptimizationType;
-
-const char kUserDataKey[] = "commerce.price_tracking_notification_bridge";
-
-// static
-PriceTrackingNotificationBridge*
-PriceTrackingNotificationBridge::GetForBrowserContext(
-    content::BrowserContext* context) {
-  if (!context->GetUserData(kUserDataKey)) {
-    context->SetUserData(kUserDataKey,
-                         base::WrapUnique(new PriceTrackingNotificationBridge));
-  }
-  return static_cast<PriceTrackingNotificationBridge*>(
-      context->GetUserData(kUserDataKey));
-}
-
-PriceTrackingNotificationBridge::PriceTrackingNotificationBridge() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  java_obj_.Reset(env, Java_PriceTrackingNotificationBridge_create(
-                           env, reinterpret_cast<intptr_t>(this))
-                           .obj());
-}
-
-PriceTrackingNotificationBridge::~PriceTrackingNotificationBridge() = default;
-
-void PriceTrackingNotificationBridge::OnNotificationPayload(
-    optimization_guide::proto::OptimizationType optimization_type,
-    const optimization_guide::proto::Any& payload) {
-  // Only parse PRICE_TRACKING payload.
-  if (optimization_type != OptimizationType::PRICE_TRACKING ||
-      !payload.has_value()) {
-    return;
-  }
-
-  // Pass the payload to Java.
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_PriceTrackingNotificationBridge_showNotification(
-      env, java_obj_, base::android::ToJavaByteArray(env, payload.value()));
-}
diff --git a/chrome/browser/commerce/price_tracking/android/price_tracking_notification_bridge.h b/chrome/browser/commerce/price_tracking/android/price_tracking_notification_bridge.h
deleted file mode 100644
index 4330bfe..0000000
--- a/chrome/browser/commerce/price_tracking/android/price_tracking_notification_bridge.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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 CHROME_BROWSER_COMMERCE_PRICE_TRACKING_ANDROID_PRICE_TRACKING_NOTIFICATION_BRIDGE_H_
-#define CHROME_BROWSER_COMMERCE_PRICE_TRACKING_ANDROID_PRICE_TRACKING_NOTIFICATION_BRIDGE_H_
-
-#include "base/android/scoped_java_ref.h"
-#include "base/supports_user_data.h"
-#include "components/optimization_guide/core/push_notification_manager.h"
-#include "components/optimization_guide/proto/common_types.pb.h"
-#include "components/optimization_guide/proto/push_notification.pb.h"
-
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-// JNI bridge that receives the price tracking notification payload from
-// optimization_guide::PushNotificationManager. This class is owned by a browser
-// context through SupportsUserData.
-class PriceTrackingNotificationBridge
-    : public optimization_guide::PushNotificationManager::Observer,
-      public base::SupportsUserData::Data {
- public:
-  // Get the bridge from a browser context.
-  static PriceTrackingNotificationBridge* GetForBrowserContext(
-      content::BrowserContext* context);
-  ~PriceTrackingNotificationBridge() override;
-
-  // optimization_guide::PushNotificationManager::Observer implementation.
-  void OnNotificationPayload(
-      optimization_guide::proto::OptimizationType optimization_type,
-      const optimization_guide::proto::Any& payload) override;
-
- private:
-  PriceTrackingNotificationBridge();
-
-  // The Java object, owned by the native object.
-  base::android::ScopedJavaGlobalRef<jobject> java_obj_;
-};
-
-#endif  // CHROME_BROWSER_COMMERCE_PRICE_TRACKING_ANDROID_PRICE_TRACKING_NOTIFICATION_BRIDGE_H_
diff --git a/chrome/browser/commerce/price_tracking/android/test_java_sources.gni b/chrome/browser/commerce/price_tracking/android/test_java_sources.gni
index 0663454..7950aa7 100644
--- a/chrome/browser/commerce/price_tracking/android/test_java_sources.gni
+++ b/chrome/browser/commerce/price_tracking/android/test_java_sources.gni
@@ -4,7 +4,4 @@
 
 price_tracking_test_java_sources = [ "//chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManagerTest.java" ]
 
-price_tracking_junit_test_java_sources = [
-  "//chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java",
-  "//chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridgeUnitTest.java",
-]
+price_tracking_junit_test_java_sources = [ "//chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java" ]
diff --git a/chrome/browser/commerce/price_tracking/proto/BUILD.gn b/chrome/browser/commerce/price_tracking/proto/BUILD.gn
index b12177a..8d079a9 100644
--- a/chrome/browser/commerce/price_tracking/proto/BUILD.gn
+++ b/chrome/browser/commerce/price_tracking/proto/BUILD.gn
@@ -5,8 +5,6 @@
 import("//build/config/android/rules.gni")
 import("//third_party/protobuf/proto_library.gni")
 
-proto_java_library("proto_java") {
-  proto_path = "//"
+proto_library("notifications_proto") {
   sources = [ "notifications.proto" ]
-  deps = [ "//components/commerce/core:proto_java" ]
 }
diff --git a/chrome/browser/commerce/price_tracking/proto/notifications.proto b/chrome/browser/commerce/price_tracking/proto/notifications.proto
index a8cf97d..8f07db73 100644
--- a/chrome/browser/commerce/price_tracking/proto/notifications.proto
+++ b/chrome/browser/commerce/price_tracking/proto/notifications.proto
@@ -4,12 +4,10 @@
 
 syntax = "proto2";
 
-package commerce.proto;
+package org.chromium.chrome.browser.price_tracking.proto;
 option optimize_for = LITE_RUNTIME;
 option java_package = "org.chromium.chrome.browser.price_tracking.proto";
 
-import "components/commerce/core/proto/price_tracking.proto";
-
 // Copied from
 // google3/java/com/google/chrome/memex/service/proto/shopping/notifications.proto,
 // must synced with the google3 proto. Contains either a ChromeMessage to
@@ -50,12 +48,3 @@
   optional string action_id = 1;
   optional string text = 2;
 }
-
-// Contains notification metadata for Chrome Price Drop notifications.
-message PriceDropNotificationPayload {
-  optional string product_name = 1;
-  optional uint64 offer_id = 2;
-  optional string destination_url = 3;
-  optional commerce.ProductPrice current_price = 4;
-  optional commerce.ProductPrice previous_price = 5;
-}
diff --git a/chrome/browser/extensions/DEPS b/chrome/browser/extensions/DEPS
index d3aad9a7..3406b3a 100644
--- a/chrome/browser/extensions/DEPS
+++ b/chrome/browser/extensions/DEPS
@@ -22,7 +22,7 @@
   ],
 
   "content_script_apitest.cc": [
-    "+components/web_package/test_support",
+    "+components/web_package",
   ],
   "extension_protocols_unittest\.cc": [
     "+services/network/test",
diff --git a/chrome/browser/extensions/api/DEPS b/chrome/browser/extensions/api/DEPS
index 5a22ab5e..c2f7fbe1 100644
--- a/chrome/browser/extensions/api/DEPS
+++ b/chrome/browser/extensions/api/DEPS
@@ -11,7 +11,7 @@
   ".*test.*": [
     "+chrome/browser/ui/views/frame",
     "+components/captive_portal",
-    "+components/web_package/test_support",
+    "+components/web_package",
     "+skia/public/mojom/bitmap.mojom.h",
   ],
   "tls_socket_unittest\.cc": [
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index 271c7d5..e73d07a3 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -60,7 +60,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/proxy_config/proxy_config_dictionary.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
-#include "components/web_package/test_support/web_bundle_builder.h"
+#include "components/web_package/web_bundle_builder.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -5477,7 +5477,7 @@
   // of a web bundle. So we use |pass_js_url_str| for the fallback URL.
   // TODO(crbug.com/966753): Stop using |pass_js_url_str| when
   // https://github.com/WICG/webpackage/issues/590 is resolved.
-  web_package::test::WebBundleBuilder builder(pass_js_url_str, "");
+  web_package::WebBundleBuilder builder(pass_js_url_str, "");
   auto pass_js_location = builder.AddResponse(
       {{":status", "200"}, {"content-type", "application/javascript"}},
       "document.title = 'script loaded';");
@@ -5550,7 +5550,7 @@
   // of a web bundle. So we use |pass_js_url_str| for the fallback URL.
   // TODO(crbug.com/966753): Stop using |pass_js_url_str| when
   // https://github.com/WICG/webpackage/issues/590 is resolved.
-  web_package::test::WebBundleBuilder builder(pass_js_url, "");
+  web_package::WebBundleBuilder builder(pass_js_url, "");
   auto pass_js_location = builder.AddResponse(
       {{":status", "200"}, {"content-type", "application/javascript"}},
       "document.title = 'script loaded';");
@@ -5629,7 +5629,7 @@
   // of a web bundle. So we use |redirect_js_url_str| for the fallback URL.
   // TODO(crbug.com/966753): Stop using |redirect_js_url_str| when
   // https://github.com/WICG/webpackage/issues/590 is resolved.
-  web_package::test::WebBundleBuilder builder(redirect_js_url_str, "");
+  web_package::WebBundleBuilder builder(redirect_js_url_str, "");
   auto redirect_js_location = builder.AddResponse(
       {{":status", "200"}, {"content-type", "application/javascript"}},
       "document.title = 'redirect';");
@@ -5719,7 +5719,7 @@
 
   // Create a web bundle.
   std::string js_url_str = embedded_test_server()->GetURL("/script.js").spec();
-  web_package::test::WebBundleBuilder builder(js_url_str, "");
+  web_package::WebBundleBuilder builder(js_url_str, "");
   builder.AddExchange(
       js_url_str,
       {{":status", "200"}, {"content-type", "application/javascript"}},
diff --git a/chrome/browser/extensions/api/terminal/terminal_private_api.cc b/chrome/browser/extensions/api/terminal/terminal_private_api.cc
index 71d18a1d..4e7f992 100644
--- a/chrome/browser/extensions/api/terminal/terminal_private_api.cc
+++ b/chrome/browser/extensions/api/terminal/terminal_private_api.cc
@@ -45,6 +45,7 @@
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extensions_browser_client.h"
+#include "ui/display/types/display_constants.h"
 
 namespace terminal_private = extensions::api::terminal_private;
 namespace OnTerminalResize =
@@ -58,6 +59,7 @@
 namespace SendInput = extensions::api::terminal_private::SendInput;
 namespace AckOutput = extensions::api::terminal_private::AckOutput;
 namespace SetSettings = extensions::api::terminal_private::SetSettings;
+namespace OpenWindow = extensions::api::terminal_private::OpenWindow;
 
 using crostini::mojom::InstallerState;
 
@@ -604,7 +606,17 @@
     default;
 
 ExtensionFunction::ResponseAction TerminalPrivateOpenWindowFunction::Run() {
-  crostini::LaunchTerminal(Profile::FromBrowserContext(browser_context()));
+  std::unique_ptr<OpenWindow::Params> params(
+      OpenWindow::Params::Create(args()));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  if (params->data && params->data->url) {
+    crostini::LaunchTerminalWithUrl(
+        Profile::FromBrowserContext(browser_context()),
+        display::kInvalidDisplayId, GURL(*params->data->url));
+  } else {
+    crostini::LaunchTerminal(Profile::FromBrowserContext(browser_context()));
+  }
   return RespondNow(NoArguments());
 }
 
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index c752dfd..c77ebc4 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -70,7 +70,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/proxy_config/proxy_config_dictionary.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
-#include "components/web_package/test_support/web_bundle_builder.h"
+#include "components/web_package/web_bundle_builder.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
@@ -3574,7 +3574,7 @@
   // of a web bundle. So we use |script_url_str| for the fallback URL.
   // TODO(crbug.com/966753): Stop using |script_url_str| when
   // https://github.com/WICG/webpackage/issues/590 is resolved.
-  web_package::test::WebBundleBuilder builder(script_url_str, "");
+  web_package::WebBundleBuilder builder(script_url_str, "");
   auto script_location = builder.AddResponse(
       {{":status", "200"}, {"content-type", "application/javascript"}},
       "document.title = 'ScriptDone';");
@@ -3697,7 +3697,7 @@
       embedded_test_server()->GetURL("/pass.js").spec();
   std::string cancel_js_url_str =
       embedded_test_server()->GetURL("/cancel.js").spec();
-  web_package::test::WebBundleBuilder builder(pass_js_url_str, "");
+  web_package::WebBundleBuilder builder(pass_js_url_str, "");
   auto pass_js_location = builder.AddResponse(
       {{":status", "200"}, {"content-type", "application/javascript"}},
       "document.title = 'script loaded';");
@@ -3821,7 +3821,7 @@
   // Create a web bundle.
   std::string target_txt_url_str =
       embedded_test_server()->GetURL("/target.txt").spec();
-  web_package::test::WebBundleBuilder builder(target_txt_url_str, "");
+  web_package::WebBundleBuilder builder(target_txt_url_str, "");
   auto target_txt_location = builder.AddResponse(
       {{":status", "200"}, {"content-type", "text/plain"}, {"foo", "bar"}},
       "Hello world");
@@ -3924,7 +3924,7 @@
   ASSERT_TRUE(StartEmbeddedTestServer());
 
   // Create a web bundle.
-  web_package::test::WebBundleBuilder builder(uun_uuid_url, "");
+  web_package::WebBundleBuilder builder(uun_uuid_url, "");
   auto uun_uuid_script = builder.AddResponse(
       {{":status", "200"}, {"content-type", "application/javascript"}},
       "document.title = 'loaded';");
@@ -4036,7 +4036,7 @@
       embedded_test_server()->GetURL("/redirected_to_unlisted.js").spec();
   std::string redirect_to_server_js_url_str =
       embedded_test_server()->GetURL("/redirect_to_server.js").spec();
-  web_package::test::WebBundleBuilder builder(redirect_js_url_str, "");
+  web_package::WebBundleBuilder builder(redirect_js_url_str, "");
   auto redirect_js_location = builder.AddResponse(
       {{":status", "200"}, {"content-type", "application/javascript"}},
       "document.title = 'redirect';");
@@ -4132,7 +4132,7 @@
 
   // Create a web bundle.
   std::string js_url_str = embedded_test_server()->GetURL("/script.js").spec();
-  web_package::test::WebBundleBuilder builder(js_url_str, "");
+  web_package::WebBundleBuilder builder(js_url_str, "");
   builder.AddExchange(
       js_url_str,
       {{":status", "200"}, {"content-type", "application/javascript"}},
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc
index 199cdaa..8f5cf2e 100644
--- a/chrome/browser/extensions/content_script_apitest.cc
+++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -31,7 +31,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/javascript_dialogs/tab_modal_dialog_manager.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
-#include "components/web_package/test_support/web_bundle_builder.h"
+#include "components/web_package/web_bundle_builder.h"
 #include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
@@ -1953,7 +1953,7 @@
   // of a web bundle. So we use |urn_uuid_html_url| for the fallback URL.
   // TODO(crbug.com/966753): Stop using |urn_uuid_html_url| when
   // https://github.com/WICG/webpackage/issues/590 is resolved.
-  web_package::test::WebBundleBuilder builder(urn_uuid_html_url, "");
+  web_package::WebBundleBuilder builder(urn_uuid_html_url, "");
   auto html_location =
       builder.AddResponse({{":status", "200"}, {"content-type", "text/html"}},
                           "<script>console.error('hoge');</script>");
diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc
index 0f712d34..9874822a 100644
--- a/chrome/browser/extensions/extension_prefs_unittest.cc
+++ b/chrome/browser/extensions/extension_prefs_unittest.cc
@@ -735,15 +735,6 @@
 
     {
       base::DictionaryValue dictionary;
-      dictionary.SetString(manifest_keys::kName, "from_bookmark");
-      dictionary.SetString(manifest_keys::kVersion, "0.1");
-      dictionary.SetInteger(manifest_keys::kManifestVersion, 2);
-      bookmark_extension_ = prefs_.AddExtensionWithManifestAndFlags(
-          dictionary, ManifestLocation::kInternal, Extension::FROM_BOOKMARK);
-    }
-
-    {
-      base::DictionaryValue dictionary;
       dictionary.SetString(manifest_keys::kName, "was_installed_by_default");
       dictionary.SetString(manifest_keys::kVersion, "0.1");
       dictionary.SetInteger(manifest_keys::kManifestVersion, 2);
@@ -765,18 +756,12 @@
 
   void Verify() override {
     EXPECT_TRUE(prefs()->IsFromWebStore(webstore_extension_->id()));
-    EXPECT_FALSE(prefs()->IsFromBookmark(webstore_extension_->id()));
-
-    EXPECT_TRUE(prefs()->IsFromBookmark(bookmark_extension_->id()));
-    EXPECT_FALSE(prefs()->IsFromWebStore(bookmark_extension_->id()));
-
     EXPECT_TRUE(prefs()->WasInstalledByDefault(default_extension_->id()));
     EXPECT_TRUE(prefs()->WasInstalledByOem(oem_extension_->id()));
   }
 
  private:
   scoped_refptr<Extension> webstore_extension_;
-  scoped_refptr<Extension> bookmark_extension_;
   scoped_refptr<Extension> default_extension_;
   scoped_refptr<Extension> oem_extension_;
 };
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index e5e6afa..5cbc294b 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -1261,8 +1261,6 @@
 // Tests that flags passed to OnExternalExtensionFileFound() make it to the
 // extension object.
 TEST_F(ExtensionServiceTest, InstallingExternalExtensionWithFlags) {
-  const char kPrefFromBookmark[] = "from_bookmark";
-
   InitializeEmptyExtensionService();
 
   base::FilePath path = data_dir().AppendASCII("good.crx");
@@ -1281,12 +1279,10 @@
       registry()->enabled_extensions().GetByID(good_crx);
   ASSERT_TRUE(extension);
   ASSERT_TRUE(extension->from_bookmark());
-  ASSERT_TRUE(ValidateBooleanPref(good_crx, kPrefFromBookmark, true));
 
   // Upgrade to version 2.0, the flag should be preserved.
   path = data_dir().AppendASCII("good2.crx");
   UpdateExtension(good_crx, path, ENABLED);
-  ASSERT_TRUE(ValidateBooleanPref(good_crx, kPrefFromBookmark, true));
   extension = registry()->enabled_extensions().GetByID(good_crx);
   ASSERT_TRUE(extension);
   ASSERT_TRUE(extension->from_bookmark());
@@ -4680,8 +4676,6 @@
 
 TEST_F(ExtensionServiceTest, MAYBE_UpdatingPendingExternalExtensionWithFlags) {
   // Regression test for crbug.com/627522
-  const char kPrefFromBookmark[] = "from_bookmark";
-
   InitializeEmptyExtensionService();
 
   base::FilePath path = data_dir().AppendASCII("good.crx");
@@ -4701,7 +4695,6 @@
   // Upgrade to version 2.0, the flag should be preserved.
   path = data_dir().AppendASCII("good2.crx");
   UpdateExtension(good_crx, path, ENABLED);
-  ASSERT_TRUE(ValidateBooleanPref(good_crx, kPrefFromBookmark, true));
   const Extension* extension =
       registry()->enabled_extensions().GetByID(good_crx);
   ASSERT_TRUE(extension);
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index baad1e43..c3aea1d 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -5411,6 +5411,7 @@
 #endif  // BUILDFLAG(ENABLE_SIDE_SEARCH)
 
 #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
+const char kWebUITabStripFlagId[] = "webui-tab-strip";
 const char kWebUITabStripName[] = "WebUI tab strip";
 const char kWebUITabStripDescription[] =
     "When enabled makes use of a WebUI-based tab strip.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index c940335..d0af920 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -3142,6 +3142,7 @@
 #endif  // BUILDFLAG(ENABLE_SIDE_SEARCH)
 
 #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
+extern const char kWebUITabStripFlagId[];
 extern const char kWebUITabStripName[];
 extern const char kWebUITabStripDescription[];
 
diff --git a/chrome/browser/lacros/browser_service_lacros.cc b/chrome/browser/lacros/browser_service_lacros.cc
index 039911f..d4eff4e 100644
--- a/chrome/browser/lacros/browser_service_lacros.cc
+++ b/chrome/browser/lacros/browser_service_lacros.cc
@@ -86,9 +86,15 @@
 void BrowserServiceLacros::NewFullscreenWindow(
     const GURL& url,
     NewFullscreenWindowCallback callback) {
-  // TODO(anqing): refactor the following window control logic and make it
-  // shared by both lacros and ash chrome.
+  // Get the current user profile. Report an error to ash if it doesn't exist.
   Profile* profile = ProfileManager::GetLastUsedProfileAllowedByPolicy();
+  if (!profile) {
+    std::move(callback).Run(crosapi::mojom::CreationResult::kProfileNotExist);
+    return;
+  }
+
+  // Launch a fullscreen window with the user profile, and navigate to the
+  // target URL.
   Browser::CreateParams params = Browser::CreateParams::CreateForApp(
       "app_name", true, gfx::Rect(), profile, false);
   params.initial_show_state = ui::SHOW_STATE_FULLSCREEN;
@@ -96,8 +102,14 @@
   NavigateParams nav_params(browser, url,
                             ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL);
   Navigate(&nav_params);
-  CHECK(browser);
-  CHECK(browser->window());
+
+  // Verify the creation result of browser window.
+  if (!browser || !browser->window()) {
+    std::move(callback).Run(
+        crosapi::mojom::CreationResult::kBrowserWindowUnavailable);
+    return;
+  }
+
   browser->window()->Show();
 
   // TODO(crbug/1247638): we'd better figure out a better solution to move this
@@ -107,8 +119,9 @@
     KioskSessionServiceLacros::Get()->InitWebKioskSession(browser);
   }
 
-  // TODO(anqing): valicate current profile and window status, and return
-  // non-success result if anything is wrong.
+  // Report a success result to ash. Please note that showing Lacros window is
+  // asynchronous. Ash-chrome should use the `exo::WMHelper` class rather than
+  // this callback method call to track window creation status.
   std::move(callback).Run(crosapi::mojom::CreationResult::kSuccess);
 }
 
diff --git a/chrome/browser/lifetime/application_lifetime.cc b/chrome/browser/lifetime/application_lifetime.cc
index 782604ed..894b9fa 100644
--- a/chrome/browser/lifetime/application_lifetime.cc
+++ b/chrome/browser/lifetime/application_lifetime.cc
@@ -163,6 +163,7 @@
 
   PrefService* pref_service = g_browser_process->local_state();
   pref_service->SetBoolean(prefs::kWasRestarted, true);
+  KeepAliveRegistry::GetInstance()->SetRestarting();
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   chromeos::BootTimesRecorder::Get()->set_restart_requested();
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index 74aeb2ab..d602d24 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -12,10 +12,6 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "base/files/file_util.h"
 #include "base/system/sys_info.h"
-#include "chrome/browser/download/download_prefs.h"
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chrome/common/chrome_paths.h"
 #endif
 
@@ -83,10 +79,6 @@
   if (base::PathService::Get(base::DIR_TEMP, &temp_dir))
     allowlist.push_back(temp_dir);
 
-  // For developers on linux-chromeos, MyFiles dir is at $HOME/Downloads.
-  if (!base::SysInfo::IsRunningOnChromeOS())
-    allowlist.push_back(base::GetHomeDir().Append("Downloads"));
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // The actual location of "/home/chronos/user/Xyz" is the Xyz directory under
   // the profile path ("/home/chronos/user' is a hard link to current primary
@@ -100,10 +92,19 @@
     const base::FilePath webrtc_logs = profile_path.AppendASCII("WebRTC Logs");
     allowlist.push_back(webrtc_logs);
   }
+  // For developers using the linux-chromeos emulator, the MyFiles dir is at
+  // $HOME/Downloads. Ensure developers can access it for manual testing.
+  if (!base::SysInfo::IsRunningOnChromeOS()) {
+    base::FilePath downloads_dir;
+    if (base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_dir))
+      allowlist.push_back(downloads_dir);
+  }
 #else
   // Lacros uses the system-level documents directory and downloads directory
   // under /home/chronos/u-<hash>, which are provided via PathService. Since
   // they are system-level, they are not subdirectories of |profile_path|.
+  // PathService also provides valid paths for developers using the
+  // linux-chromeos emulator.
   base::FilePath documents_dir;
   if (base::PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir))
     allowlist.push_back(documents_dir);
diff --git a/chrome/browser/net/chrome_network_delegate_unittest.cc b/chrome/browser/net/chrome_network_delegate_unittest.cc
index e3074ee..76f489fb 100644
--- a/chrome/browser/net/chrome_network_delegate_unittest.cc
+++ b/chrome/browser/net/chrome_network_delegate_unittest.cc
@@ -16,9 +16,6 @@
 #include "base/system/sys_info.h"
 #include "base/test/scoped_running_on_chromeos.h"
 #include "base/time/time.h"
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chrome/common/chrome_paths.h"
 #endif
 
@@ -86,6 +83,14 @@
   EXPECT_TRUE(IsAccessAllowed("/profile/Downloads", "/profile"));
   EXPECT_TRUE(IsAccessAllowed("/profile/MyFiles", "/profile"));
   EXPECT_TRUE(IsAccessAllowed("/profile/MyFiles/file.pdf", "/profile"));
+  // $HOME/Downloads is allowed for linux-chromeos, but not on devices.
+  base::FilePath downloads_dir;
+  base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_dir);
+  EXPECT_TRUE(IsAccessAllowed(downloads_dir.AsUTF8Unsafe(), ""));
+  {
+    base::test::ScopedRunningOnChromeOS running_on_chromeos;
+    EXPECT_FALSE(IsAccessAllowed(downloads_dir.AsUTF8Unsafe(), ""));
+  }
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -113,14 +118,6 @@
   EXPECT_FALSE(IsAccessAllowed("/profile/GCache/v2", "/profile"));
   EXPECT_FALSE(IsAccessAllowed("/home/chronos/user/GCache/v2/id/Logs", ""));
 
-  // $HOME/Downloads is allowed for linux-chromeos, but not on devices.
-  std::string home_downloads = base::GetHomeDir().Append("Downloads").value();
-  EXPECT_TRUE(IsAccessAllowed(home_downloads, ""));
-  {
-    base::test::ScopedRunningOnChromeOS running_on_chromeos;
-    EXPECT_FALSE(IsAccessAllowed(home_downloads, ""));
-  }
-
 #elif defined(OS_ANDROID)
   // Android allows the following directories.
   EXPECT_TRUE(IsAccessAllowed("/sdcard", ""));
diff --git a/chrome/browser/notifications/arc_application_notifier_controller.cc b/chrome/browser/notifications/arc_application_notifier_controller.cc
index e9dedf65..ec593e2 100644
--- a/chrome/browser/notifications/arc_application_notifier_controller.cc
+++ b/chrome/browser/notifications/arc_application_notifier_controller.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/notifications/notifier_dataset.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/services/app_service/public/cpp/app_update.h"
+#include "components/services/app_service/public/cpp/permission_utils.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "ui/message_center/public/cpp/message_center_constants.h"
 #include "ui/message_center/public/cpp/notifier_id.h"
@@ -52,7 +53,7 @@
           apps::mojom::PermissionType::kNotifications) {
         continue;
       }
-      DCHECK(permission->value_type == apps::mojom::PermissionValueType::kBool);
+      DCHECK(permission->value->is_bool_value());
       // Do not include notifier metadata for system apps.
       if (update.InstallReason() == apps::mojom::InstallReason::kSystem) {
         return;
@@ -60,7 +61,7 @@
       notifier_dataset.push_back(NotifierDataset{
           update.AppId() /*app_id*/, update.ShortName() /*app_name*/,
           update.PublisherId() /*publisher_id*/,
-          !!permission->value /*enabled*/});
+          permission->value->get_bool_value() /*enabled*/});
     }
   });
 
@@ -92,8 +93,8 @@
   last_used_profile_ = profile;
   auto permission = apps::mojom::Permission::New();
   permission->permission_type = apps::mojom::PermissionType::kNotifications;
-  permission->value_type = apps::mojom::PermissionValueType::kBool;
-  permission->value = enabled;
+  permission->value = apps::mojom::PermissionValue::New();
+  permission->value->set_bool_value(enabled);
   permission->is_managed = false;
   apps::AppServiceProxy* service =
       apps::AppServiceProxyFactory::GetForProfile(profile);
@@ -147,7 +148,8 @@
           apps::mojom::PermissionType::kNotifications) {
         message_center::NotifierId notifier_id(
             message_center::NotifierType::ARC_APPLICATION, update.AppId());
-        observer_->OnNotifierEnabledChanged(notifier_id, permission->value);
+        observer_->OnNotifierEnabledChanged(
+            notifier_id, apps_util::IsPermissionEnabled(permission->value));
       }
     }
   }
diff --git a/chrome/browser/notifications/pwa_notifier_controller.cc b/chrome/browser/notifications/pwa_notifier_controller.cc
index 85b4b1b..f812b2c 100644
--- a/chrome/browser/notifications/pwa_notifier_controller.cc
+++ b/chrome/browser/notifications/pwa_notifier_controller.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/notifications/notifier_dataset.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/services/app_service/public/cpp/app_update.h"
+#include "components/services/app_service/public/cpp/permission_utils.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "ui/message_center/public/cpp/message_center_constants.h"
 #include "ui/message_center/public/cpp/notifier_id.h"
@@ -44,8 +45,7 @@
               apps::mojom::PermissionType::kNotifications) {
             continue;
           }
-          DCHECK(permission->value_type ==
-                 apps::mojom::PermissionValueType::kTriState);
+          DCHECK(permission->value->is_tristate_value());
           // Do not include notifier metadata for system apps.
           if (update.InstallReason() == apps::mojom::InstallReason::kSystem) {
             return;
@@ -53,10 +53,7 @@
           notifier_dataset.push_back(NotifierDataset{
               update.AppId() /*app_id*/, update.ShortName() /*app_name*/,
               update.PublisherId() /*publisher_id*/,
-              static_cast<apps::mojom::TriState>(permission->value) ==
-                      apps::mojom::TriState::kAllow
-                  ? true
-                  : false /*enabled*/});
+              apps_util::IsPermissionEnabled(permission->value)});
         }
       });
   std::vector<ash::NotifierMetadata> notifiers;
@@ -89,8 +86,8 @@
   DCHECK(observed_profile_->IsSameOrParent(profile));
   auto permission = apps::mojom::Permission::New();
   permission->permission_type = apps::mojom::PermissionType::kNotifications;
-  permission->value_type = apps::mojom::PermissionValueType::kTriState;
-  permission->value = static_cast<uint32_t>(
+  permission->value = apps::mojom::PermissionValue::New();
+  permission->value->set_tristate_value(
       enabled ? apps::mojom::TriState::kAllow : apps::mojom::TriState::kBlock);
   permission->is_managed = false;
   apps::AppServiceProxy* service =
@@ -142,7 +139,8 @@
           apps::mojom::PermissionType::kNotifications) {
         message_center::NotifierId notifier_id(
             message_center::NotifierType::APPLICATION, update.AppId());
-        observer_->OnNotifierEnabledChanged(notifier_id, permission->value);
+        observer_->OnNotifierEnabledChanged(
+            notifier_id, apps_util::IsPermissionEnabled(permission->value));
       }
     }
   }
diff --git a/chrome/browser/optimization_guide/android/android_push_notification_manager_unittest.cc b/chrome/browser/optimization_guide/android/android_push_notification_manager_unittest.cc
index 3ad4f5ca..162b1264 100644
--- a/chrome/browser/optimization_guide/android/android_push_notification_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/android/android_push_notification_manager_unittest.cc
@@ -112,8 +112,6 @@
     ASSERT_TRUE(profile_manager_.SetUp(temp_dir_.GetPath()));
     profile_ = profile_manager_.CreateTestingProfile(chrome::kInitialProfile);
 
-    Java_OptimizationGuidePushNotificationTestHelper_setUpMocks(env_, j_test_);
-
     service_ = static_cast<OptimizationGuideKeyedService*>(
         OptimizationGuideKeyedServiceFactory::GetInstance()
             ->SetTestingFactoryAndUse(
@@ -124,6 +122,8 @@
     service_->GetHintsManager()->push_notification_manager()->AddObserver(
         &observer_);
 
+    Java_OptimizationGuidePushNotificationTestHelper_setUpMocks(env_, j_test_);
+
     // It takes two session starts for experimental params and feature flags to
     // be picked up by Java, so override them manually.
     Java_OptimizationGuidePushNotificationTestHelper_setOverflowSizeForTesting(
diff --git a/chrome/browser/optimization_guide/chrome_hints_manager.cc b/chrome/browser/optimization_guide/chrome_hints_manager.cc
index bcd354e..63eec4e 100644
--- a/chrome/browser/optimization_guide/chrome_hints_manager.cc
+++ b/chrome/browser/optimization_guide/chrome_hints_manager.cc
@@ -19,7 +19,6 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #if defined(OS_ANDROID)
-#include "chrome/browser/commerce/price_tracking/android/price_tracking_notification_bridge.h"
 #include "chrome/browser/optimization_guide/android/android_push_notification_manager.h"
 #endif
 
@@ -27,18 +26,12 @@
 
 // Creates the platform specific push notification manager.
 std::unique_ptr<optimization_guide::PushNotificationManager>
-MaybeCreatePushNotificationManager(Profile* profile,
-                                   PrefService* pref_service) {
+MaybeCreatePushNotificationManager(PrefService* pref_service) {
 #if defined(OS_ANDROID)
   if (optimization_guide::features::IsPushNotificationsEnabled()) {
-    auto push_notification_manager = std::make_unique<
+    return std::make_unique<
         optimization_guide::android::AndroidPushNotificationManager>(
         pref_service);
-    // TODO(xingliu): Move this to OptimizationGuideKeyedServiceFactory. See
-    // crbug.com/1256908.
-    push_notification_manager->AddObserver(
-        PriceTrackingNotificationBridge::GetForBrowserContext(profile));
-    return push_notification_manager;
   }
 #endif
   return nullptr;
@@ -85,7 +78,7 @@
                    tab_url_provider,
                    url_loader_factory,
                    network_connection_tracker,
-                   MaybeCreatePushNotificationManager(profile, pref_service)),
+                   MaybeCreatePushNotificationManager(pref_service)),
       profile_(profile) {
   NavigationPredictorKeyedService* navigation_predictor_service =
       NavigationPredictorKeyedServiceFactory::GetForProfile(profile);
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common_manifest.json.jinja2 b/chrome/browser/resources/chromeos/accessibility/accessibility_common_manifest.json.jinja2
index d51026b..e69dbfa1 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common_manifest.json.jinja2
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common_manifest.json.jinja2
@@ -31,9 +31,7 @@
   "input_components": [
     {
       "name": "Dictation",
-      "type": "ime",
       "id": "dictation",
-      "description": "Dictation",
       "language": ["none"]
     }
   ]
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 1504ad85e4..03d7703 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -258,6 +258,8 @@
     "chromeos/os_apps_page/app_management_page/types.js",
     "chromeos/os_apps_page/app_management_page/util.js",
     "chromeos/os_apps_page/app_notifications_page/mojo_interface_provider.js",
+    "chromeos/os_apps_page/permission_constants.js",
+    "chromeos/os_apps_page/permission_util.js",
     "chromeos/os_languages_page/input_method_settings.js",
     "chromeos/os_languages_page/languages_browser_proxy.js",
     "chromeos/os_languages_page/languages.js",
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/BUILD.gn
index eda4686..aeb1ba9 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/BUILD.gn
@@ -13,6 +13,8 @@
     ":android_apps_browser_proxy",
     ":android_apps_subpage",
     ":os_apps_page",
+    ":permission_constants",
+    ":permission_util",
   ]
 }
 
@@ -58,6 +60,17 @@
   ]
 }
 
+js_library("permission_util") {
+  deps = [
+    ":permission_constants",
+    "//ui/webui/resources/js:assert.m",
+  ]
+}
+
+js_library("permission_constants") {
+  deps = [ "//chrome/browser/ui/webui/app_management:mojo_bindings_js_library_for_compile" ]
+}
+
 html_to_js("web_components") {
   js_files = [
     "android_apps_subpage.js",
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
index 63b0ffe..290a938 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
@@ -308,6 +308,7 @@
 
 js_library("util") {
   deps = [
+    "../:permission_constants",
     "../..:os_route.m",
     "../../..:router",
   ]
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js
index 454b70f3..1f49d7e 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.js
@@ -14,7 +14,9 @@
 import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
-import {AppType, InstallReason, PermissionType, TriState} from './constants.js';
+import {PermissionType, TriState} from '../permission_constants.js';
+
+import {AppType, InstallReason} from './constants.js';
 import {FakePageHandler} from './fake_page_handler.js';
 
 export class BrowserProxy {
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/constants.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/constants.js
index 0eea81c..c3e2189 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/constants.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/constants.js
@@ -28,31 +28,14 @@
   DETAIL: 1,
 };
 
-/**
- * A number representation of a Bool. Permission values should be of this type
- * for permissions with value type PermissionValueType.kBool.
- * @enum {number}
- * @const
- */
-export const Bool = {
-  kFalse: 0,
-  kTrue: 1,
-};
-
 export const AppType = apps.mojom.AppType;
 
-export const PermissionValueType = apps.mojom.PermissionValueType;
-
-export const TriState = apps.mojom.TriState;
-
 export const OptionalBool = apps.mojom.OptionalBool;
 
 export const InstallReason = apps.mojom.InstallReason;
 
 export const WindowMode = apps.mojom.WindowMode;
 
-export const PermissionType = apps.mojom.PermissionType;
-
 // This histogram is also declared and used at chrome/browser/ui/webui/settings/
 // chromeos/app_management/app_management_uma.h.
 export const AppManagementEntryPointsHistogramName =
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
index 92ab454..ae98192 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
@@ -4,9 +4,12 @@
 
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
-import {AppType, Bool, OptionalBool, PermissionType, PermissionValueType, TriState} from './constants.js';
+
+import {PermissionType, PermissionValue, TriState} from '../permission_constants.js';
+import {createBoolPermission, createTriStatePermission, getTriStatePermissionValue} from '../permission_util.js';
+
+import {AppType, OptionalBool} from './constants.js';
 import {AppManagementStore} from './store.js';
-import {createPermission} from './util.js';
 
 /**
  * @implements {appManagement.mojom.PageHandlerInterface}
@@ -32,12 +35,12 @@
 
       if (options && options[permissionType]) {
         const opts = options[permissionType];
-        permissionValue = opts.permissionValue || permissionValue;
+        permissionValue =
+            getTriStatePermissionValue(opts.value) || permissionValue;
         isManaged = opts.isManaged || isManaged;
       }
-      permissions[permissionType] = createPermission(
-          permissionType, PermissionValueType.kTriState, permissionValue,
-          isManaged);
+      permissions[permissionType] =
+          createTriStatePermission(permissionType, permissionValue, isManaged);
     }
 
     return permissions;
@@ -60,9 +63,8 @@
     const permissions = {};
 
     for (const permissionType of permissionTypes) {
-      permissions[permissionType] = createPermission(
-          permissionType, PermissionValueType.kBool, Bool.kTrue,
-          false /*is_managed*/);
+      permissions[permissionType] =
+          createBoolPermission(permissionType, true, false /*is_managed*/);
     }
 
     return permissions;
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_item.js
index 7abaebe..ac3489d 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_item.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/permission_item.js
@@ -8,11 +8,13 @@
 import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSearch, recordSettingChange, setUserActionRecorderForTesting} from '../../metrics_recorder.m.js';
+import {PermissionType, PermissionValue, TriState} from '../permission_constants.js';
+import {createBoolPermission, createTriStatePermission, getBoolPermissionValue, getTriStatePermissionValue, isBoolValue, isTriStateValue} from '../permission_util.js';
 
 import {BrowserProxy} from './browser_proxy.js';
-import {AppManagementUserAction, Bool, PermissionType, PermissionValueType, TriState} from './constants.js';
+import {AppManagementUserAction} from './constants.js';
 import {AppManagementStoreClient} from './store_client.js';
-import {createPermission, getPermission, getPermissionValueBool, getSelectedApp, recordAppManagementUserAction} from './util.js';
+import {getPermission, getPermissionValueBool, getSelectedApp, recordAppManagementUserAction} from './util.js';
 
 Polymer({
   _template: html`{__html_template__}`,
@@ -166,20 +168,21 @@
     let newPermission;
 
     let newBoolState = false;  // to keep the closure compiler happy.
-    switch (getPermission(this.app_, this.permissionType).valueType) {
-      case PermissionValueType.kBool:
-        newPermission =
-            this.getUIPermissionBoolean_(this.app_, this.permissionType);
-        newBoolState = newPermission.value === Bool.kTrue;
-        break;
-      case PermissionValueType.kTriState:
-        newPermission =
-            this.getUIPermissionTriState_(this.app_, this.permissionType);
-        newBoolState = newPermission.value === TriState.kAllow;
-        break;
-      default:
-        assertNotReached();
+    const permissionValue = getPermission(this.app_, this.permissionType).value;
+    if (isBoolValue(permissionValue)) {
+      newPermission =
+          this.getUIPermissionBoolean_(this.app_, this.permissionType);
+      newBoolState = getBoolPermissionValue(newPermission.value);
+    } else if (isTriStateValue(permissionValue)) {
+      newPermission =
+          this.getUIPermissionTriState_(this.app_, this.permissionType);
+
+      newBoolState =
+          getTriStatePermissionValue(newPermission.value) === TriState.kAllow;
+    } else {
+      assertNotReached();
     }
+
     BrowserProxy.getInstance().handler.setPermission(
         this.app_.id, newPermission);
 
@@ -199,23 +202,15 @@
    * @private
    */
   getUIPermissionBoolean_(app, permissionType) {
-    let newPermissionValue;
     const currentPermission = getPermission(app, permissionType);
 
-    switch (currentPermission.value) {
-      case Bool.kFalse:
-        newPermissionValue = Bool.kTrue;
-        break;
-      case Bool.kTrue:
-        newPermissionValue = Bool.kFalse;
-        break;
-      default:
-        assertNotReached();
-    }
-    assert(newPermissionValue !== undefined);
-    return createPermission(
-        PermissionType[permissionType], PermissionValueType.kBool,
-        newPermissionValue, currentPermission.isManaged);
+    assert(isBoolValue(currentPermission.value));
+
+    const newPermissionValue = !getBoolPermissionValue(currentPermission.value);
+
+    return createBoolPermission(
+        PermissionType[permissionType], newPermissionValue,
+        currentPermission.isManaged);
   },
 
   /**
@@ -230,7 +225,9 @@
     let newPermissionValue;
     const currentPermission = getPermission(app, permissionType);
 
-    switch (currentPermission.value) {
+    assert(isTriStateValue(currentPermission.value));
+
+    switch (getTriStatePermissionValue(currentPermission.value)) {
       case TriState.kBlock:
         newPermissionValue = TriState.kAllow;
         break;
@@ -249,9 +246,9 @@
     }
 
     assert(newPermissionValue !== undefined);
-    return createPermission(
-        PermissionType[permissionType], PermissionValueType.kTriState,
-        newPermissionValue, currentPermission.isManaged);
+    return createTriStatePermission(
+        PermissionType[permissionType], newPermissionValue,
+        currentPermission.isManaged);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js
index 1e83aa05..e674fd2 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/util.js
@@ -8,8 +8,11 @@
 
 import {Route, Router} from '../../../router.js';
 import {routes} from '../../os_route.m.js';
+import {PermissionType, PermissionValue, TriState} from '../permission_constants.js';
+import {getBoolPermissionValue, getTriStatePermissionValue, isPermissionEnabled} from '../permission_util.js';
 
-import {AppManagementUserAction, AppType, Bool, OptionalBool, PermissionType, PermissionValueType, TriState, WindowMode} from './constants.js';
+import {AppManagementUserAction, AppType, OptionalBool, WindowMode} from './constants.js';
+
 
 /**
  * @fileoverview Utility functions for the App Management page.
@@ -40,22 +43,6 @@
 }
 
 /**
- * @param {PermissionType} permissionType
- * @param {!PermissionValueType} valueType
- * @param {number} value
- * @param {boolean} isManaged
- * @return {!Permission}
- */
-export function createPermission(permissionType, valueType, value, isManaged) {
-  return {
-    permissionType,
-    valueType,
-    value,
-    isManaged,
-  };
-}
-
-/**
  * @param {App} app
  * @return {string}
  */
@@ -104,14 +91,7 @@
   const permission = getPermission(app, permissionType);
   assert(permission);
 
-  switch (permission.valueType) {
-    case PermissionValueType.kBool:
-      return permission.value === Bool.kTrue;
-    case PermissionValueType.kTriState:
-      return permission.value === TriState.kAllow;
-    default:
-      assertNotReached();
-  }
+  return isPermissionEnabled(permission.value);
 }
 
 /**
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/BUILD.gn
index be4ab14..3e27d53d 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/BUILD.gn
@@ -18,6 +18,7 @@
   closure_flags = os_settings_closure_flags
   is_polymer3 = true
   deps = [
+    ":app_notification_row",
     ":app_notifications_subpage",
     ":mojo_interface_provider",
   ]
@@ -39,6 +40,8 @@
 
 js_library("app_notification_row") {
   deps = [
+    "../:permission_constants",
+    "../:permission_util",
     "../..:metrics_recorder.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js
index ae449a7..0249cc84 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js
@@ -14,6 +14,7 @@
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {recordSettingChange} from '../../metrics_recorder.m.js';
+import {createBoolPermissionValue, createTriStatePermissionValue, isBoolValue, isPermissionEnabled, isTriStateValue} from '../permission_util.js';
 
 import {getAppNotificationProvider} from './mojo_interface_provider.js';
 
@@ -58,34 +59,20 @@
   }
 
   isNotificationPermissionEnabled_() {
-    if (this.app.notificationPermission.valueType ===
-            apps.mojom.PermissionValueType.kBool &&
-        this.app.notificationPermission.value === 1) {
-      this.checked_ = true;
-      return;
-    }
-
-    if (this.app.notificationPermission.valueType ===
-            apps.mojom.PermissionValueType.kTriState &&
-        this.app.notificationPermission.value === apps.mojom.TriState.kAllow) {
-      this.checked_ = true;
-      return;
-    }
-
-    this.checked_ = false;
+    this.checked_ = isPermissionEnabled(this.app.notificationPermission.value);
   }
 
   /** @param {!Event} event */
   onNotificationRowClicked_(event) {
     const permission = this.app.notificationPermission;
 
-    if (permission.valueType === apps.mojom.PermissionValueType.kBool) {
-      // apps.mojom.permission.value expects a number type.
-      permission.value = this.checked_ ? 0 : 1;
-    } else if (
-        permission.valueType === apps.mojom.PermissionValueType.kTriState) {
-      permission.value = this.checked_ ? apps.mojom.TriState.kBlock :
-                                         apps.mojom.TriState.kAllow;
+    if (isBoolValue(permission.value)) {
+      permission.value =
+          createBoolPermissionValue(this.checked_ ? false : true);
+    } else if (isTriStateValue(permission.value)) {
+      permission.value = createTriStatePermissionValue(
+          this.checked_ ? apps.mojom.TriState.kBlock :
+                          apps.mojom.TriState.kAllow);
     }
 
     this.mojoInterfaceProvider_.setNotificationPermission(
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/permission_constants.js b/chrome/browser/resources/settings/chromeos/os_apps_page/permission_constants.js
new file mode 100644
index 0000000..75256e57
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/permission_constants.js
@@ -0,0 +1,14 @@
+// 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.
+
+import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+
+import '/app-management/types.mojom-lite.js';
+
+
+export const TriState = apps.mojom.TriState;
+
+export const PermissionType = apps.mojom.PermissionType;
+
+export const PermissionValue = apps.mojom.PermissionValue;
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/permission_util.js b/chrome/browser/resources/settings/chromeos/os_apps_page/permission_util.js
new file mode 100644
index 0000000..962b0ad
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/permission_util.js
@@ -0,0 +1,109 @@
+// 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.
+
+import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
+
+import {PermissionType, PermissionValue, TriState} from './permission_constants.js';
+
+/**
+ * @param {PermissionType} permissionType
+ * @param {PermissionValue} value
+ * @param {boolean} isManaged
+ * @return {!apps.mojom.Permission}
+ */
+export function createPermission(permissionType, value, isManaged) {
+  return {
+    permissionType,
+    value,
+    isManaged,
+  };
+}
+
+/**
+ * @param {TriState} value
+ * @returns {PermissionValue}
+ */
+export function createTriStatePermissionValue(value) {
+  return {tristateValue: value};
+}
+
+/**
+ * @param {PermissionValue} permissionValue
+ * @returns {TriState}
+ */
+export function getTriStatePermissionValue(permissionValue) {
+  assert(isTriStateValue(permissionValue));
+  return permissionValue['tristateValue'];
+}
+
+/**
+ * @param {boolean} value
+ * @returns {PermissionValue}
+ */
+export function createBoolPermissionValue(value) {
+  return {boolValue: value};
+}
+
+/**
+ * @param {PermissionValue} permissionValue
+ * @returns {boolean}
+ */
+export function getBoolPermissionValue(permissionValue) {
+  assert(isBoolValue(permissionValue));
+  return permissionValue['boolValue'];
+}
+
+/**
+ * @param {PermissionValue} permissionValue
+ * @returns {boolean}
+ */
+export function isTriStateValue(permissionValue) {
+  return permissionValue['tristateValue'] !== undefined &&
+      permissionValue['boolValue'] === undefined;
+}
+
+/**
+ * @param {PermissionValue} permissionValue
+ * @returns {boolean}
+ */
+export function isBoolValue(permissionValue) {
+  return permissionValue['boolValue'] !== undefined &&
+      permissionValue['tristateValue'] === undefined;
+}
+
+/**
+ * @param {PermissionType} permissionType
+ * @param {boolean} value
+ * @param {boolean} isManaged
+ * @return {!apps.mojom.Permission}
+ */
+export function createBoolPermission(permissionType, value, isManaged) {
+  return createPermission(
+      permissionType, createBoolPermissionValue(value), isManaged);
+}
+
+/**
+ * @param {PermissionType} permissionType
+ * @param {TriState} value
+ * @param {boolean} isManaged
+ * @return {!apps.mojom.Permission}
+ */
+export function createTriStatePermission(permissionType, value, isManaged) {
+  return createPermission(
+      permissionType, createTriStatePermissionValue(value), isManaged);
+}
+
+/**
+ * @param {PermissionValue} permissionValue
+ * @returns {boolean}
+ */
+export function isPermissionEnabled(permissionValue) {
+  if (isBoolValue(permissionValue)) {
+    return getBoolPermissionValue(permissionValue);
+  } else if (isTriStateValue(permissionValue)) {
+    return getTriStatePermissionValue(permissionValue) === TriState.kAllow;
+  } else {
+    assertNotReached();
+  }
+}
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js
index b25e535..d6aff2d7 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -129,14 +129,16 @@
 export {AndroidAppsBrowserProxyImpl} from './os_apps_page/android_apps_browser_proxy.js';
 export {addApp, changeApp, removeApp, updateSelectedAppId} from './os_apps_page/app_management_page/actions.js';
 export {BrowserProxy} from './os_apps_page/app_management_page/browser_proxy.js';
-export {Bool, PageType, PermissionType, PermissionValueType, TriState, WindowMode} from './os_apps_page/app_management_page/constants.js';
+export {PageType, WindowMode} from './os_apps_page/app_management_page/constants.js';
 export {FakePageHandler} from './os_apps_page/app_management_page/fake_page_handler.js';
 export {PluginVmBrowserProxyImpl} from './os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.js';
 export {AppState, reduceAction} from './os_apps_page/app_management_page/reducers.js';
 export {AppManagementStore} from './os_apps_page/app_management_page/store.js';
 export {AppManagementStoreClientImpl} from './os_apps_page/app_management_page/store_client.js';
-export {convertOptionalBoolToBool, createEmptyState, createInitialState, createPermission, getPermissionValueBool} from './os_apps_page/app_management_page/util.js';
+export {convertOptionalBoolToBool, createEmptyState, createInitialState, getPermissionValueBool} from './os_apps_page/app_management_page/util.js';
 export {setAppNotificationProviderForTesting} from './os_apps_page/app_notifications_page/mojo_interface_provider.js';
+export {PermissionType, TriState} from './os_apps_page/permission_constants.js';
+export {createBoolPermission, createTriStatePermission, getBoolPermissionValue, isBoolValue} from './os_apps_page/permission_util.js';
 export {osPageVisibility} from './os_page_visibility.m.js';
 export {AccountManagerBrowserProxy, AccountManagerBrowserProxyImpl} from './os_people_page/account_manager_browser_proxy.js';
 export {FingerprintBrowserProxyImpl, FingerprintResultType} from './os_people_page/fingerprint_browser_proxy.m.js';
diff --git a/chrome/browser/translate/chrome_translate_client.cc b/chrome/browser/translate/chrome_translate_client.cc
index 9dd9050..7d880b1 100644
--- a/chrome/browser/translate/chrome_translate_client.cc
+++ b/chrome/browser/translate/chrome_translate_client.cc
@@ -102,9 +102,8 @@
         *web_contents, &web_contents->GetController(),
         UrlLanguageHistogramFactory::GetForBrowserContext(
             web_contents->GetBrowserContext()),
-        TranslateModelServiceFactory::GetOrBuildForKey(
-            Profile::FromBrowserContext(web_contents->GetBrowserContext())
-                ->GetProfileKey()));
+        TranslateModelServiceFactory::GetForProfile(
+            Profile::FromBrowserContext(web_contents->GetBrowserContext())));
   }
   translate_manager_ = std::make_unique<translate::TranslateManager>(
       this,
diff --git a/chrome/browser/translate/translate_model_service_browsertest.cc b/chrome/browser/translate/translate_model_service_browsertest.cc
index 6af8880..6e98855 100644
--- a/chrome/browser/translate/translate_model_service_browsertest.cc
+++ b/chrome/browser/translate/translate_model_service_browsertest.cc
@@ -101,8 +101,8 @@
 
 IN_PROC_BROWSER_TEST_F(TranslateModelServiceDisabledBrowserTest,
                        TranslateModelServiceDisabled) {
-  EXPECT_FALSE(TranslateModelServiceFactory::GetOrBuildForKey(
-      browser()->profile()->GetProfileKey()));
+  EXPECT_FALSE(
+      TranslateModelServiceFactory::GetForProfile(browser()->profile()));
 }
 
 class TranslateModelServiceWithoutOptimizationGuideBrowserTest
@@ -125,8 +125,8 @@
 // the optimization guide does not exist.
 IN_PROC_BROWSER_TEST_F(TranslateModelServiceWithoutOptimizationGuideBrowserTest,
                        TranslateModelServiceEnabled) {
-  EXPECT_FALSE(TranslateModelServiceFactory::GetOrBuildForKey(
-      browser()->profile()->GetProfileKey()));
+  EXPECT_FALSE(
+      TranslateModelServiceFactory::GetForProfile(browser()->profile()));
 }
 
 IN_PROC_BROWSER_TEST_F(TranslateModelServiceDisabledBrowserTest,
@@ -167,8 +167,7 @@
   ~TranslateModelServiceBrowserTest() override = default;
 
   translate::TranslateModelService* translate_model_service() {
-    return TranslateModelServiceFactory::GetOrBuildForKey(
-        browser()->profile()->GetProfileKey());
+    return TranslateModelServiceFactory::GetForProfile(browser()->profile());
   }
 
   const GURL& english_url() const { return english_url_; }
@@ -215,11 +214,8 @@
 
 IN_PROC_BROWSER_TEST_F(TranslateModelServiceBrowserTest,
                        TranslateModelServiceEnabled_OffTheRecord) {
-  EXPECT_TRUE(TranslateModelServiceFactory::GetOrBuildForKey(
-      browser()
-          ->profile()
-          ->GetPrimaryOTRProfile(/*create_if_needed=*/true)
-          ->GetProfileKey()));
+  EXPECT_TRUE(TranslateModelServiceFactory::GetForProfile(
+      browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true)));
 }
 
 IN_PROC_BROWSER_TEST_F(TranslateModelServiceBrowserTest,
@@ -303,17 +299,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(TranslateModelServiceBrowserTest,
-                       LanguageDetectionModelCreated) {
-  base::HistogramTester histogram_tester;
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), english_url()));
-  RetryForHistogramUntilCountReached(
-      &histogram_tester,
-      "LanguageDetection.TFLiteModel.WasModelAvailableForDetection", 1);
-  histogram_tester.ExpectUniqueSample(
-      "LanguageDetection.TFLiteModel.WasModelAvailableForDetection", false, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(TranslateModelServiceBrowserTest,
                        LanguageDetectionModelAvailableForDetection) {
   base::HistogramTester histogram_tester;
   OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
diff --git a/chrome/browser/translate/translate_model_service_factory.cc b/chrome/browser/translate/translate_model_service_factory.cc
index c0c0718..6ca5eb9 100644
--- a/chrome/browser/translate/translate_model_service_factory.cc
+++ b/chrome/browser/translate/translate_model_service_factory.cc
@@ -10,19 +10,17 @@
 #include "base/task/thread_pool.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_key.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/keyed_service/core/simple_dependency_manager.h"
 #include "components/translate/content/browser/translate_model_service.h"
 #include "components/translate/core/common/translate_util.h"
 
 // static
-translate::TranslateModelService*
-TranslateModelServiceFactory::GetOrBuildForKey(SimpleFactoryKey* key) {
+translate::TranslateModelService* TranslateModelServiceFactory::GetForProfile(
+    Profile* profile) {
   return static_cast<translate::TranslateModelService*>(
-      GetInstance()->GetServiceForKey(key, true));
+      GetInstance()->GetServiceForBrowserContext(profile, true));
 }
 
 // static
@@ -32,36 +30,33 @@
 }
 
 TranslateModelServiceFactory::TranslateModelServiceFactory()
-    : SimpleKeyedServiceFactory("TranslateModelService",
-                                SimpleDependencyManager::GetInstance()) {}
+    : BrowserContextKeyedServiceFactory(
+          "TranslateModelService",
+          BrowserContextDependencyManager::GetInstance()) {}
 
 TranslateModelServiceFactory::~TranslateModelServiceFactory() = default;
 
-std::unique_ptr<KeyedService>
-TranslateModelServiceFactory::BuildServiceInstanceFor(
-    SimpleFactoryKey* key) const {
+KeyedService* TranslateModelServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
   if (!translate::IsTFLiteLanguageDetectionEnabled())
     return nullptr;
+
   // The optimization guide service must be available for the translate model
   // service to be created.
   auto* opt_guide = OptimizationGuideKeyedServiceFactory::GetForProfile(
-      ProfileManager::GetProfileFromProfileKey(
-          ProfileKey::FromSimpleFactoryKey(key)));
+      Profile::FromBrowserContext(context));
+
   if (opt_guide) {
     scoped_refptr<base::SequencedTaskRunner> background_task_runner =
         base::ThreadPool::CreateSequencedTaskRunner(
             {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
-    return std::make_unique<translate::TranslateModelService>(
-        opt_guide, background_task_runner);
+    return new translate::TranslateModelService(opt_guide,
+                                                background_task_runner);
   }
   return nullptr;
 }
 
-SimpleFactoryKey* TranslateModelServiceFactory::GetKeyToUse(
-    SimpleFactoryKey* key) const {
-  // The translate model service should be able to
-  // operate in off the record sessions if the model is available from the
-  // optimization guide.
-  ProfileKey* profile_key = ProfileKey::FromSimpleFactoryKey(key);
-  return profile_key->GetOriginalKey();
+content::BrowserContext* TranslateModelServiceFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextOwnInstanceInIncognito(context);
 }
diff --git a/chrome/browser/translate/translate_model_service_factory.h b/chrome/browser/translate/translate_model_service_factory.h
index b474b38..1695d21 100644
--- a/chrome/browser/translate/translate_model_service_factory.h
+++ b/chrome/browser/translate/translate_model_service_factory.h
@@ -7,22 +7,25 @@
 
 #include "base/macros.h"
 #include "base/no_destructor.h"
-#include "components/keyed_service/core/simple_keyed_service_factory.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/translate/content/browser/translate_model_service.h"
 
-class SimpleFactoryKey;
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+class Profile;
 
 // LazyInstance that owns all TranslateModelService(s) and associates
 // them with Profiles.
-class TranslateModelServiceFactory : public SimpleKeyedServiceFactory {
+class TranslateModelServiceFactory : public BrowserContextKeyedServiceFactory {
  public:
   // Gets the TranslateModelService for the profile.
   //
   // Returns null if the features that allow for this to provide useful
   // information are disabled. Importantly, only available when the
   // optimization guide service is.
-  static translate::TranslateModelService* GetOrBuildForKey(
-      SimpleFactoryKey* key);
+  static translate::TranslateModelService* GetForProfile(Profile* profile);
 
   // Gets the LazyInstance that owns all TranslateModelService(s).
   static TranslateModelServiceFactory* GetInstance();
@@ -33,10 +36,11 @@
   TranslateModelServiceFactory();
   ~TranslateModelServiceFactory() override;
 
-  // SimpleKeyedServiceFactory overrides:
-  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
-      SimpleFactoryKey* key) const override;
-  SimpleFactoryKey* GetKeyToUse(SimpleFactoryKey* key) const override;
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
 };
 
 #endif  //  CHROME_BROWSER_TRANSLATE_TRANSLATE_MODEL_SERVICE_FACTORY_H_
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 23332db..64480ad 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -5205,6 +5205,8 @@
 
   if (enable_side_search) {
     sources += [
+      "side_search/side_search_config.cc",
+      "side_search/side_search_config.h",
       "side_search/side_search_metrics.cc",
       "side_search/side_search_metrics.h",
       "side_search/side_search_prefs.cc",
diff --git a/chrome/browser/ui/app_list/app_list_model_updater.h b/chrome/browser/ui/app_list/app_list_model_updater.h
index 9044625..edfdeae3 100644
--- a/chrome/browser/ui/app_list/app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/app_list_model_updater.h
@@ -61,7 +61,8 @@
   virtual void UpdateSearchBox(const std::u16string& text,
                                bool initiated_by_user) {}
   virtual void PublishSearchResults(
-      const std::vector<ChromeSearchResult*>& results) {}
+      const std::vector<ChromeSearchResult*>& results,
+      const std::vector<ash::AppListSearchResultCategory>& categories) {}
   virtual std::vector<ChromeSearchResult*> GetPublishedSearchResultsForTest();
 
   // Item field setters only used by ChromeAppListItem and its derived classes.
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
index c291ee8a..4d0c786 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
@@ -137,7 +137,8 @@
 }
 
 void ChromeAppListModelUpdater::PublishSearchResults(
-    const std::vector<ChromeSearchResult*>& results) {
+    const std::vector<ChromeSearchResult*>& results,
+    const std::vector<ash::AppListSearchResultCategory>& categories) {
   published_results_ = results;
   for (auto* const result : results)
     result->set_model_updater(this);
@@ -146,7 +147,8 @@
   std::vector<std::unique_ptr<ash::SearchResultMetadata>> result_data;
   for (auto* result : results)
     result_data.push_back(result->CloneMetadata());
-  app_list_controller_->PublishSearchResults(std::move(result_data));
+  app_list_controller_->PublishSearchResults(std::move(result_data),
+                                             categories);
 }
 
 std::vector<ChromeSearchResult*>
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
index 00df8dc6..4fbbc85 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
@@ -41,7 +41,8 @@
   void UpdateSearchBox(const std::u16string& text,
                        bool initiated_by_user) override;
   void PublishSearchResults(
-      const std::vector<ChromeSearchResult*>& results) override;
+      const std::vector<ChromeSearchResult*>& results,
+      const std::vector<ash::AppListSearchResultCategory>& categories) override;
   std::vector<ChromeSearchResult*> GetPublishedSearchResultsForTest() override;
 
   // Methods only used by ChromeAppListItem that talk to ash directly.
diff --git a/chrome/browser/ui/app_list/search/app_list_search_browsertest.cc b/chrome/browser/ui/app_list/search/app_list_search_browsertest.cc
index 5738db14..a8fc31b 100644
--- a/chrome/browser/ui/app_list/search/app_list_search_browsertest.cc
+++ b/chrome/browser/ui/app_list/search/app_list_search_browsertest.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_manager.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/test/with_crosapi_param.h"
 #include "chrome/browser/web_applications/web_app_id.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
@@ -81,6 +82,13 @@
   AppListSearchBrowserTest(const AppListSearchBrowserTest&) = delete;
   AppListSearchBrowserTest& operator=(const AppListSearchBrowserTest&) = delete;
 
+  // InProcessBrowserTest:
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    web_app::test::WaitUntilReady(
+        web_app::WebAppProvider::GetForTest(browser()->profile()));
+  }
+
   //---------------
   // Search helpers
   //---------------
diff --git a/chrome/browser/ui/app_list/search/common/types_util.cc b/chrome/browser/ui/app_list/search/common/types_util.cc
index c0e6a8c..8eb323fd 100644
--- a/chrome/browser/ui/app_list/search/common/types_util.cc
+++ b/chrome/browser/ui/app_list/search/common/types_util.cc
@@ -168,6 +168,8 @@
       return "Card";
     case ash::SearchResultDisplayType::kChip:
       return "Chip";
+    case ash::SearchResultDisplayType::kContinue:
+      return "Continue";
   }
   NOTREACHED();
 }
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
index 27a94b3..dee9a71 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/app_list/app_list_types.h"
@@ -33,6 +34,8 @@
 namespace {
 
 // Schemas of result IDs for the results list and suggestion chips.
+// TODO(crbug.com/1258415): kChipSchema can be removed once the new launcher is
+// launched.
 constexpr char kListSchema[] = "zero_state_drive://";
 constexpr char kChipSchema[] = "drive_chip://";
 
@@ -77,6 +80,14 @@
   return drive_service->GetMountPointPath().Append(path.value());
 }
 
+// TODO(crbug.com/1258415): This exists to reroute results depending on which
+// launcher is enabled, and should be removed after the new launcher launch.
+ash::SearchResultDisplayType GetDisplayType() {
+  return ash::features::IsProductivityLauncherEnabled()
+             ? ash::SearchResultDisplayType::kContinue
+             : ash::SearchResultDisplayType::kList;
+}
+
 }  // namespace
 
 ZeroStateDriveProvider::ZeroStateDriveProvider(
@@ -243,8 +254,8 @@
     const float relevance) {
   return std::make_unique<FileResult>(
       kListSchema, ReparentToDriveMount(filepath, drive_service_),
-      ash::AppListSearchResultType::kZeroStateDrive,
-      ash::SearchResultDisplayType::kList, relevance, profile_);
+      ash::AppListSearchResultType::kZeroStateDrive, GetDisplayType(),
+      relevance, profile_);
 }
 
 std::unique_ptr<FileResult> ZeroStateDriveProvider::MakeChipResult(
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h
index 9c1ca13..1a13f05 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h
+++ b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h
@@ -52,6 +52,8 @@
 
   std::unique_ptr<FileResult> MakeListResult(const base::FilePath& filepath,
                                              const float relevance);
+  // TODO(crbug.com/1258415): Chip results don't exist in the new launcher.
+  // MakeChipResult can be removed after launch.
   std::unique_ptr<FileResult> MakeChipResult(const base::FilePath& filepath,
                                              const float relevance);
 
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc b/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
index 314b337..20ed3761 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "base/bind.h"
@@ -30,6 +31,8 @@
 namespace app_list {
 namespace {
 
+// TODO(crbug.com/1258415): kFileChipSchema can be removed once the new
+// launcher is launched.
 constexpr char kFileChipSchema[] = "file_chip://";
 constexpr char kZeroStateFileSchema[] = "zero_state_file://";
 
@@ -60,6 +63,14 @@
       chromeos::prefs::kSuggestedContentEnabled);
 }
 
+// TODO(crbug.com/1258415): This exists to reroute results depending on which
+// launcher is enabled, and should be removed after the new launcher launch.
+ash::SearchResultDisplayType GetDisplayType() {
+  return ash::features::IsProductivityLauncherEnabled()
+             ? ash::SearchResultDisplayType::kContinue
+             : ash::SearchResultDisplayType::kList;
+}
+
 }  // namespace
 
 ZeroStateFileProvider::ZeroStateFileProvider(Profile* profile)
@@ -119,12 +130,14 @@
   for (const auto& filepath_score : results.first) {
     auto result = std::make_unique<FileResult>(
         kZeroStateFileSchema, filepath_score.first,
-        ash::AppListSearchResultType::kZeroStateFile,
-        ash::SearchResultDisplayType::kList, filepath_score.second, profile_);
+        ash::AppListSearchResultType::kZeroStateFile, GetDisplayType(),
+        filepath_score.second, profile_);
     result->RequestThumbnail(&thumbnail_loader_);
     new_results.push_back(std::move(result));
 
     // Add suggestion chip file results
+    // TODO(crbug.com/1258415): This can be removed once the new launcher is
+    // launched.
     if (app_list_features::IsSuggestedLocalFilesEnabled() &&
         IsSuggestedContentEnabled(profile_)) {
       new_results.emplace_back(
diff --git a/chrome/browser/ui/app_list/search/mixer.cc b/chrome/browser/ui/app_list/search/mixer.cc
index 7522e81..0bdf8ae 100644
--- a/chrome/browser/ui/app_list/search/mixer.cc
+++ b/chrome/browser/ui/app_list/search/mixer.cc
@@ -191,7 +191,8 @@
     new_results.push_back(sort_data.result);
   }
   search_controller_->NotifyResultsAdded(new_results);
-  model_updater_->PublishSearchResults(new_results);
+  // Categories are unused in old search.
+  model_updater_->PublishSearchResults(new_results, /*categories=*/{});
 }
 
 void Mixer::FetchResults(const std::u16string& query) {
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_new.cc b/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
index 1260ab4..a5f670f1 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
@@ -202,7 +202,8 @@
       observer.OnResultsAdded(last_query_, observer_results);
   }
 
-  model_updater_->PublishSearchResults(all_results);
+  // TODO(crbug.com/1199206): Send category ranks.
+  model_updater_->PublishSearchResults(all_results, /*categories=*/{});
 }
 
 ChromeSearchResult* SearchControllerImplNew::FindSearchResult(
diff --git a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
index 9ef9239..4b45e7a 100644
--- a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
@@ -169,7 +169,8 @@
 }
 
 void FakeAppListModelUpdater::PublishSearchResults(
-    const std::vector<ChromeSearchResult*>& results) {
+    const std::vector<ChromeSearchResult*>& results,
+    const std::vector<ash::AppListSearchResultCategory>& categories) {
   search_results_ = results;
 }
 
diff --git a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
index 7ff5e55..fe6d1df 100644
--- a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
@@ -47,7 +47,8 @@
   // For SearchModel:
   void SetSearchEngineIsGoogle(bool is_google) override;
   void PublishSearchResults(
-      const std::vector<ChromeSearchResult*>& results) override;
+      const std::vector<ChromeSearchResult*>& results,
+      const std::vector<ash::AppListSearchResultCategory>& categories) override;
 
   void ActivateChromeItem(const std::string& id, int event_flags) override;
   void LoadAppIcon(const std::string& id) override;
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
index 8659ce4..68a8329 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
@@ -459,6 +459,8 @@
 
     os_hooks_suppress_ =
         web_app::OsIntegrationManager::ScopedSuppressOsHooksForTesting();
+    web_app::test::WaitUntilReady(
+        web_app::WebAppProvider::GetForTest(browser()->profile()));
   }
 
  private:
@@ -1244,7 +1246,8 @@
 
 // Verifies that native browser window properties are properly set when showing
 // a PWA tab.
-IN_PROC_BROWSER_TEST_F(ShelfWebAppBrowserTest, AppIDForPWA) {
+// DISABLED due to flakiness (http://crbug.com/1258995).
+IN_PROC_BROWSER_TEST_F(ShelfWebAppBrowserTest, DISABLED_AppIDForPWA) {
   // Start server and open test page.
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
diff --git a/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.cc
index 753ed18..12756eb 100644
--- a/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.cc
@@ -167,22 +167,12 @@
   Show();
 }
 
-void OfferNotificationBubbleControllerImpl::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
-  // frames. This caller was converted automatically to the primary main frame
-  // to preserve its semantics. Follow up to confirm correctness.
-  if (!navigation_handle->IsInPrimaryMainFrame() ||
-      !navigation_handle->HasCommitted())
-    return;
-
-  // Don't react to same-document (fragment) navigations.
-  if (navigation_handle->IsSameDocument())
-    return;
-
+void OfferNotificationBubbleControllerImpl::PrimaryPageChanged(
+    content::Page& page) {
   // Don't do anything if user is still on an eligible origin for this offer.
-  if (base::ranges::count(origins_to_display_bubble_,
-                          navigation_handle->GetURL().GetOrigin())) {
+  if (base::ranges::count(
+          origins_to_display_bubble_,
+          page.GetMainDocument().GetLastCommittedURL().GetOrigin())) {
     return;
   }
 
diff --git a/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.h b/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.h
index d02132c..81ab0ae6 100644
--- a/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.h
+++ b/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.h
@@ -63,8 +63,7 @@
       content::WebContents* web_contents);
 
   // AutofillBubbleControllerBase:
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
+  void PrimaryPageChanged(content::Page& page) override;
   PageActionIconType GetPageActionIconType() override;
   void DoShowBubble() override;
 
diff --git a/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl_unittest.cc b/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl_unittest.cc
index 596e4a65..676ff95 100644
--- a/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.h"
 
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/autofill/autofill_bubble_base.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -11,8 +12,11 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/autofill_offer_data.h"
 #include "content/public/test/mock_navigation_handle.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 
 namespace autofill {
 
@@ -32,13 +36,6 @@
       content::WebContents* web_contents)
       : OfferNotificationBubbleControllerImpl(web_contents) {}
 
-  void SimulateNavigation(std::string url) {
-    content::RenderFrameHost* rfh = web_contents()->GetMainFrame();
-    content::MockNavigationHandle navigation_handle(GURL(url), rfh);
-    navigation_handle.set_has_committed(true);
-    DidFinishNavigation(&navigation_handle);
-  }
-
  private:
   // Overrides to bypass the IsWebContentsActive check.
   void DoShowBubble() override {
@@ -65,10 +62,16 @@
 
   void SetUp() override {
     BrowserWithTestWindowTest::SetUp();
-    AddTab(browser(), GURL("about:blank"));
-    content::WebContents* web_contents =
-        browser()->tab_strip_model()->GetActiveWebContents();
-    TestOfferNotificationBubbleControllerImpl::CreateForTesting(web_contents);
+    AddTab(GURL("about:blank"));
+    TestOfferNotificationBubbleControllerImpl::CreateForTesting(web_contents());
+  }
+
+  content::WebContents* web_contents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  virtual void AddTab(const GURL& url) {
+    BrowserWithTestWindowTest::AddTab(browser(), url);
   }
 
  protected:
@@ -98,7 +101,7 @@
   TestOfferNotificationBubbleControllerImpl* controller() {
     return static_cast<TestOfferNotificationBubbleControllerImpl*>(
         TestOfferNotificationBubbleControllerImpl::FromWebContents(
-            browser()->tab_strip_model()->GetActiveWebContents()));
+            web_contents()));
   }
 
  private:
@@ -152,4 +155,65 @@
   EXPECT_EQ(&offer, controller()->GetOffer());
 }
 
+class OfferNotificationBubbleControllerImplPrerenderTest
+    : public OfferNotificationBubbleControllerImplTest {
+ public:
+  OfferNotificationBubbleControllerImplPrerenderTest() {
+    scoped_feature_list_.InitWithFeatures(
+        {blink::features::kPrerender2},
+        // This feature is to run test on any bot.
+        {blink::features::kPrerender2MemoryControls});
+  }
+
+  // BrowserWithTestWindowTest::AddTab creates a real WebContentsImpl object,
+  // but we need a TestWebContents object to unit test prerendering, so we
+  // override AddTab to create a TestWebContents instead.
+  void AddTab(const GURL& url) override {
+    std::unique_ptr<content::WebContents> contents(
+        content::WebContentsTester::CreateTestWebContents(profile(), nullptr));
+    content::WebContents* raw_contents = contents.get();
+    browser()->tab_strip_model()->AppendWebContents(std::move(contents), true);
+    EXPECT_EQ(web_contents(), raw_contents);
+    content::WebContentsTester::For(raw_contents)->NavigateAndCommit(url);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(OfferNotificationBubbleControllerImplPrerenderTest,
+       DoNotHideBubbleForPrerender) {
+  const GURL& original_url = GURL("https://www.example.com/first/");
+  NavigateAndCommitActiveTab(original_url);
+
+  // Ensure a bubble is visible on the primary page.
+  AutofillOfferData offer =
+      CreateTestOfferWithOrigins({original_url.GetOrigin()});
+  ShowBubble(&offer);
+  EXPECT_TRUE(controller()->GetOfferNotificationBubbleView());
+
+  // Start a prerender and navigate the test page.
+  const GURL& prerender_url = GURL("https://www.example.com/second/");
+  content::RenderFrameHost* prerender_frame =
+      content::WebContentsTester::For(web_contents())
+          ->AddPrerenderAndCommitNavigation(prerender_url);
+  ASSERT_EQ(prerender_frame->GetLifecycleState(),
+            content::RenderFrameHost::LifecycleState::kPrerendering);
+
+  // Ensure a bubble is still visible on the primary page.
+  EXPECT_TRUE(controller()->GetOfferNotificationBubbleView());
+
+  // Activate the prerendered page.
+  content::NavigationSimulator::CreateRendererInitiated(
+      prerender_url, web_contents()->GetMainFrame())
+      ->Commit();
+
+  // Ensure a bubble is still visible on the activated page since it has the
+  // same-origin as the original url. Cross-origin prerendering is unsupported
+  // right now, so this navigation will always be same-origin.
+  // TODO(crbug.com/1176054): Add a cross-origin test when prerendering
+  // eventually support it.
+  EXPECT_TRUE(controller()->GetOfferNotificationBubbleView());
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/profile_picker.h b/chrome/browser/ui/profile_picker.h
index e3dfb0c..2019a45b 100644
--- a/chrome/browser/ui/profile_picker.h
+++ b/chrome/browser/ui/profile_picker.h
@@ -127,9 +127,6 @@
   // Returns the web view (embedded in the picker) for testing.
   static views::WebView* GetWebViewForTesting();
 
-  // Returns the simple toolbar (embedded in the picker) for testing.
-  static views::View* GetToolbarForTesting();
-
   // Add a callback that will be called the next time the picker is opened.
   static void AddOnProfilePickerOpenedCallbackForTesting(
       base::OnceClosure callback);
diff --git a/chrome/browser/ui/side_search/side_search_config.cc b/chrome/browser/ui/side_search/side_search_config.cc
new file mode 100644
index 0000000..9a5ba7be
--- /dev/null
+++ b/chrome/browser/ui/side_search/side_search_config.cc
@@ -0,0 +1,64 @@
+// 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/ui/side_search/side_search_config.h"
+
+#include "chrome/common/webui_url_constants.h"
+#include "components/google/core/common/google_util.h"
+#include "content/public/browser/browser_context.h"
+#include "url/gurl.h"
+
+namespace {
+
+constexpr char kSideSearchConfigKey[] = "side_search_config_key";
+
+// Default condition test for CanShowSidePanelForURL() for the Google DSE.
+// TODO(tluk): This should be defined elsewhere as we move to support generic
+// DSEs.
+bool GoogleSRPShouldNavigateInSidePanel(const GURL& url) {
+  return !google_util::IsGoogleSearchUrl(url) &&
+         !google_util::IsGoogleHomePageUrl(url) &&
+         url.spec() != chrome::kChromeUINewTabURL;
+}
+
+}  // namespace
+
+SideSearchConfig::SideSearchConfig()
+    : should_navigate_in_side_panel_callback_(
+          base::BindRepeating(google_util::IsGoogleSearchUrl)),
+      can_show_side_panel_for_url_callback_(base::BindRepeating(
+          base::BindRepeating(GoogleSRPShouldNavigateInSidePanel))) {}
+
+SideSearchConfig::~SideSearchConfig() = default;
+
+// static
+SideSearchConfig* SideSearchConfig::Get(content::BrowserContext* context) {
+  SideSearchConfig* data = static_cast<SideSearchConfig*>(
+      context->GetUserData(kSideSearchConfigKey));
+  if (!data) {
+    auto new_data = std::make_unique<SideSearchConfig>();
+    data = new_data.get();
+    context->SetUserData(kSideSearchConfigKey, std::move(new_data));
+  }
+  return data;
+}
+
+bool SideSearchConfig::ShouldNavigateInSidePanel(const GURL& url) {
+  return should_navigate_in_side_panel_callback_.Run(url);
+}
+
+void SideSearchConfig::SetShouldNavigateInSidePanelCalback(
+    URLTestConditionCallback callback) {
+  should_navigate_in_side_panel_callback_ = std::move(callback);
+}
+
+bool SideSearchConfig::CanShowSidePanelForURL(const GURL& url) {
+  return is_side_panel_srp_available_ &&
+         can_show_side_panel_for_url_callback_.Run(url);
+}
+
+void SideSearchConfig::SetCanShowSidePanelForURLCallback(
+    URLTestConditionCallback callback) {
+  can_show_side_panel_for_url_callback_ = std::move(callback);
+}
diff --git a/chrome/browser/ui/side_search/side_search_config.h b/chrome/browser/ui/side_search/side_search_config.h
new file mode 100644
index 0000000..4bd57d7
--- /dev/null
+++ b/chrome/browser/ui/side_search/side_search_config.h
@@ -0,0 +1,57 @@
+// 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 CHROME_BROWSER_UI_SIDE_SEARCH_SIDE_SEARCH_CONFIG_H_
+#define CHROME_BROWSER_UI_SIDE_SEARCH_SIDE_SEARCH_CONFIG_H_
+
+#include "base/callback.h"
+#include "base/supports_user_data.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+class GURL;
+
+// Stores per-profile configuration data for side search.
+class SideSearchConfig : public base::SupportsUserData::Data {
+ public:
+  using URLTestConditionCallback = base::RepeatingCallback<bool(const GURL&)>;
+
+  SideSearchConfig();
+  SideSearchConfig(const SideSearchConfig&) = delete;
+  SideSearchConfig& operator=(const SideSearchConfig&) = delete;
+  ~SideSearchConfig() override;
+
+  // Gets the instance of the config for `context`.
+  static SideSearchConfig* Get(content::BrowserContext* context);
+
+  // Returns whether a `url` in the side panel should be allowed to commit in
+  // the side panel or if it should be redirected to the content frame.
+  bool ShouldNavigateInSidePanel(const GURL& url);
+  void SetShouldNavigateInSidePanelCalback(URLTestConditionCallback callback);
+
+  // Returns whether the side panel can be shown for the `url`. This is used to
+  // avoid having the side panel on pages on which it doesn't make sense to have
+  // it appear (e.g. NTP).
+  bool CanShowSidePanelForURL(const GURL& url);
+  void SetCanShowSidePanelForURLCallback(URLTestConditionCallback callback);
+
+  // Gets and sets the bit that determines whether or not the SRP is available.
+  // TODO(tluk): Move the code that tests for availability into this class.
+  bool is_side_panel_srp_available() { return is_side_panel_srp_available_; }
+  void set_is_side_panel_srp_available(bool is_side_panel_srp_available) {
+    is_side_panel_srp_available_ = is_side_panel_srp_available;
+  }
+
+ private:
+  // Whether or not the service providing the SRP for the side panel is
+  // available or not.
+  bool is_side_panel_srp_available_ = false;
+
+  URLTestConditionCallback should_navigate_in_side_panel_callback_;
+  URLTestConditionCallback can_show_side_panel_for_url_callback_;
+};
+
+#endif  // CHROME_BROWSER_UI_SIDE_SEARCH_SIDE_SEARCH_CONFIG_H_
diff --git a/chrome/browser/ui/side_search/side_search_side_contents_helper.cc b/chrome/browser/ui/side_search/side_search_side_contents_helper.cc
index 9ab00f0..e3271b3 100644
--- a/chrome/browser/ui/side_search/side_search_side_contents_helper.cc
+++ b/chrome/browser/ui/side_search/side_search_side_contents_helper.cc
@@ -4,12 +4,13 @@
 
 #include "chrome/browser/ui/side_search/side_search_side_contents_helper.h"
 
+#include "chrome/browser/ui/side_search/side_search_config.h"
 #include "chrome/browser/ui/side_search/side_search_metrics.h"
 #include "chrome/browser/ui/side_search/side_search_tab_contents_helper.h"
-#include "components/google/core/common/google_util.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/page_navigator.h"
+#include "content/public/browser/web_contents.h"
 #include "net/base/url_util.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "url/gurl.h"
@@ -47,7 +48,10 @@
     const auto& url = navigation_handle()->GetURL();
 
     // Allow Google search navigations to proceed in the side panel.
-    if (google_util::IsGoogleSearchUrl(url)) {
+    auto* config = SideSearchConfig::Get(
+        navigation_handle()->GetWebContents()->GetBrowserContext());
+    DCHECK(config);
+    if (config->ShouldNavigateInSidePanel(url)) {
       return NavigationThrottle::PROCEED;
     }
 
@@ -106,7 +110,7 @@
   }
 
   const auto& url = navigation_handle->GetURL();
-  DCHECK(google_util::IsGoogleSearchUrl(url));
+  DCHECK(GetConfig()->ShouldNavigateInSidePanel(url));
   DCHECK(delegate_);
   delegate_->LastSearchURLUpdated(url);
 
@@ -151,7 +155,7 @@
 }
 
 void SideSearchSideContentsHelper::LoadURL(const GURL& url) {
-  DCHECK(google_util::IsGoogleSearchUrl(url));
+  DCHECK(GetConfig()->ShouldNavigateInSidePanel(url));
 
   // Do not reload the side contents if already navigated to `url`.
   if (web_contents()->GetLastCommittedURL() == url)
@@ -210,4 +214,8 @@
   redirection_to_tab_count_ = 0;
 }
 
+SideSearchConfig* SideSearchSideContentsHelper::GetConfig() {
+  return SideSearchConfig::Get(web_contents()->GetBrowserContext());
+}
+
 WEB_CONTENTS_USER_DATA_KEY_IMPL(SideSearchSideContentsHelper);
diff --git a/chrome/browser/ui/side_search/side_search_side_contents_helper.h b/chrome/browser/ui/side_search/side_search_side_contents_helper.h
index cea37f9..366ef407 100644
--- a/chrome/browser/ui/side_search/side_search_side_contents_helper.h
+++ b/chrome/browser/ui/side_search/side_search_side_contents_helper.h
@@ -17,6 +17,7 @@
 }  // namespace content
 
 class GURL;
+class SideSearchConfig;
 
 // Side Search helper for the WebContents hosted in the side panel.
 class SideSearchSideContentsHelper
@@ -91,6 +92,8 @@
   // Emits metrics data for the previous user journey if present.
   void MaybeRecordMetricsPerJourney();
 
+  SideSearchConfig* GetConfig();
+
   // `delegate_` will outlive the SideContentsWrapper.
   Delegate* delegate_ = nullptr;
 
diff --git a/chrome/browser/ui/side_search/side_search_tab_contents_helper.cc b/chrome/browser/ui/side_search/side_search_tab_contents_helper.cc
index c715422..449dd9f 100644
--- a/chrome/browser/ui/side_search/side_search_tab_contents_helper.cc
+++ b/chrome/browser/ui/side_search/side_search_tab_contents_helper.cc
@@ -6,24 +6,16 @@
 
 #include "chrome/browser/task_manager/web_contents_tags.h"
 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
-#include "chrome/common/webui_url_constants.h"
-#include "components/google/core/common/google_util.h"
+#include "chrome/browser/ui/side_search/side_search_config.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
 #include "net/http/http_request_headers.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 
-namespace {
-
-// A flag to track whether the last call to `TestSRPAvailability()` returned
-// successfully. Currently only called once per browser session.
-bool g_is_side_panel_srp_available = false;
-
-}  // namespace
-
 SideSearchTabContentsHelper::~SideSearchTabContentsHelper() = default;
 
 void SideSearchTabContentsHelper::NavigateInTabContents(
@@ -33,7 +25,7 @@
 }
 
 void SideSearchTabContentsHelper::LastSearchURLUpdated(const GURL& url) {
-  DCHECK(google_util::IsGoogleSearchUrl(url));
+  DCHECK(GetConfig()->ShouldNavigateInSidePanel(url));
   last_search_url_ = url;
 }
 
@@ -58,8 +50,9 @@
   }
 
   const auto& url = navigation_handle->GetURL();
+  auto* config = GetConfig();
 
-  if (google_util::IsGoogleSearchUrl(url)) {
+  if (config->ShouldNavigateInSidePanel(url)) {
     returned_to_previous_srp_ = navigation_handle->GetPageTransition() &
                                 ui::PAGE_TRANSITION_FORWARD_BACK;
 
@@ -67,7 +60,7 @@
     // navigation completes.
     last_search_url_ = url;
 
-    if (!g_is_side_panel_srp_available)
+    if (!config->is_side_panel_srp_available())
       TestSRPAvailability();
 
     if (side_panel_contents_)
@@ -102,10 +95,7 @@
 
 bool SideSearchTabContentsHelper::CanShowSidePanelForCommittedNavigation() {
   const GURL& url = web_contents()->GetLastCommittedURL();
-  return last_search_url_ && g_is_side_panel_srp_available &&
-         !google_util::IsGoogleSearchUrl(url) &&
-         !google_util::IsGoogleHomePageUrl(url) &&
-         url.spec() != chrome::kChromeUINewTabURL;
+  return last_search_url_ && GetConfig()->CanShowSidePanelForURL(url);
 }
 
 void SideSearchTabContentsHelper::SetDelegate(
@@ -121,11 +111,6 @@
   GetSideContentsHelper()->SetDelegate(this);
 }
 
-void SideSearchTabContentsHelper::SetIsSidePanelSRPAvailableForTesting(
-    bool is_side_panel_srp_available) {
-  g_is_side_panel_srp_available = is_side_panel_srp_available;
-}
-
 SideSearchTabContentsHelper::SideSearchTabContentsHelper(
     content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents) {}
@@ -154,17 +139,17 @@
   DCHECK(side_panel_contents_);
   // Only update the side panel contents with the latest `last_search_url_` if
   // present
-  if (last_search_url_ && g_is_side_panel_srp_available)
+  if (last_search_url_ && GetConfig()->is_side_panel_srp_available())
     GetSideContentsHelper()->LoadURL(last_search_url_.value());
 }
 
 void SideSearchTabContentsHelper::TestSRPAvailability() {
-  if (g_is_side_panel_srp_available)
+  if (GetConfig()->is_side_panel_srp_available())
     return;
   // TODO(tluk): Add rate limiting to the SRP test to permanently disable the
   // feature for a given session if the availability check fails enough times.
   DCHECK(last_search_url_.has_value());
-  DCHECK(google_util::IsGoogleSearchUrl(last_search_url_.value()));
+  DCHECK(GetConfig()->ShouldNavigateInSidePanel(last_search_url_.value()));
   auto traffic_annotation =
       net::DefineNetworkTrafficAnnotation("side_search_availability_test", R"(
         semantics {
@@ -208,7 +193,8 @@
 
 void SideSearchTabContentsHelper::OnResponseLoaded(
     scoped_refptr<net::HttpResponseHeaders> headers) {
-  g_is_side_panel_srp_available = simple_loader_->NetError() == net::OK;
+  GetConfig()->set_is_side_panel_srp_available(simple_loader_->NetError() ==
+                                               net::OK);
 
   // The test for availability is performed async so alert `delegate_` that the
   // side panel SRP is available to give it the opportunity to update
@@ -217,4 +203,8 @@
     delegate_->SidePanelAvailabilityChanged(false);
 }
 
+SideSearchConfig* SideSearchTabContentsHelper::GetConfig() {
+  return SideSearchConfig::Get(web_contents()->GetBrowserContext());
+}
+
 WEB_CONTENTS_USER_DATA_KEY_IMPL(SideSearchTabContentsHelper);
diff --git a/chrome/browser/ui/side_search/side_search_tab_contents_helper.h b/chrome/browser/ui/side_search/side_search_tab_contents_helper.h
index 97bfb32..0246591 100644
--- a/chrome/browser/ui/side_search/side_search_tab_contents_helper.h
+++ b/chrome/browser/ui/side_search/side_search_tab_contents_helper.h
@@ -20,6 +20,7 @@
 }  // namespace network
 
 class GURL;
+class SideSearchConfig;
 
 // Side Search helper for the WebContents hosted in the browser's main tab area.
 class SideSearchTabContentsHelper
@@ -86,8 +87,6 @@
   void SetSidePanelContentsForTesting(
       std::unique_ptr<content::WebContents> side_panel_contents);
 
-  void SetIsSidePanelSRPAvailableForTesting(bool is_side_panel_srp_available);
-
   content::WebContents* side_panel_contents_for_testing() const {
     return side_panel_contents_.get();
   }
@@ -116,6 +115,8 @@
   void TestSRPAvailability();
   void OnResponseLoaded(scoped_refptr<net::HttpResponseHeaders> headers);
 
+  SideSearchConfig* GetConfig();
+
   // Use a weak ptr for the delegate to avoid issues whereby the tab contents
   // could outlive the delegate.
   base::WeakPtr<Delegate> delegate_;
diff --git a/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc b/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc
index 718b4ac8..4433e06b 100644
--- a/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc
+++ b/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ui/side_search/side_search_config.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/browser_context.h"
@@ -51,7 +52,7 @@
     SideSearchTabContentsHelper::CreateForWebContents(web_contents_.get());
     helper()->SetSidePanelContentsForTesting(
         content::WebContentsTester::CreateTestWebContents(&profile_, nullptr));
-    helper()->SetIsSidePanelSRPAvailableForTesting(true);
+    SideSearchConfig::Get(&profile_)->set_is_side_panel_srp_available(true);
     Test::SetUp();
   }
 
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_panel.cc
index 3db8354..d35c2c0 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_panel.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_panel.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/views/apps/app_info_dialog/app_info_label.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "extensions/common/extension.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/geometry/insets.h"
@@ -24,6 +25,8 @@
 
 AppInfoPanel::AppInfoPanel(Profile* profile, const extensions::Extension* app)
     : profile_(profile), app_(app) {
+  // Bookmark Apps have been replaced by Web Apps.
+  DCHECK(!app_->from_bookmark());
 }
 
 AppInfoPanel::~AppInfoPanel() {
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_panel.h b/chrome/browser/ui/views/apps/app_info_dialog/app_info_panel.h
index 2a12ad74..1a9c47b 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_panel.h
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_panel.h
@@ -59,8 +59,8 @@
       std::unique_ptr<views::View> key,
       std::unique_ptr<views::View> value) const;
 
-  Profile* profile_;
-  const extensions::Extension* app_;
+  Profile* const profile_;
+  const extensions::Extension* const app_;
 };
 
 BEGIN_VIEW_BUILDER(/* no export */, AppInfoPanel, views::View)
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_summary_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_summary_panel.cc
index 43d1596..319e8f24 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_summary_panel.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_summary_panel.cc
@@ -175,17 +175,14 @@
   details_list->AddChildView(
       CreateKeyValueField(std::move(size_title), std::move(size_value)));
 
-  // The version doesn't make sense for bookmark apps.
-  if (!app_->from_bookmark()) {
-    auto version_title = std::make_unique<AppInfoLabel>(
-        l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_VERSION_LABEL));
+  auto version_title = std::make_unique<AppInfoLabel>(
+      l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_VERSION_LABEL));
 
-    auto version_value = std::make_unique<AppInfoLabel>(
-        base::UTF8ToUTF16(app_->GetVersionForDisplay()));
+  auto version_value = std::make_unique<AppInfoLabel>(
+      base::UTF8ToUTF16(app_->GetVersionForDisplay()));
 
-    details_list->AddChildView(CreateKeyValueField(std::move(version_title),
-                                                   std::move(version_value)));
-  }
+  details_list->AddChildView(
+      CreateKeyValueField(std::move(version_title), std::move(version_value)));
 
   vertical_stack->AddChildView(std::move(details_list));
 }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
index 2731df1..e003075 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -184,7 +184,7 @@
           .SetTitle(l10n_util::GetStringUTF16(
               already_bookmarked ? IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARK
                                  : IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED))
-          .SetWindowClosingCallback(
+          .SetDialogDestroyingCallback(
               base::BindOnce(&BookmarkBubbleDelegate::OnWindowClosing,
                              base::Unretained(bubble_delegate)))
           .AddOkButton(base::BindOnce(&BookmarkBubbleDelegate::ApplyEdits,
diff --git a/chrome/browser/ui/views/direct_sockets_connection_bubble_dialog.cc b/chrome/browser/ui/views/direct_sockets_connection_bubble_dialog.cc
index 9a08f67..3fc1b40 100644
--- a/chrome/browser/ui/views/direct_sockets_connection_bubble_dialog.cc
+++ b/chrome/browser/ui/views/direct_sockets_connection_bubble_dialog.cc
@@ -71,7 +71,7 @@
       ui::DialogModel::Builder(std::move(bubble_delegate_unique))
           .SetTitle(l10n_util::GetStringUTF16(
               IDS_DIRECT_SOCKETS_CONNECTION_BUBBLE_TITLE_LABEL))
-          .SetWindowClosingCallback(
+          .SetDialogDestroyingCallback(
               base::BindOnce(&DirectSocketsConnectionBubbleDelegate::OnClose,
                              base::Unretained(bubble_delegate)))
           .AddOkButton(
diff --git a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc
index 28ac557..9c25f33d 100644
--- a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc
@@ -93,7 +93,7 @@
           l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_TITLE,
                                      base::UTF8ToUTF16(extension()->name())))
       .OverrideShowCloseButton(false)
-      .SetWindowClosingCallback(
+      .SetDialogDestroyingCallback(
           base::BindOnce(&ExtensionUninstallDialogViews::DialogClosing,
                          weak_ptr_factory_.GetWeakPtr()))
       .SetIcon(ui::ImageModel::FromImageSkia(
diff --git a/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc b/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc
index 3dc38bb..0ee087ce 100644
--- a/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc
+++ b/chrome/browser/ui/views/outdated_upgrade_bubble_view.cc
@@ -137,8 +137,8 @@
                                             : IDS_REENABLE_UPDATES))
           .AddBodyText(
               ui::DialogModelLabel(IDS_UPGRADE_BUBBLE_TEXT).set_is_secondary())
-          .SetWindowClosingCallback(base::BindOnce(&OnWindowClosing))
-          .SetCloseCallback(base::BindOnce(
+          .SetDialogDestroyingCallback(base::BindOnce(&OnWindowClosing))
+          .SetCloseActionCallback(base::BindOnce(
               &base::RecordAction,
               base::UserMetricsAction("OutdatedUpgradeBubble.Later")))
           .Build();
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
index d94e202..b8638ec 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
 #include "chrome/browser/web_applications/install_bounce_metric.h"
 #include "chrome/browser/web_applications/os_integration_manager.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_install_finalizer.h"
 #include "chrome/browser/web_applications/web_app_prefs_utils.h"
@@ -166,6 +167,8 @@
     web_contents_ = GetCurrentTab();
     app_banner_manager_ =
         webapps::TestAppBannerManagerDesktop::FromWebContents(web_contents_);
+    web_app::test::WaitUntilReady(
+        web_app::WebAppProvider::GetForTest(browser()->profile()));
   }
 
   std::unique_ptr<net::test_server::HttpResponse> RequestInterceptor(
diff --git a/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc b/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
index 6ae7d54..4d965aa 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_interactive_uitest.cc
@@ -60,9 +60,10 @@
   ProfilePickerInteractiveUiTest() = default;
   ~ProfilePickerInteractiveUiTest() override = default;
 
-  void ShowAndFocusPicker(ProfilePicker::EntryPoint entry_point) {
+  void ShowAndFocusPicker(ProfilePicker::EntryPoint entry_point,
+                          const GURL& expected_url) {
     ProfilePicker::Show(entry_point);
-    WaitForLayoutWithoutToolbar();
+    WaitForLoadStop(expected_url);
     EXPECT_TRUE(
         ui_test_utils::ShowAndFocusNativeWindow(widget()->GetNativeWindow()));
   }
@@ -120,8 +121,8 @@
 // Checks that the main picker view can be closed with keyboard shortcut.
 IN_PROC_BROWSER_TEST_F(ProfilePickerInteractiveUiTest, CloseWithKeyboard) {
   // Open a new picker.
-  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLoadStop(web_contents(), GURL("chrome://profile-picker"));
+  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuManageProfiles,
+                     GURL("chrome://profile-picker"));
   EXPECT_TRUE(ProfilePicker::IsOpen());
   SendCloseWindowKeyboardCommand();
   WaitForPickerClosed();
@@ -134,8 +135,8 @@
 // keyboard shortcut to exit Chrome.
 IN_PROC_BROWSER_TEST_F(ProfilePickerInteractiveUiTest, ExitWithKeyboard) {
   // Open a new picker.
-  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLoadStop(web_contents(), GURL("chrome://profile-picker"));
+  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuManageProfiles,
+                     GURL("chrome://profile-picker"));
   EXPECT_TRUE(ProfilePicker::IsOpen());
 
   content::WindowedNotificationObserver terminate_observer(
@@ -154,8 +155,8 @@
 // Checks that the main picker view can switch to full screen.
 IN_PROC_BROWSER_TEST_F(ProfilePickerInteractiveUiTest, FullscreenWithKeyboard) {
   // Open a new picker.
-  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLoadStop(web_contents(), GURL("chrome://profile-picker"));
+  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuManageProfiles,
+                     GURL("chrome://profile-picker"));
   EXPECT_TRUE(ProfilePicker::IsOpen());
   EXPECT_FALSE(widget()->IsFullscreen());
   WidgetBoundsChangeWaiter bounds_waiter(widget());
@@ -187,7 +188,8 @@
 #endif
 IN_PROC_BROWSER_TEST_F(ProfilePickerInteractiveUiTest,
                        MAYBE_CloseSigninWithKeyboard) {
-  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
+  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile,
+                     GURL("chrome://profile-picker/new-profile"));
 
   // Simulate a click on the signin button.
   base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
@@ -196,9 +198,7 @@
                                     switch_finished_callback.Get());
 
   // Switch to the signin webview.
-  WaitForLayoutWithToolbar();
-  WaitForLoadStop(web_contents(),
-                  GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+  WaitForLoadStop(GaiaUrls::GetInstance()->signin_chrome_sync_dice());
 
   // Close the picker with the keyboard.
   EXPECT_TRUE(ProfilePicker::IsOpen());
@@ -219,14 +219,14 @@
   // Simulate walking through the flow starting at the picker so that navigating
   // back to the picker makes sense. Check that the navigation list is populated
   // correctly.
-  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLoadStop(web_contents(), GURL("chrome://profile-picker"));
+  ShowAndFocusPicker(ProfilePicker::EntryPoint::kProfileMenuManageProfiles,
+                     GURL("chrome://profile-picker"));
   EXPECT_EQ(1, web_contents()->GetController().GetEntryCount());
   EXPECT_EQ(0, web_contents()->GetController().GetLastCommittedEntryIndex());
   web_contents()->GetController().LoadURL(
       GURL("chrome://profile-picker/new-profile"), content::Referrer(),
       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
-  WaitForLoadStop(web_contents(), GURL("chrome://profile-picker/new-profile"));
+  WaitForLoadStop(GURL("chrome://profile-picker/new-profile"));
   EXPECT_EQ(2, web_contents()->GetController().GetEntryCount());
   EXPECT_EQ(1, web_contents()->GetController().GetLastCommittedEntryIndex());
 
@@ -237,19 +237,16 @@
                                     switch_finished_callback.Get());
 
   // Switch to the signin webview.
-  WaitForLayoutWithToolbar();
-  WaitForLoadStop(web_contents(),
-                  GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+  WaitForLoadStop(GaiaUrls::GetInstance()->signin_chrome_sync_dice());
 
   // Navigate back with the keyboard.
   SendBackKeyboardCommand();
-  WaitForLayoutWithoutToolbar();
-  WaitForLoadStop(web_contents(), GURL("chrome://profile-picker/new-profile"));
+  WaitForLoadStop(GURL("chrome://profile-picker/new-profile"));
   EXPECT_EQ(1, web_contents()->GetController().GetLastCommittedEntryIndex());
 
   // Navigate again back with the keyboard.
   SendBackKeyboardCommand();
-  WaitForLoadStop(web_contents(), GURL("chrome://profile-picker"));
+  WaitForLoadStop(GURL("chrome://profile-picker"));
   EXPECT_EQ(0, web_contents()->GetController().GetLastCommittedEntryIndex());
 
   // Navigating back once again does nothing.
diff --git a/chrome/browser/ui/views/profiles/profile_picker_test_base.cc b/chrome/browser/ui/views/profiles/profile_picker_test_base.cc
index f49a10fa..b0ee655f 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_test_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_test_base.cc
@@ -19,15 +19,14 @@
 
 namespace {
 
-// Waits until a view's visibility has the expected value.
-class ViewVisibilityChangedWaiter : public views::ViewObserver {
+// Waits until a view gets attached to its widget.
+class WidgetAttachedWaiter : public views::ViewObserver {
  public:
-  ViewVisibilityChangedWaiter(views::View* view, bool expect_toolbar_visible)
-      : view_(view), expect_toolbar_visible_(expect_toolbar_visible) {}
-  ~ViewVisibilityChangedWaiter() override = default;
+  explicit WidgetAttachedWaiter(views::View* view) : view_(view) {}
+  ~WidgetAttachedWaiter() override = default;
 
   void Wait() {
-    if (view_->GetVisible() == expect_toolbar_visible_)
+    if (view_->GetWidget())
       return;
     observation_.Observe(view_);
     run_loop_.Run();
@@ -35,17 +34,13 @@
 
  private:
   // ViewObserver:
-  void OnViewVisibilityChanged(views::View* observed_view,
-                               views::View* starting_view) override {
-    if (observed_view == starting_view &&
-        starting_view->GetVisible() == expect_toolbar_visible_) {
+  void OnViewAddedToWidget(views::View* observed_view) override {
+    if (observed_view == view_)
       run_loop_.Quit();
-    }
   }
 
   base::RunLoop run_loop_;
   views::View* const view_;
-  bool expect_toolbar_visible_;
   base::ScopedObservation<views::View, views::ViewObserver> observation_{this};
 };
 
@@ -91,28 +86,23 @@
   return ProfilePicker::GetWebViewForTesting();
 }
 
-void ProfilePickerTestBase::WaitForLayoutWithToolbar() {
-  ViewVisibilityChangedWaiter(ProfilePicker::GetToolbarForTesting(),
-                              /*expect_toolbar_visible=*/true)
-      .Wait();
+void ProfilePickerTestBase::WaitForPickerWidgetCreated() {
+  WidgetAttachedWaiter(view()).Wait();
 }
 
-void ProfilePickerTestBase::WaitForLayoutWithoutToolbar() {
-  ViewVisibilityChangedWaiter(ProfilePicker::GetToolbarForTesting(),
-                              /*expect_toolbar_visible=*/false)
-      .Wait();
-}
-
-void ProfilePickerTestBase::WaitForLoadStop(content::WebContents* contents,
-                                            const GURL& url) {
-  DCHECK(contents);
-  if (contents->GetLastCommittedURL() == url && !contents->IsLoading())
+void ProfilePickerTestBase::WaitForLoadStop(const GURL& url,
+                                            content::WebContents* target) {
+  content::WebContents* wc = target ? target : web_contents();
+  if (wc && wc->GetLastCommittedURL() == url && !wc->IsLoading())
     return;
 
   ui_test_utils::UrlLoadObserver url_observer(
       url, content::NotificationService::AllSources());
   url_observer.Wait();
-  EXPECT_EQ(contents->GetLastCommittedURL(), url);
+
+  // Update the pointer (as web_contents() could have changed in the mean-time).
+  wc = target ? target : web_contents();
+  EXPECT_EQ(wc->GetLastCommittedURL(), url);
 }
 
 void ProfilePickerTestBase::WaitForPickerClosed() {
diff --git a/chrome/browser/ui/views/profiles/profile_picker_test_base.h b/chrome/browser/ui/views/profiles/profile_picker_test_base.h
index c712ec3..6940fea50b 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_test_base.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_test_base.h
@@ -33,13 +33,15 @@
   // Returns the internal web view for the profile picker.
   views::WebView* web_view();
 
-  // Waits until a relayout of the main view has been performed. This implies
-  // the appropriate web_contents() is attached to the layout.
-  void WaitForLayoutWithToolbar();
-  void WaitForLayoutWithoutToolbar();
+  // Wait until the widget of the picker gets created and the initialization of
+  // the picker is thus finished (and notably `widget()` is not null).
+  void WaitForPickerWidgetCreated();
 
-  // Waits until the web contents stops loading `url`.
-  void WaitForLoadStop(content::WebContents* contents, const GURL& url);
+  // Waits until `target` WebContents stops loading `url`. If no `target` is
+  // provided, it checks for the current `web_contents()` to stop loading `url`.
+  // This also works if `web_contents()` changes throughout the waiting as it is
+  // technically observing all web contents.
+  void WaitForLoadStop(const GURL& url, content::WebContents* target = nullptr);
 
   // Waits until the picker gets closed.
   void WaitForPickerClosed();
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index a6980dea..32c12dd 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -241,17 +241,6 @@
 }
 
 // static
-views::View* ProfilePicker::GetToolbarForTesting() {
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
-  if (!g_profile_picker_view)
-    return nullptr;
-  return g_profile_picker_view->toolbar_;
-#else
-  return nullptr;
-#endif
-}
-
-// static
 void ProfilePicker::AddOnProfilePickerOpenedCallbackForTesting(
     base::OnceClosure callback) {
   DCHECK(!g_profile_picker_opened_callback_for_testing);
@@ -552,11 +541,6 @@
 #endif
 
   ShowScreenInSystemContents(CreateURLForEntryPoint(entry_point_));
-#if BUILDFLAG(ENABLE_DICE_SUPPORT)
-  // It's important for tests that the toolbar container starts visible (but
-  // empty) and gets hidden later when setting up the layout.
-  toolbar_->SetVisible(false);
-#endif
   GetWidget()->Show();
   state_ = kReady;
 
@@ -785,9 +769,9 @@
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
   auto toolbar = std::make_unique<ProfilePickerDiceSignInToolbar>();
-  // It is important for tests that the top container starts visible (being
-  // empty).
   toolbar_ = AddChildView(std::move(toolbar));
+  // Toolbar gets built and set visible once we it's needed for the Dice signin.
+  toolbar_->SetVisible(false);
 #endif
 
   auto web_view = std::make_unique<views::WebView>();
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
index 42b8034..c28616d 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -281,9 +281,8 @@
   // profile that was created.
   Profile* StartSigninFlow() {
     ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-    WaitForLayoutWithoutToolbar();
     // Wait until webUI is fully initialized.
-    content::WaitForLoadStop(web_contents());
+    WaitForLoadStop(GURL("chrome://profile-picker/new-profile"));
 
     // Simulate a click on the signin button.
     base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
@@ -293,9 +292,7 @@
 
     // The DICE navigation happens in a new web contents (for the profile being
     // created), wait for it.
-    WaitForLayoutWithToolbar();
-    WaitForLoadStop(web_contents(),
-                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+    WaitForLoadStop(GaiaUrls::GetInstance()->signin_chrome_sync_dice());
     return static_cast<Profile*>(web_contents()->GetBrowserContext());
   }
 
@@ -399,25 +396,25 @@
 
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, ShowPicker) {
   ProfilePicker::Show(ProfilePicker::EntryPoint::kOnStartup);
-  WaitForLayoutWithoutToolbar();
   EXPECT_TRUE(ProfilePicker::IsOpen());
+  WaitForPickerWidgetCreated();
   // Check that non-default accessible title is provided both before the page
   // loads and after it loads.
   views::WidgetDelegate* delegate = widget()->widget_delegate();
   EXPECT_NE(delegate->GetWindowTitle(), delegate->GetAccessibleWindowTitle());
-  WaitForLoadStop(web_contents(), GURL("chrome://profile-picker"));
+  WaitForLoadStop(GURL("chrome://profile-picker"));
   EXPECT_NE(delegate->GetWindowTitle(), delegate->GetAccessibleWindowTitle());
 }
 
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, ShowChoice) {
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  WaitForLayoutWithoutToolbar();
   EXPECT_TRUE(ProfilePicker::IsOpen());
+  WaitForPickerWidgetCreated();
   // Check that non-default accessible title is provided both before the page
   // loads and after it loads.
   views::WidgetDelegate* delegate = widget()->widget_delegate();
   EXPECT_NE(delegate->GetWindowTitle(), delegate->GetAccessibleWindowTitle());
-  WaitForLoadStop(web_contents(), GURL("chrome://profile-picker/new-profile"));
+  WaitForLoadStop(GURL("chrome://profile-picker/new-profile"));
   EXPECT_NE(delegate->GetWindowTitle(), delegate->GetAccessibleWindowTitle());
 }
 
@@ -431,14 +428,14 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in sync
   // confirmation screen getting displayed.
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/"));
+  WaitForLoadStop(GURL("chrome://sync-confirmation/"));
 
   // Simulate closing the UI with "No, thanks".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::ABORT_SYNC);
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://newtab/"));
+  WaitForLoadStop(GURL("chrome://newtab/"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
 
   // Check expectations when the profile creation flow is done.
   WaitForPickerClosed();
@@ -468,7 +465,7 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in sync
   // confirmation screen getting displayed.
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/"));
+  WaitForLoadStop(GURL("chrome://sync-confirmation/"));
 
   // Close the flow with the [X] button.
   widget()->CloseWithReason(views::Widget::ClosedReason::kCloseButtonClicked);
@@ -480,14 +477,14 @@
 
   // As the flow for `profile_to_cancel` got aborted, it's disregarded. Instead
   // of the profile switch screen, the normal sync confirmation should appear.
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/"));
+  WaitForLoadStop(GURL("chrome://sync-confirmation/"));
 
   // Simulate closing the UI with "No, thanks".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::ABORT_SYNC);
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://newtab/"));
+  WaitForLoadStop(GURL("chrome://newtab/"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
 
   // Check expectations when the profile creation flow is done.
   WaitForPickerClosed();
@@ -524,14 +521,14 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in sync
   // confirmation screen getting displayed.
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/"));
+  WaitForLoadStop(GURL("chrome://sync-confirmation/"));
 
   // Simulate closing the UI with "No, thanks".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::ABORT_SYNC);
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://newtab/"));
+  WaitForLoadStop(GURL("chrome://newtab/"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
 
   // Check expectations when the profile creation flow is done.
   WaitForPickerClosed();
@@ -560,14 +557,14 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in sync
   // confirmation screen getting displayed.
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/"));
+  WaitForLoadStop(GURL("chrome://sync-confirmation/"));
 
   // Simulate closing the UI with "Yes, I'm in".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::CONFIGURE_SYNC_FIRST);
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://settings/syncSetup"));
+  WaitForLoadStop(GURL("chrome://settings/syncSetup"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
 
   // Check expectations when the profile creation flow is done.
   WaitForPickerClosed();
@@ -606,7 +603,7 @@
   // A new pppup browser is displayed (with the specified URL).
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
   EXPECT_EQ(new_browser->type(), Browser::TYPE_POPUP);
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(), kURL);
+  WaitForLoadStop(kURL, new_browser->tab_strip_model()->GetActiveWebContents());
 }
 
 // Regression test for crbug.com/1219980.
@@ -648,14 +645,14 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in sync
   // confirmation screen getting displayed.
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/"));
+  WaitForLoadStop(GURL("chrome://sync-confirmation/"));
 
   // Simulate closing the UI with "Yes, I'm in".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::ABORT_SYNC);
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://newtab/"));
+  WaitForLoadStop(GURL("chrome://newtab/"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
 
   // Check expectations when the profile creation flow is done.
   WaitForPickerClosed();
@@ -687,7 +684,7 @@
   wc->GetController().LoadURL(kNonGaiaURL, content::Referrer(),
                               ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(wc, kNonGaiaURL);
+  WaitForLoadStop(kNonGaiaURL, wc);
   WaitForPickerClosed();
 
   // Check that the web contents got actually moved to the browser.
@@ -773,7 +770,7 @@
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
                        OpenPickerAndClose) {
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLayoutWithoutToolbar();
+  WaitForLoadStop(GURL("chrome://profile-picker"));
   EXPECT_TRUE(ProfilePicker::IsOpen());
   ProfilePicker::Hide();
   WaitForPickerClosed();
@@ -784,7 +781,7 @@
                        OpenPickerWhileClosing) {
   // Open the first picker.
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLayoutWithoutToolbar();
+  WaitForLoadStop(GURL("chrome://profile-picker"));
   EXPECT_TRUE(ProfilePicker::IsOpen());
 
   // Request to open the second picker window while the first one is still
@@ -805,13 +802,13 @@
   base::FilePath other_path = CreateNewProfileWithoutBrowser();
   // Open the picker.
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLayoutWithoutToolbar();
+  WaitForLoadStop(GURL("chrome://profile-picker"));
   // Open the other profile.
   OpenProfileFromPicker(other_path, /*open_settings=*/false);
   // Browser for the profile is displayed.
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://newtab/"));
+  WaitForLoadStop(GURL("chrome://newtab/"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
   EXPECT_EQ(new_browser->profile()->GetPath(), other_path);
   WaitForPickerClosed();
   // IPH is shown.
@@ -826,13 +823,13 @@
   base::FilePath other_path = CreateNewProfileWithoutBrowser();
   // Open the picker.
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLayoutWithoutToolbar();
+  WaitForLoadStop(GURL("chrome://profile-picker"));
   // Open the other profile.
   OpenProfileFromPicker(other_path, /*open_settings=*/true);
   // Browser for the profile is displayed.
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://settings/manageProfile"));
+  WaitForLoadStop(GURL("chrome://settings/manageProfile"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
   EXPECT_EQ(new_browser->profile()->GetPath(), other_path);
   WaitForPickerClosed();
   // IPH is not shown.
@@ -848,13 +845,13 @@
   // Open the picker.
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles,
                       kTargetURL);
-  WaitForLayoutWithoutToolbar();
+  WaitForLoadStop(GURL("chrome://profile-picker"));
   // Open the profile.
   OpenProfileFromPicker(profile_path, /*open_settings=*/false);
   // Browser for the profile is displayed.
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  kTargetURL);
+  WaitForLoadStop(kTargetURL,
+                  new_browser->tab_strip_model()->GetActiveWebContents());
   EXPECT_EQ(new_browser->profile()->GetPath(), profile_path);
   WaitForPickerClosed();
 }
@@ -867,7 +864,7 @@
   base::FilePath profile_path = CreateNewProfileWithoutBrowser();
   // Open the picker without target URL.
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLayoutWithoutToolbar();
+  WaitForLoadStop(GURL("chrome://profile-picker"));
   // Request a URL when the picker is already open.
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles,
                       kTargetURL);
@@ -875,8 +872,8 @@
   OpenProfileFromPicker(profile_path, /*open_settings=*/false);
   // Browser for the profile is displayed.
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  kTargetURL);
+  WaitForLoadStop(kTargetURL,
+                  new_browser->tab_strip_model()->GetActiveWebContents());
   EXPECT_EQ(new_browser->profile()->GetPath(), profile_path);
   WaitForPickerClosed();
 }
@@ -890,13 +887,13 @@
   base::FilePath other_path = CreateNewProfileWithoutBrowser();
   // Open the picker.
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLayoutWithoutToolbar();
+  WaitForLoadStop(GURL("chrome://profile-picker"));
   // Open a Guest profile.
   OpenGuestFromPicker();
   // Browser for the guest profile is displayed.
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://newtab"));
+  WaitForLoadStop(GURL("chrome://newtab"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
   EXPECT_TRUE(new_browser->profile()->IsGuestSession());
   WaitForPickerClosed();
   // IPH is not shown.
@@ -912,8 +909,7 @@
 
   // Open the picker.
   ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuManageProfiles);
-  WaitForLayoutWithoutToolbar();
-  WaitForLoadStop(web_contents(), GURL("chrome://profile-picker"));
+  WaitForLoadStop(GURL("chrome://profile-picker"));
 
   // Close the browser window.
   BrowserList::GetInstance()->CloseAllBrowsersWithProfile(browser()->profile());
@@ -976,21 +972,21 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in enterprise
   // welcome screen getting displayed.
-  WaitForLoadStop(web_contents(), GURL("chrome://enterprise-profile-welcome/"));
+  WaitForLoadStop(GURL("chrome://enterprise-profile-welcome/"));
 
   ExpectEnterpriseScreenTypeAndProceed(
       /*expected_type=*/EnterpriseProfileWelcomeUI::ScreenType::
           kEntepriseAccountSyncEnabled,
       /*proceed=*/true);
 
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/"));
+  WaitForLoadStop(GURL("chrome://sync-confirmation/"));
   // Simulate finishing the flow with "No, thanks".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::ABORT_SYNC);
 
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://newtab/"));
+  WaitForLoadStop(GURL("chrome://newtab/"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
   WaitForPickerClosed();
 
   // Check expectations when the profile creation flow is done.
@@ -1028,7 +1024,7 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in enterprise
   // welcome screen getting displayed.
-  WaitForLoadStop(web_contents(), GURL("chrome://enterprise-profile-welcome/"));
+  WaitForLoadStop(GURL("chrome://enterprise-profile-welcome/"));
 
   ExpectEnterpriseScreenTypeAndProceed(
       /*expected_type=*/EnterpriseProfileWelcomeUI::ScreenType::
@@ -1036,8 +1032,8 @@
       /*proceed=*/true);
 
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://newtab/"));
+  WaitForLoadStop(GURL("chrome://newtab/"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
   WaitForPickerClosed();
 
   // Check expectations when the profile creation flow is done.
@@ -1072,21 +1068,21 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in enterprise
   // welcome screen getting displayed.
-  WaitForLoadStop(web_contents(), GURL("chrome://enterprise-profile-welcome/"));
+  WaitForLoadStop(GURL("chrome://enterprise-profile-welcome/"));
 
   ExpectEnterpriseScreenTypeAndProceed(
       /*expected_type=*/EnterpriseProfileWelcomeUI::ScreenType::
           kEntepriseAccountSyncEnabled,
       /*proceed=*/true);
 
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/"));
+  WaitForLoadStop(GURL("chrome://sync-confirmation/"));
   // Simulate finishing the flow with "Configure sync".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::CONFIGURE_SYNC_FIRST);
 
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://settings/syncSetup"));
+  WaitForLoadStop(GURL("chrome://settings/syncSetup"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
   WaitForPickerClosed();
 
   // Check expectations when the profile creation flow is done.
@@ -1121,7 +1117,7 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in enterprise
   // welcome screen getting displayed.
-  WaitForLoadStop(web_contents(), GURL("chrome://enterprise-profile-welcome/"));
+  WaitForLoadStop(GURL("chrome://enterprise-profile-welcome/"));
 
   ProfileDeletionObserver observer;
   ExpectEnterpriseScreenTypeAndProceed(
@@ -1165,11 +1161,10 @@
   // Simulate a successful Gaia sign-in.
   SignIn(profile_being_created, "joe.consumer@gmail.com", "Joe");
 
-  // The profile switch screen should be displayed.
-  WaitForLayoutWithoutToolbar();
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/loading"));
-  WaitForLoadStop(web_contents(),
-                  GURL("chrome://profile-picker/profile-switch"));
+  // The profile switch screen should be displayed (in between,
+  // chrome://sync-confirmation/loading gets displayed but that page may not
+  // finish loading and anyway is not so relevant).
+  WaitForLoadStop(GURL("chrome://profile-picker/profile-switch"));
   EXPECT_EQ(ProfilePicker::GetSwitchProfilePath(), other_path);
 
   // Simulate clicking on the confirm switch button.
@@ -1180,8 +1175,8 @@
 
   // Browser for a pre-existing profile is displayed.
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://newtab/"));
+  WaitForLoadStop(GURL("chrome://newtab/"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
   EXPECT_EQ(new_browser->profile()->GetPath(), other_path);
 
   // Check expectations when the profile creation flow is done.
@@ -1215,11 +1210,10 @@
   // Simulate a successful Gaia sign-in.
   SignIn(profile_being_created, "joe.consumer@gmail.com", "Joe");
 
-  // The profile switch screen should be displayed.
-  WaitForLayoutWithoutToolbar();
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/loading"));
-  WaitForLoadStop(web_contents(),
-                  GURL("chrome://profile-picker/profile-switch"));
+  // The profile switch screen should be displayed (in between,
+  // chrome://sync-confirmation/loading gets displayed but that page may not
+  // finish loading and anyway is not so relevant).
+  WaitForLoadStop(GURL("chrome://profile-picker/profile-switch"));
   EXPECT_EQ(ProfilePicker::GetSwitchProfilePath(), other_path);
 
   // Simulate clicking on the cancel button.
@@ -1351,14 +1345,14 @@
 
   // Wait for the sign-in to propagate to the flow, resulting in sync
   // confirmation screen getting displayed.
-  WaitForLoadStop(web_contents(), GURL("chrome://sync-confirmation/"));
+  WaitForLoadStop(GURL("chrome://sync-confirmation/"));
 
   // Simulate closing the UI with "No, thanks".
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::ABORT_SYNC);
   Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForLoadStop(new_browser->tab_strip_model()->GetActiveWebContents(),
-                  GURL("chrome://newtab/"));
+  WaitForLoadStop(GURL("chrome://newtab/"),
+                  new_browser->tab_strip_model()->GetActiveWebContents());
 
   WaitForPickerClosed();
   EXPECT_EQ(2u, profile_manager()->GetNumberOfProfiles());
diff --git a/chrome/browser/ui/views/session_crashed_bubble_view.cc b/chrome/browser/ui/views/session_crashed_bubble_view.cc
index a78d5f1..bef9e38 100644
--- a/chrome/browser/ui/views/session_crashed_bubble_view.cc
+++ b/chrome/browser/ui/views/session_crashed_bubble_view.cc
@@ -248,7 +248,7 @@
       .SetTitle(l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_BUBBLE_TITLE))
       .DisableCloseOnDeactivate()
       .SetIsAlertDialog()
-      .SetWindowClosingCallback(
+      .SetDialogDestroyingCallback(
           base::BindOnce(&SessionCrashedBubbleDelegate::OnWindowClosing,
                          base::Unretained(bubble_delegate)))
       .AddBodyText(ui::DialogModelLabel(IDS_SESSION_CRASHED_VIEW_MESSAGE));
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller_browsertest.cc b/chrome/browser/ui/views/side_search/side_search_browser_controller_browsertest.cc
index d15514aa..852b169 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller_browsertest.cc
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller_browsertest.cc
@@ -10,13 +10,16 @@
 #include "build/build_config.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/side_search/side_search_config.h"
 #include "chrome/browser/ui/side_search/side_search_tab_contents_helper.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/google/core/common/google_util.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/result_codes.h"
@@ -29,32 +32,76 @@
 
 namespace {
 
-constexpr char kGoogleSearchURL[] = "https://www.google.com/search?q=test1";
-constexpr char kGoogleSearchHomePageURL[] = "https://www.google.com";
-constexpr char kNonGoogleURL[] = "https://www.test.com";
-
 ui::MouseEvent GetDummyEvent() {
   return ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::PointF(), gfx::PointF(),
                         base::TimeTicks::Now(), 0, 0);
 }
 
+// Strips out the port from a given `url`. Useful when testing side search with
+// the EmbeddedTestServer.
+GURL GetFilteredURL(const GURL& url) {
+  GURL filtered_url = url;
+  if (url.has_port()) {
+    GURL::Replacements remove_port;
+    remove_port.ClearPort();
+    filtered_url = filtered_url.ReplaceComponents(remove_port);
+  }
+  return filtered_url;
+}
+
+// In the spirit of testing for the current use of side search for the Google
+// DSE set the callback to allow Google SRP urls to proceed in the side panel.
+// Note we clear away the port here since google_util::IsGoogleSearchUrl does
+// not allow arbitrary ports in the url but the EmbeddedTestServer requires us
+// to use URLs with the specific non-standard port it listens on.
+// TODO(tluk): Eliminate the Google specific nature of both of these. We should
+// be able to update tests to use URLs that satisfy a combination of the below
+// two checks without needing such complicated and specific logic.
+bool ShouldNavigateInSidePanel(const GURL& url) {
+  return google_util::IsGoogleSearchUrl(GetFilteredURL(url));
+}
+
+bool CanShowSidePanelForURL(const GURL& url) {
+  GURL filtered_url = GetFilteredURL(url);
+  return !google_util::IsGoogleSearchUrl(filtered_url) &&
+         !google_util::IsGoogleHomePageUrl(filtered_url) &&
+         filtered_url.spec() != chrome::kChromeUINewTabURL;
+}
+
 }  // namespace
 
-// TODO(tluk): Add more tests for the different side panel configurations.
+// TODO(tluk): Refactor out google specific references and have this apply
+// more generically to DSEs.
 class SideSearchBrowserControllerTest : public InProcessBrowserTest {
  public:
   // InProcessBrowserTest:
   void SetUp() override {
     scoped_feature_list_.InitWithFeatures(GetEnabledFeatures(), {});
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
     InProcessBrowserTest::SetUp();
   }
+
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
-    ASSERT_TRUE(embedded_test_server()->Start());
+    embedded_test_server()->RegisterDefaultHandler(
+        base::BindRepeating(&SideSearchBrowserControllerTest::HandleRequest,
+                            base::Unretained(this)));
+    embedded_test_server()->StartAcceptingConnections();
+
     InProcessBrowserTest::SetUpOnMainThread();
+    auto* config = SideSearchConfig::Get(browser()->profile());
+    config->SetShouldNavigateInSidePanelCalback(
+        base::BindRepeating(ShouldNavigateInSidePanel));
+    config->SetCanShowSidePanelForURLCallback(
+        base::BindRepeating(CanShowSidePanelForURL));
     SetIsSidePanelSRPAvailableAt(browser(), 0, true);
   }
 
+  void TearDownOnMainThread() override {
+    EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
+    InProcessBrowserTest::TearDownOnMainThread();
+  }
+
   virtual std::vector<base::Feature> GetEnabledFeatures() {
     return {features::kSideSearch};
   }
@@ -63,14 +110,14 @@
     browser->tab_strip_model()->ActivateTabAt(index);
   }
 
-  void AppendTab(Browser* browser, const std::string& url) {
-    chrome::AddTabAt(browser, GURL(url), -1, true);
+  void AppendTab(Browser* browser, const GURL& url) {
+    chrome::AddTabAt(browser, url, -1, true);
     SetIsSidePanelSRPAvailableAt(
         browser, browser->tab_strip_model()->GetTabCount() - 1, true);
   }
 
-  void NavigateActiveTab(Browser* browser, const std::string& url) {
-    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, GURL(url)));
+  void NavigateActiveTab(Browser* browser, const GURL& url) {
+    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, url));
   }
 
   void NotifyButtonClick(Browser* browser) {
@@ -87,9 +134,8 @@
   void SetIsSidePanelSRPAvailableAt(Browser* browser,
                                     int index,
                                     bool is_available) {
-    auto* tab_contents_helper = SideSearchTabContentsHelper::FromWebContents(
-        browser->tab_strip_model()->GetWebContentsAt(index));
-    tab_contents_helper->SetIsSidePanelSRPAvailableForTesting(is_available);
+    SideSearchConfig::Get(browser->profile())
+        ->set_is_side_panel_srp_available(is_available);
   }
 
   BrowserView* BrowserViewFor(Browser* browser) {
@@ -116,21 +162,26 @@
     return tab_contents_helper->side_panel_contents_for_testing();
   }
 
-  void NavigateToSRPAndNonGoogleUrl(Browser* browser) {
+  void NavigateToSRPAndNonGoogleUrl(
+      Browser* browser,
+      absl::optional<GURL> google_url = absl::nullopt) {
     // The side panel button should never be visible on the Google search page.
-    NavigateActiveTab(browser, kGoogleSearchURL);
+    NavigateActiveTab(browser, google_url.value_or(google_search_url()));
     EXPECT_FALSE(GetSidePanelButtonFor(browser)->GetVisible());
     EXPECT_FALSE(GetSidePanelFor(browser)->GetVisible());
 
     // The side panel button should be visible if on a non-Google page and the
     // current tab has previously encountered a Google search page.
-    NavigateActiveTab(browser, kNonGoogleURL);
+    NavigateActiveTab(browser, non_google_url());
     EXPECT_TRUE(GetSidePanelButtonFor(browser)->GetVisible());
     EXPECT_FALSE(GetSidePanelFor(browser)->GetVisible());
   }
 
-  void NavigateToSRPAndOpenSidePanel(Browser* browser) {
-    NavigateToSRPAndNonGoogleUrl(browser);
+  void NavigateToSRPAndOpenSidePanel(
+      Browser* browser,
+      absl::optional<GURL> google_url = absl::nullopt) {
+    NavigateToSRPAndNonGoogleUrl(browser,
+                                 google_url.value_or(google_search_url()));
 
     NotifyButtonClick(browser);
     EXPECT_TRUE(GetSidePanelButtonFor(browser)->GetVisible());
@@ -138,6 +189,26 @@
   }
 
  protected:
+  GURL google_search_url() {
+    return embedded_test_server()->GetURL("www.google.com", "/search?q=test");
+  }
+
+  GURL google_homepage_url() {
+    return embedded_test_server()->GetURL("www.google.com", "/");
+  }
+
+  GURL non_google_url() {
+    return embedded_test_server()->GetURL("www.test.com", "/");
+  }
+
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) {
+    auto http_response =
+        std::make_unique<net::test_server::BasicHttpResponse>();
+    http_response->set_code(net::HTTP_OK);
+    return std::move(http_response);
+  }
+
   base::HistogramTester histogram_tester_;
 
  private:
@@ -147,21 +218,21 @@
 IN_PROC_BROWSER_TEST_F(SideSearchBrowserControllerTest,
                        SidePanelButtonShowsCorrectlySingleTab) {
   // The side panel button should never be visible on the Google home page.
-  NavigateActiveTab(browser(), kGoogleSearchHomePageURL);
+  NavigateActiveTab(browser(), google_homepage_url());
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
 
   // If no previous Google search page has been navigated to the button should
   // not be visible.
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
 
   // The side panel button should never be visible on the Google search page.
-  NavigateActiveTab(browser(), kGoogleSearchURL);
+  NavigateActiveTab(browser(), google_search_url());
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
 
   // The side panel button should be visible if on a non-Google page and the
   // current tab has previously encountered a Google search page.
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_TRUE(GetSidePanelButtonFor(browser())->GetVisible());
   histogram_tester_.ExpectBucketCount(
       "SideSearch.AvailabilityChanged",
@@ -169,7 +240,7 @@
 
   // The side panel button should never be visible on the Google home page even
   // if it has already been navigated to a Google search page.
-  NavigateActiveTab(browser(), kGoogleSearchHomePageURL);
+  NavigateActiveTab(browser(), google_homepage_url());
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
   histogram_tester_.ExpectBucketCount(
       "SideSearch.AvailabilityChanged",
@@ -177,7 +248,7 @@
 
   // The side panel button should be visible if on a non-Google page and the
   // current tab has previously encountered a Google search page.
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_TRUE(GetSidePanelButtonFor(browser())->GetVisible());
   histogram_tester_.ExpectBucketCount(
       "SideSearch.AvailabilityChanged",
@@ -187,16 +258,16 @@
 IN_PROC_BROWSER_TEST_F(SideSearchBrowserControllerTest,
                        SidePanelButtonShowsCorrectlyMultipleTabs) {
   // The side panel button should never be visible on the Google home page.
-  AppendTab(browser(), kGoogleSearchHomePageURL);
+  AppendTab(browser(), google_homepage_url());
   ActivateTabAt(browser(), 1);
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
 
   // Navigate to a Google search page and then to a non-Google search page. This
   // should show the side panel button in the toolbar.
-  AppendTab(browser(), kGoogleSearchURL);
+  AppendTab(browser(), google_search_url());
   ActivateTabAt(browser(), 2);
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_TRUE(GetSidePanelButtonFor(browser())->GetVisible());
 
   // Switch back to the Google search page, the side panel button should no
@@ -212,13 +283,13 @@
 
 IN_PROC_BROWSER_TEST_F(SideSearchBrowserControllerTest,
                        SidePanelTogglesCorrectlySingleTab) {
-  NavigateActiveTab(browser(), kGoogleSearchURL);
+  NavigateActiveTab(browser(), google_search_url());
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
 
   // The side panel button should be visible if on a non-Google page and the
   // current tab has previously encountered a Google search page.
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_TRUE(GetSidePanelButtonFor(browser())->GetVisible());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
 
@@ -245,19 +316,19 @@
   // independent browser tabs such that both have the side panel ready.
 
   // Tab 1.
-  NavigateActiveTab(browser(), kGoogleSearchURL);
+  NavigateActiveTab(browser(), google_search_url());
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_TRUE(GetSidePanelButtonFor(browser())->GetVisible());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
 
   // Tab 2.
-  AppendTab(browser(), kGoogleSearchURL);
+  AppendTab(browser(), google_search_url());
   ActivateTabAt(browser(), 1);
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_TRUE(GetSidePanelButtonFor(browser())->GetVisible());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
 
@@ -315,8 +386,8 @@
                        SideSearchNotAvailableInOTR) {
   Browser* browser2 = CreateIncognitoBrowser();
   EXPECT_TRUE(browser2->profile()->IsOffTheRecord());
-  NavigateActiveTab(browser2, kGoogleSearchURL);
-  NavigateActiveTab(browser2, kNonGoogleURL);
+  NavigateActiveTab(browser2, google_search_url());
+  NavigateActiveTab(browser2, non_google_url());
 
   EXPECT_EQ(nullptr, GetSidePanelButtonFor(browser2));
   EXPECT_EQ(nullptr, GetSidePanelFor(browser2));
@@ -328,21 +399,21 @@
   SetIsSidePanelSRPAvailableAt(browser(), 0, false);
 
   // The side panel button should never be visible on the Google home page.
-  NavigateActiveTab(browser(), kGoogleSearchHomePageURL);
+  NavigateActiveTab(browser(), google_homepage_url());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
 
   // If no previous Google search page has been navigated to the button should
   // not be visible.
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
 
   // The side panel button should never be visible on the Google search page.
-  NavigateActiveTab(browser(), kGoogleSearchURL);
+  NavigateActiveTab(browser(), google_search_url());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
 
   // The side panel button should not be visible if the side panel SRP is not
   // available.
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
 }
 
@@ -446,19 +517,19 @@
   // side panel should respect the state-per-tab flag.
 
   // Tab 1.
-  NavigateActiveTab(browser(), kGoogleSearchURL);
+  NavigateActiveTab(browser(), google_search_url());
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_TRUE(GetSidePanelButtonFor(browser())->GetVisible());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
 
   // Tab 2.
-  AppendTab(browser(), kGoogleSearchURL);
+  AppendTab(browser(), google_search_url());
   ActivateTabAt(browser(), 1);
   EXPECT_FALSE(GetSidePanelButtonFor(browser())->GetVisible());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
-  NavigateActiveTab(browser(), kNonGoogleURL);
+  NavigateActiveTab(browser(), non_google_url());
   EXPECT_TRUE(GetSidePanelButtonFor(browser())->GetVisible());
   EXPECT_FALSE(GetSidePanelFor(browser())->GetVisible());
 
@@ -510,7 +581,7 @@
 
   // Switch to another tab and open the side panel. The side panel should still
   // have focus as it was opened via the toolbar button.
-  AppendTab(browser(), kNonGoogleURL);
+  AppendTab(browser(), non_google_url());
   ActivateTabAt(browser(), 1);
   NavigateToSRPAndOpenSidePanel(browser());
   EXPECT_TRUE(side_panel->GetVisible());
@@ -541,12 +612,12 @@
   // Navigating to a Google SRP URL should automatically hide the side panel as
   // it should not be available.
   EXPECT_TRUE(side_panel->GetVisible());
-  NavigateActiveTab(browser(), kGoogleSearchURL);
+  NavigateActiveTab(browser(), google_search_url());
   EXPECT_FALSE(side_panel->GetVisible());
 
   // When navigating again to a non-Google / non-NTP page the side panel will
   // become available again but should not automatically reopen.
-  NavigateActiveTab(browser(), kGoogleSearchURL);
+  NavigateActiveTab(browser(), google_search_url());
   EXPECT_FALSE(side_panel->GetVisible());
 }
 
@@ -554,7 +625,7 @@
                        SidePanelCrashesCloseSidePanel) {
   // Open two tabs with the side panel open.
   NavigateToSRPAndOpenSidePanel(browser());
-  AppendTab(browser(), kNonGoogleURL);
+  AppendTab(browser(), non_google_url());
   ActivateTabAt(browser(), 1);
   NavigateToSRPAndOpenSidePanel(browser());
 
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
index 431ea80..1b07b71 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
@@ -8,6 +8,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/timer/elapsed_timer.h"
 #include "build/build_config.h"
+#include "build/buildflag.h"
 #include "chrome/browser/about_flags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/flag_descriptions.h"
@@ -51,7 +52,8 @@
   kTabScrollingSelected = 3,
   kSidePanelSelected = 4,
   kLensRegionSearchSelected = 5,
-  kMaxValue = kLensRegionSearchSelected,
+  kWebUITabStripSelected = 6,
+  kMaxValue = kWebUITabStripSelected,
 };
 
 void EmitToHistogram(const std::u16string& selected_lab_state,
@@ -81,6 +83,10 @@
     } else if (internal_name ==
                flag_descriptions::kEnableLensRegionSearchFlagId) {
       return ChromeLabsSelectedLab::kLensRegionSearchSelected;
+#if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP) && defined(OS_WIN)
+    } else if (internal_name == flag_descriptions::kWebUITabStripFlagId) {
+      return ChromeLabsSelectedLab::kWebUITabStripSelected;
+#endif
     } else {
       return ChromeLabsSelectedLab::kUnspecifiedSelected;
     }
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc
index 52b3afb..dd6d76b1 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.cc
@@ -6,6 +6,7 @@
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "build/buildflag.h"
 #include "chrome/browser/flag_descriptions.h"
 #include "chrome/grit/generated_resources.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -65,6 +66,16 @@
         "chrome-labs-tab-scrolling", version_info::Channel::BETA,
         tab_scrolling_variation_descriptions));
 
+    // Thumbnail Tab Strip for Windows
+#if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP) && defined(OS_WIN)
+    lab_info.emplace_back(LabInfo(
+        flag_descriptions::kWebUITabStripFlagId,
+        l10n_util::GetStringUTF16(IDS_THUMBNAIL_TAB_STRIP_EXPERIMENT_NAME),
+        l10n_util::GetStringUTF16(
+            IDS_THUMBNAIL_TAB_STRIP_EXPERIMENT_DESCRIPTION),
+        "chrome-labs-thumbnail-tab-strip", version_info::Channel::BETA));
+#endif
+
     return lab_info;
   }());
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.cc
index eb324811..07fb1e2 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_base.cc
@@ -42,6 +42,7 @@
 #include "chrome/browser/web_applications/manifest_update_manager.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/test/web_app_test_observers.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_id.h"
@@ -325,6 +326,8 @@
   if (!delegate_->IsSyncTest()) {
     observation_.Observe(&GetProvider()->registrar());
   }
+  web_app::test::WaitUntilReady(
+      web_app::WebAppProvider::GetForTest(browser()->profile()));
 }
 
 void WebAppIntegrationBrowserTestBase::TearDownOnMainThread() {
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.cc b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
index f88e647..f1ce366 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
@@ -36,6 +36,7 @@
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "chrome/browser/ash/apps/apk_web_app_service.h"
 
@@ -225,10 +226,16 @@
     return web_theme_color;
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  absl::optional<SkColor> dark_mode_color =
-      registrar().GetAppDarkModeThemeColor(app_id());
-  if (ash::ColorProvider::Get()->IsDarkModeEnabled() && dark_mode_color) {
-    return dark_mode_color;
+  // ash::ColorProvider::Get()->IsDarkModeEnabled() flips semantics depending on
+  // the status of ash::Features::IsDarkLightModeEnabled(), so we have to check
+  // both.
+  if (ash::features::IsDarkLightModeEnabled()) {
+    absl::optional<SkColor> dark_mode_color =
+        registrar().GetAppDarkModeThemeColor(app_id());
+
+    if (ash::ColorProvider::Get()->IsDarkModeEnabled() && dark_mode_color) {
+      return dark_mode_color;
+    }
   }
 #endif
 
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index cd680b58..37f10e8 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -227,7 +227,7 @@
       features::kDesktopPWAsTabStrip};
 };
 
-// TODO(crbug.com/1257751): Stabilize the test.
+// TODO(crbug.com/1175536): Stabilize the test.
 #if defined(OS_POSIX)
 #define DISABLE_POSIX(TEST) DISABLED_##TEST
 #else
@@ -427,7 +427,7 @@
                                    /*open_as_window=*/false));
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, DISABLE_POSIX(DisplayOverride)) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, DisplayOverride) {
   GURL test_url = https_server()->GetURL(
       "/banners/"
       "manifest_test_page.html?manifest=manifest_display_override.json");
@@ -867,8 +867,7 @@
 }
 
 // Tests that an installed PWA is not used when out of scope by one path level.
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest,
-                       DISABLE_POSIX(MenuOptionsOutsideInstalledPwaScope)) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, MenuOptionsOutsideInstalledPwaScope) {
   NavigateToURLAndWait(
       browser(),
       https_server()->GetURL("/banners/scope_is_start_url/index.html"));
@@ -884,8 +883,7 @@
             kNotPresent);
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest,
-                       DISABLE_POSIX(InstallInstallableSite)) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, InstallInstallableSite) {
   base::Time before_install_time = base::Time::Now();
   base::UserActionTester user_action_tester;
   NavigateToURLAndWait(browser(), GetInstallableAppURL());
@@ -912,7 +910,7 @@
 #endif
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, DISABLE_POSIX(CanInstallOverTabPwa)) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, CanInstallOverTabPwa) {
   NavigateToURLAndWait(browser(), GetInstallableAppURL());
   const AppId app_id = InstallPwaForCurrentUrl();
 
@@ -930,8 +928,7 @@
             kNotPresent);
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest,
-                       DISABLE_POSIX(CannotInstallOverWindowPwa)) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, CannotInstallOverWindowPwa) {
   NavigateToURLAndWait(browser(), GetInstallableAppURL());
   InstallPwaForCurrentUrl();
 
@@ -1002,8 +999,7 @@
 }
 
 #if defined(OS_MAC) || defined(OS_WIN)
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest,
-                       DISABLE_POSIX(ShortcutIconCorrectColor)) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, ShortcutIconCorrectColor) {
   os_hooks_suppress_.reset();
   base::ScopedAllowBlockingForTesting allow_blocking;
 
@@ -1606,7 +1602,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_WindowControlsOverlay,
-                       DISABLE_POSIX(WindowControlsOverlay)) {
+                       WindowControlsOverlay) {
   GURL test_url = https_server()->GetURL(
       "/banners/"
       "manifest_test_page.html?manifest=manifest_window_controls_overlay.json");
@@ -1626,8 +1622,7 @@
             app_browser->app_controller()->AppUsesWindowControlsOverlay());
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_Tabbed,
-                       DISABLE_POSIX(TabbedDisplayOverride)) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_Tabbed, TabbedDisplayOverride) {
   GURL test_url = https_server()->GetURL(
       "/banners/"
       "manifest_test_page.html?manifest=manifest_tabbed_display_override.json");
@@ -1653,8 +1648,7 @@
       features::kRemoveStatusBarInWebApps};
 };
 
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_RemoveStatusBar,
-                       DISABLE_POSIX(RemoveStatusBar)) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_RemoveStatusBar, RemoveStatusBar) {
   NavigateToURLAndWait(browser(), GetInstallableAppURL());
   const AppId app_id = InstallPwaForCurrentUrl();
   Browser* const app_browser = LaunchWebAppBrowser(app_id);
@@ -1704,8 +1698,7 @@
       blink::features::kWebAppEnableManifestId};
 };
 
-IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_ManifestId,
-                       DISABLE_POSIX(NoManifestId)) {
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_ManifestId, NoManifestId) {
   NavigateToURLAndWait(browser(), GetInstallableAppURL());
 
   const AppId app_id = InstallPwaForCurrentUrl();
diff --git a/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc b/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
index 1e501ac..e29d88d7 100644
--- a/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
@@ -201,6 +201,8 @@
   cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
 
   os_hooks_suppress_ = OsIntegrationManager::ScopedSuppressOsHooksForTesting();
+  web_app::test::WaitUntilReady(
+      web_app::WebAppProvider::GetForTest(browser()->profile()));
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_launch_manager.cc b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
index 59497f1..7347edb 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_manager.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
@@ -177,16 +177,15 @@
   const apps::ShareTarget* MaybeGetShareTarget() const;
   std::tuple<GURL, bool /*is_file_handling*/> GetLaunchUrl(
       const apps::ShareTarget* share_target) const;
-  WindowOpenDisposition GetNavigationDisposition() const;
+  WindowOpenDisposition GetNavigationDisposition(bool is_new_browser) const;
   content::WebContents* MaybeLaunchSystemWebApp(const GURL& launch_url);
-  std::tuple<Browser*, WindowOpenDisposition> EnsureBrowser();
+  std::tuple<Browser*, bool /*is_new_browser*/> EnsureBrowser();
   Browser* MaybeFindBrowserForLaunch() const;
   Browser* CreateBrowserForLaunch();
-  content::WebContents* NavigateBrowser(
-      Browser* browser,
-      const GURL& launch_url,
-      WindowOpenDisposition navigation_disposition,
-      const apps::ShareTarget* share_target);
+  content::WebContents* NavigateBrowser(Browser* browser,
+                                        bool is_new_browser,
+                                        const GURL& launch_url,
+                                        const apps::ShareTarget* share_target);
   void MaybeEnqueueWebLaunchParams(const GURL& launch_url,
                                    bool is_file_handling,
                                    content::WebContents* web_contents);
@@ -228,11 +227,11 @@
     return web_contents;
 
   Browser* browser = nullptr;
-  WindowOpenDisposition navigation_disposition;
-  std::tie(browser, navigation_disposition) = EnsureBrowser();
+  bool is_new_browser;
+  std::tie(browser, is_new_browser) = EnsureBrowser();
 
-  web_contents = NavigateBrowser(browser, launch_url, navigation_disposition,
-                                 share_target);
+  web_contents =
+      NavigateBrowser(browser, is_new_browser, launch_url, share_target);
   if (!web_contents)
     return nullptr;
 
@@ -295,7 +294,21 @@
   return {launch_url, is_file_handling};
 }
 
-WindowOpenDisposition LaunchProcess::GetNavigationDisposition() const {
+WindowOpenDisposition LaunchProcess::GetNavigationDisposition(
+    bool is_new_browser) const {
+  if (is_new_browser) {
+    // By opening a new window we've already performed part of a "disposition",
+    // the only remaining thing for Navigate() to do is navigate the new window.
+    return WindowOpenDisposition::CURRENT_TAB;
+    // TODO(crbug.com/1200944): Use NEW_FOREGROUND_TAB instead of CURRENT_TAB.
+    // The window has no tabs so it doesn't make sense to open the "current"
+    // tab. We use it anyway because it happens to work.
+    // If NEW_FOREGROUND_TAB is used the the WindowCanOpenTabs() check fails
+    // when `launch_url` is out of scope for web app windows causing it to
+    // open another separate browser window. It should be updated to check the
+    // extended scope.
+  }
+
   // Only CURRENT_TAB and NEW_FOREGROUND_TAB dispositions are supported for web
   // app launches.
   return params_.disposition == WindowOpenDisposition::CURRENT_TAB
@@ -315,26 +328,17 @@
   return browser->tab_strip_model()->GetActiveWebContents();
 }
 
-std::tuple<Browser*, WindowOpenDisposition> LaunchProcess::EnsureBrowser() {
+std::tuple<Browser*, bool /*is_new_browser*/> LaunchProcess::EnsureBrowser() {
   Browser* browser = MaybeFindBrowserForLaunch();
-  WindowOpenDisposition navigation_disposition = GetNavigationDisposition();
+  bool is_new_browser = false;
   if (browser) {
     browser->window()->Activate();
   } else {
     browser = CreateBrowserForLaunch();
-    // By opening a new window we've already performed part of a "disposition",
-    // the only remaining thing for Navigate() to do is navigate the new window.
-    navigation_disposition = WindowOpenDisposition::CURRENT_TAB;
-    // TODO(crbug.com/1200944): Use NEW_FOREGROUND_TAB instead of CURRENT_TAB.
-    // The window has no tabs so it doesn't make sense to open the "current"
-    // tab. We use it anyway because it happens to work.
-    // If NEW_FOREGROUND_TAB is used the the WindowCanOpenTabs() check fails
-    // when `launch_url` is out of scope for web app windows causing it to
-    // open another separate browser window. It should be updated to check the
-    // extended scope.
+    is_new_browser = true;
   }
   browser->window()->Show();
-  return {browser, navigation_disposition};
+  return {browser, is_new_browser};
 }
 
 Browser* LaunchProcess::MaybeFindBrowserForLaunch() const {
@@ -373,9 +377,12 @@
 
 content::WebContents* LaunchProcess::NavigateBrowser(
     Browser* browser,
+    bool is_new_browser,
     const GURL& launch_url,
-    WindowOpenDisposition navigation_disposition,
     const apps::ShareTarget* share_target) {
+  WindowOpenDisposition navigation_disposition =
+      GetNavigationDisposition(is_new_browser);
+
   if (share_target) {
     NavigateParams nav_params =
         NavigateParamsForShareTarget(browser, *share_target, *params_.intent);
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc b/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc
index 6e3d073..c0e48fe 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc
@@ -45,6 +45,13 @@
             base::Unretained(this))) {}
 
  protected:
+  // InProcessBrowserTest:
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    web_app::test::WaitUntilReady(
+        web_app::WebAppProvider::GetForTest(browser()->profile()));
+  }
+
   Profile* profile() { return browser()->profile(); }
 
   const AppId InstallWebApp(const GURL& start_url) {
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_apps_page/app_notification_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/os_apps_page/app_notification_handler_unittest.cc
index b54a2f6..28d0f32 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_apps_page/app_notification_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_apps_page/app_notification_handler_unittest.cc
@@ -125,13 +125,12 @@
       std::string fake_id,
       apps::mojom::AppType app_type,
       apps::mojom::PermissionType permission_type,
-      apps::mojom::PermissionValueType permission_value_type,
-      uint32_t permission_value = 1) {
+      bool permission_value = true) {
     std::vector<apps::mojom::PermissionPtr> fake_permissions;
     apps::mojom::PermissionPtr fake_permission = apps::mojom::Permission::New();
     fake_permission->permission_type = permission_type;
-    fake_permission->value_type = permission_value_type;
-    fake_permission->value = /*True=*/permission_value;
+    fake_permission->value = apps::mojom::PermissionValue::New();
+    fake_permission->value->set_bool_value(permission_value);
     fake_permission->is_managed = false;
 
     fake_permissions.push_back(fake_permission.Clone());
@@ -205,69 +204,66 @@
 TEST_F(AppNotificationHandlerTest, TestAppListUpdated) {
   CreateAndStoreFakeApp("arcAppWithNotifications", apps::mojom::AppType::kArc,
                         apps::mojom::PermissionType::kNotifications,
-                        apps::mojom::PermissionValueType::kBool,
-                        /*permission_value=*/1);
+                        /*permission_value=*/true);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(observer()->app_list_changed(), 1);
   EXPECT_EQ("arcAppWithNotifications", observer()->recently_updated_app()->id);
-  EXPECT_EQ(1,
-            observer()->recently_updated_app()->notification_permission->value);
+  EXPECT_TRUE(observer()
+                  ->recently_updated_app()
+                  ->notification_permission->value->get_bool_value());
 
   CreateAndStoreFakeApp("webAppWithNotifications", apps::mojom::AppType::kWeb,
                         apps::mojom::PermissionType::kNotifications,
-                        apps::mojom::PermissionValueType::kBool,
-                        /*permission_value=*/1);
+                        /*permission_value=*/true);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(observer()->app_list_changed(), 2);
   EXPECT_EQ("webAppWithNotifications", observer()->recently_updated_app()->id);
-  EXPECT_EQ(1,
-            observer()->recently_updated_app()->notification_permission->value);
+  EXPECT_TRUE(observer()
+                  ->recently_updated_app()
+                  ->notification_permission->value->get_bool_value());
 
   CreateAndStoreFakeApp("arcAppWithCamera", apps::mojom::AppType::kArc,
-                        apps::mojom::PermissionType::kCamera,
-                        apps::mojom::PermissionValueType::kBool);
+                        apps::mojom::PermissionType::kCamera);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(observer()->app_list_changed(), 2);
 
   CreateAndStoreFakeApp("webAppWithGeolocation", apps::mojom::AppType::kWeb,
-                        apps::mojom::PermissionType::kLocation,
-                        apps::mojom::PermissionValueType::kBool);
+                        apps::mojom::PermissionType::kLocation);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(observer()->app_list_changed(), 2);
 
   CreateAndStoreFakeApp("pluginVmAppWithPrinting",
                         apps::mojom::AppType::kPluginVm,
-                        apps::mojom::PermissionType::kPrinting,
-                        apps::mojom::PermissionValueType::kBool);
+                        apps::mojom::PermissionType::kPrinting);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(observer()->app_list_changed(), 2);
 
   CreateAndStoreFakeApp("arcAppWithNotifications", apps::mojom::AppType::kArc,
                         apps::mojom::PermissionType::kNotifications,
-                        apps::mojom::PermissionValueType::kBool,
-                        /*permission_value=*/0);
+                        /*permission_value=*/false);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(observer()->app_list_changed(), 3);
   EXPECT_EQ("arcAppWithNotifications", observer()->recently_updated_app()->id);
-  EXPECT_EQ(0,
-            observer()->recently_updated_app()->notification_permission->value);
+  EXPECT_FALSE(observer()
+                   ->recently_updated_app()
+                   ->notification_permission->value->get_bool_value());
 
   CreateAndStoreFakeApp("webAppWithNotifications", apps::mojom::AppType::kWeb,
                         apps::mojom::PermissionType::kNotifications,
-                        apps::mojom::PermissionValueType::kBool,
-                        /*permission_value=*/0);
+                        /*permission_value=*/false);
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(observer()->app_list_changed(), 4);
   EXPECT_EQ("webAppWithNotifications", observer()->recently_updated_app()->id);
-  EXPECT_EQ(0,
-            observer()->recently_updated_app()->notification_permission->value);
+  EXPECT_FALSE(observer()
+                   ->recently_updated_app()
+                   ->notification_permission->value->get_bool_value());
 }
 
 TEST_F(AppNotificationHandlerTest, TestNotifyPageReady) {
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
index 6ec6ef29..ffc2340 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
@@ -320,8 +320,8 @@
 
     auto permission = apps::mojom::Permission::New();
     permission->permission_type = GetPermissionType(type);
-    permission->value_type = apps::mojom::PermissionValueType::kTriState;
-    permission->value = static_cast<uint32_t>(setting_val);
+    permission->value = apps::mojom::PermissionValue::New();
+    permission->value->set_tristate_value(setting_val);
     permission->is_managed =
         setting_info.source == content_settings::SETTING_SOURCE_POLICY;
 
@@ -704,10 +704,9 @@
     return;
   }
 
-  DCHECK_EQ(permission->value_type,
-            apps::mojom::PermissionValueType::kTriState);
+  DCHECK(permission->value->is_tristate_value());
   ContentSetting permission_value = CONTENT_SETTING_DEFAULT;
-  switch (static_cast<apps::mojom::TriState>(permission->value)) {
+  switch (permission->value->get_tristate_value()) {
     case apps::mojom::TriState::kAllow:
       permission_value = CONTENT_SETTING_ALLOW;
       break;
diff --git a/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc b/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
index f0268872..69da02b 100644
--- a/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
+++ b/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
@@ -339,10 +339,9 @@
                             apps::mojom::PermissionType::kCamera;
                    });
   ASSERT_TRUE(camera_permission != permissions.end());
-  EXPECT_EQ((*camera_permission)->value_type,
-            apps::mojom::PermissionValueType::kTriState);
-  EXPECT_EQ((*camera_permission)->value,
-            static_cast<uint32_t>(apps::mojom::TriState::kAllow));
+  EXPECT_TRUE((*camera_permission)->value->is_tristate_value());
+  EXPECT_EQ((*camera_permission)->value->get_tristate_value(),
+            apps::mojom::TriState::kAllow);
 }
 
 IN_PROC_BROWSER_TEST_F(WebAppsPublisherHostBrowserTest, MediaRequest) {
diff --git a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
index 4ef235a5..cceed3a 100644
--- a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/web_applications/os_integration_manager.h"
 #include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_installation.h"
 #include "chrome/browser/web_applications/test/web_app_icon_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/test/web_app_sync_test_utils.h"
 #include "chrome/browser/web_applications/test/web_app_test.h"
 #include "chrome/browser/web_applications/test/web_app_test_observers.h"
@@ -228,6 +229,8 @@
   void SetUpOnMainThread() override {
     // Cannot construct RunLoop in constructor due to threading restrictions.
     shortcut_run_loop_.emplace();
+    web_app::test::WaitUntilReady(
+        web_app::WebAppProvider::GetForTest(browser()->profile()));
   }
 
   void OnShortcutInfoRetrieved(std::unique_ptr<ShortcutInfo> shortcut_info) {
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc b/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc
index 6a80099..bff4b20 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/web_applications/test/fake_os_integration_manager.h"
 #include "chrome/browser/web_applications/test/test_file_utils.h"
 #include "chrome/browser/web_applications/test/web_app_icon_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -136,6 +137,12 @@
     PreinstalledWebAppManager::SkipStartupForTesting();
   }
 
+  // InProcessBrowserTest:
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    web_app::test::WaitUntilReady(
+        web_app::WebAppProvider::GetForTest(browser()->profile()));
+  }
   void TearDownOnMainThread() override {
     ResetInterceptor();
     InProcessBrowserTest::TearDownOnMainThread();
@@ -418,6 +425,11 @@
   PreinstalledWebAppManagerExtensionBrowserTest() = default;
   ~PreinstalledWebAppManagerExtensionBrowserTest() override = default;
 
+  void SetUpOnMainThread() override {
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
+    web_app::test::WaitUntilReady(
+        web_app::WebAppProvider::GetForTest(browser()->profile()));
+  }
   void TearDownOnMainThread() override {
     ResetInterceptor();
     extensions::ExtensionBrowserTest::TearDownOnMainThread();
diff --git a/chrome/browser/web_applications/web_app_icon_downloader.cc b/chrome/browser/web_applications/web_app_icon_downloader.cc
index 348f905..5c9a3eb 100644
--- a/chrome/browser/web_applications/web_app_icon_downloader.cc
+++ b/chrome/browser/web_applications/web_app_icon_downloader.cc
@@ -41,7 +41,8 @@
 
 void WebAppIconDownloader::Start() {
   // Favicons are supported only in HTTP or HTTPS WebContents.
-  if (!web_contents()->GetLastCommittedURL().SchemeIsHTTPOrHTTPS())
+  const GURL& url = web_contents()->GetLastCommittedURL();
+  if (!url.is_empty() && !url.inner_url() && !url.SchemeIsHTTPOrHTTPS())
     SkipPageFavicons();
 
   // If the candidates aren't loaded, icons will be fetched when
diff --git a/chrome/browser/web_applications/web_app_mover_browsertest.cc b/chrome/browser/web_applications/web_app_mover_browsertest.cc
index 801412b..08c6928 100644
--- a/chrome/browser/web_applications/web_app_mover_browsertest.cc
+++ b/chrome/browser/web_applications/web_app_mover_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/web_applications/os_integration_manager.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/test/web_app_test.h"
 #include "chrome/browser/web_applications/test/web_app_test_utils.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
@@ -51,6 +52,13 @@
   ~WebAppMoverBrowsertestBase() override = default;
 
  protected:
+  // InProcessBrowserTest:
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    web_app::test::WaitUntilReady(
+        web_app::WebAppProvider::GetForTest(browser()->profile()));
+  }
+
   GURL GetMigratingFromAppA() {
     return embedded_test_server()->GetURL(
         "/web_apps/mover/migrate_from/a/index.html");
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java
index 0e28809..1aa05e9 100644
--- a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java
@@ -117,4 +117,11 @@
      * Opens the settings to manager autoplay.
      */
     default void openAutoplaySettings() {}
+
+    /**
+     * Tracks the interaction, i.e. viewed/clicked, state of a card specific notice.
+     * @param key Key to identify the type of the notice.
+     * @param data A serialized ContentId protobuf message.
+     */
+    default void trackInteractionForNotice(String key, byte[] data) {}
 }
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 4b9111a..8d3c936 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1633975157-70b540de0293ec8e4308ac55c767c2b80cb02bc7.profdata
+chrome-linux-main-1633996760-4c081f0e822b7bfe51ac50ea0226a61779e53fea.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 64f4c018..b145b4f 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1633975157-aa36f13d6fbb11046f40b07a1281b0acd069c0df.profdata
+chrome-mac-main-1634018394-7d049de9168eebccc73c1f52f17c3554f041aee4.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index a91da5c0..1215d18 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1633985928-8976cc68b02f6ef4f34ada6121a0c67c1f80db29.profdata
+chrome-win32-main-1634007276-255923810f65f018d6e74d98f2eec5dd8022a181.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index f8ea82eb..e14929d 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1633975157-6e01b9278dd15cb5b5d387ee549aea30551eac75.profdata
+chrome-win64-main-1634007276-7a6d9187b564ade39002b683418de3fe62e4980e.profdata
diff --git a/chrome/common/extensions/api/terminal_private.json b/chrome/common/extensions/api/terminal_private.json
index ce805c6..ee39739 100644
--- a/chrome/common/extensions/api/terminal_private.json
+++ b/chrome/common/extensions/api/terminal_private.json
@@ -177,12 +177,20 @@
         "name": "openWindow",
         "type": "function",
         "description": "Open the Terminal tabbed window.",
-        "parameters": [],
-        "returns_async": {
-          "name": "callback",
-          "description": "Callback that will be called when complete.",
-          "parameters": []
-        }
+        "parameters": [
+          {
+            "name": "data",
+            "type": "object",
+	    "optional": true,
+            "properties": {
+              "url": {
+                "description": "The url for the new Terminal window.",
+                "optional": true,
+		"type": "string"
+              }
+	    }
+          }
+        ]
       },
       {
         "name": "openOptionsPage",
diff --git a/chrome/renderer/cart/commerce_hint_agent.cc b/chrome/renderer/cart/commerce_hint_agent.cc
index 5f52a3d..272693f0 100644
--- a/chrome/renderer/cart/commerce_hint_agent.cc
+++ b/chrome/renderer/cart/commerce_hint_agent.cc
@@ -730,6 +730,7 @@
     return;
   }
   is_extraction_pending_ = true;
+  extraction_count_++;
   DVLOG(1) << "Scheduled extraction";
   base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
@@ -739,16 +740,16 @@
 }
 
 void CommerceHintAgent::ExtractProducts() {
-  is_extraction_pending_ = false;
   if (is_extraction_running_) {
     DVLOG(1) << "Extraction is running. Try again later.";
     base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE,
-        base::BindOnce(&CommerceHintAgent::MaybeExtractProducts,
+        base::BindOnce(&CommerceHintAgent::ExtractProducts,
                        weak_factory_.GetWeakPtr()),
         kCartExtractionGapTime.Get());
     return;
   }
+  is_extraction_pending_ = false;
   is_extraction_running_ = true;
   DVLOG(2) << "is_extraction_running_ = " << is_extraction_running_;
 
@@ -898,7 +899,6 @@
   OnCartProductUpdated(render_frame(), std::move(products));
 
   is_extraction_running_ = false;
-  extraction_count_++;
   DVLOG(2) << "is_extraction_running_ = " << is_extraction_running_;
 }
 
diff --git a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
index 9d8ce3d..eb7fdf8a 100644
--- a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
+++ b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
@@ -242,7 +242,7 @@
       run_loop.Run();
       if (satisfied_)
         break;
-      base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+      base::PlatformThread::Sleep(TestTimeouts::tiny_timeout() * 10);
     }
   }
 
@@ -263,8 +263,8 @@
                   expected[i].second.merchant_cart_url());
       }
     } else {
-      VLOG(3) << "Found " << found.size() << " but expecting "
-              << expected.size();
+      LOG(INFO) << "WaitForCartCount() is expecting " << expected.size()
+                << " but found " << found.size();
     }
     std::move(closure).Run();
   }
@@ -280,7 +280,7 @@
       run_loop.Run();
       if (satisfied_)
         break;
-      base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+      base::PlatformThread::Sleep(TestTimeouts::tiny_timeout() * 10);
     }
   }
 
@@ -299,6 +299,9 @@
                           .ReplaceComponents(remove_port)
                           .spec() == expected[i].second.merchant_cart_url();
       }
+    } else {
+      LOG(INFO) << "WaitForCarts() is expecting " << expected.size()
+                << " but found " << found.size();
     }
     std::move(closure).Run();
   }
@@ -314,7 +317,7 @@
       run_loop.Run();
       if (satisfied_)
         break;
-      base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+      base::PlatformThread::Sleep(TestTimeouts::tiny_timeout() * 10);
     }
   }
 
@@ -324,8 +327,11 @@
                              ShoppingCarts found) {
     bool fail = false;
     bool same_size = found.size() == expected.size();
-    if (!same_size)
+    if (!same_size) {
       fail = true;
+      LOG(INFO) << "WaitForProductCount() is expecting " << expected.size()
+                << " but found " << found.size();
+    }
     for (size_t i = 0; i < std::min(found.size(), expected.size()); i++) {
       EXPECT_EQ(found[i].first, expected[i].first);
       GURL::Replacements remove_port;
@@ -846,7 +852,8 @@
   void SetUpInProcessBrowserTestFixture() override {
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {{ntp_features::kNtpChromeCartModule,
-          {{"cart-extraction-timeout", "0"}}}},
+          {{"cart-extraction-max-count", "1"},
+           {"cart-extraction-timeout", "0"}}}},
         {optimization_guide::features::kOptimizationHints});
   }
 
@@ -882,9 +889,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-// Flaky on Linux: https://crbug.com/1257964.
-// See definition of MAYBE_ExtractCart above.
-IN_PROC_BROWSER_TEST_F(CommerceHintMaxCountTest, MAYBE_ExtractCart) {
+IN_PROC_BROWSER_TEST_F(CommerceHintMaxCountTest, ExtractCart) {
   NavigateToURL("https://www.guitarcenter.com/cart.html");
 
   WaitForUmaBucketCount("Commerce.Carts.ExtractionTimedOut", 0, 1);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index da621592..92978c0 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1053,6 +1053,7 @@
       "//chrome/browser:theme_properties",
       "//chrome/browser/apps/app_service:test_support",
       "//chrome/browser/browsing_data:constants",
+      "//chrome/browser/commerce:feature_list",
       "//chrome/browser/continuous_search:browser_tests",
       "//chrome/browser/devtools",
       "//chrome/browser/devtools:test_support",
@@ -1467,6 +1468,7 @@
       "../browser/browsing_data/third_party_data_remover_browsertest.cc",
       "../browser/capability_delegation_browsertest.cc",
       "../browser/cart/cart_service_browsertest.cc",
+      "../browser/cart/fetch_discount_worker_browsertest.cc",
       "../browser/chrome_back_forward_cache_browsertest.cc",
       "../browser/chrome_content_browser_client_browsertest.cc",
       "../browser/chrome_do_not_track_browsertest.cc",
@@ -2734,7 +2736,7 @@
         "//components/keep_alive_registry",
         "//components/media_router/browser:test_support",
         "//components/media_router/common:test_support",
-        "//components/web_package:test_support",
+        "//components/web_package",
         "//google_apis/common:test_support",
         "//google_apis/drive",
 
diff --git a/chrome/test/data/cart/coupons/fl_codeless_discounts.json b/chrome/test/data/cart/coupons/fl_codeless_discounts.json
new file mode 100644
index 0000000..af626c2d
--- /dev/null
+++ b/chrome/test/data/cart/coupons/fl_codeless_discounts.json
@@ -0,0 +1,35 @@
+{
+  "discounts": [
+    // Use case for:
+    //  * FetchFLCodelessDiscountWorkerBrowserTest.SimplePercentOffTest
+    //  * FetchFLCodelessDiscountWorkerBrowserTest.TwoCartsOneWithDiscountOneWithoutDiscount
+    {
+      "merchantIdentifier": {
+        "cartUrl": "https://www.merchant1.com/cart",
+        "merchantId": "10046"
+      },
+      "couponDiscounts": [{
+        "type": "FREE_LISTING_WITHOUT_CODE"
+      }],
+      "overallDiscountInfo": {
+        "text": "10% off",
+        "languageCode": "en-US"
+      }
+    },
+    // Use case for FetchFLCodelessDiscountWorkerBrowserTest.SimpleDollarOffTest
+    {
+      "merchantIdentifier": {
+        "cartUrl": "https://www.merchant2.com/cart",
+        "merchantId": "10046"
+      },
+      "couponDiscounts": [{
+        "type": "FREE_LISTING_WITHOUT_CODE"
+      }],
+      "overallDiscountInfo": {
+        "text": "$2 off",
+        "languageCode": "en-US"
+      }
+    }
+    // cartUrl needs to be unique. Next cartUrl is https://www.merchant3.com/cart.
+  ]
+}
\ No newline at end of file
diff --git a/chrome/test/data/cart/coupons/fl_codeless_discounts.json.mock-http-headers b/chrome/test/data/cart/coupons/fl_codeless_discounts.json.mock-http-headers
new file mode 100644
index 0000000..fb7791b
--- /dev/null
+++ b/chrome/test/data/cart/coupons/fl_codeless_discounts.json.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Content-Type: application/json; charset=utf-8
\ No newline at end of file
diff --git a/chrome/test/data/webui/settings/chromeos/app_management/borealis_detail_view_test.js b/chrome/test/data/webui/settings/chromeos/app_management/borealis_detail_view_test.js
index f5cef8bc..10b4467a 100644
--- a/chrome/test/data/webui/settings/chromeos/app_management/borealis_detail_view_test.js
+++ b/chrome/test/data/webui/settings/chromeos/app_management/borealis_detail_view_test.js
@@ -5,7 +5,7 @@
 // clang-format off
 // #import 'chrome://os-settings/chromeos/os_settings.js';
 
-// #import {PermissionType, createPermission, PermissionValueType, Bool, AppManagementStore, updateSelectedAppId, getPermissionValueBool, convertOptionalBoolToBool} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {PermissionType, createBoolPermission, AppManagementStore, updateSelectedAppId, getPermissionValueBool, convertOptionalBoolToBool} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {setupFakeHandler, replaceStore, replaceBody, getPermissionCrToggleByType, getPermissionToggleByType} from './test_util.m.js';
 // #import {eventToPromise, flushTasks} from 'chrome://test/test_util.js';
 // #import {Router, routes, Route} from 'chrome://os-settings/chromeos/os_settings.js';
@@ -41,9 +41,8 @@
     const permissions = {};
     const permissionTypes = [PermissionType.kMicrophone];
     for (const permissionType of permissionTypes) {
-      permissions[permissionType] = app_management.util.createPermission(
-          permissionType, PermissionValueType.kBool, Bool.kTrue,
-          false /*is_managed*/);
+      permissions[permissionType] = createBoolPermission(
+          permissionType, true /*permission_value*/, false /*is_managed*/);
     }
 
     // Add main app, and make it the currently selected app.
diff --git a/chrome/test/data/webui/settings/chromeos/app_management/managed_apps_test.js b/chrome/test/data/webui/settings/chromeos/app_management/managed_apps_test.js
index 8525b22..be44b71 100644
--- a/chrome/test/data/webui/settings/chromeos/app_management/managed_apps_test.js
+++ b/chrome/test/data/webui/settings/chromeos/app_management/managed_apps_test.js
@@ -5,7 +5,7 @@
 // clang-format off
 // #import 'chrome://os-settings/chromeos/os_settings.js';
 
-// #import {PermissionType, TriState, FakePageHandler, AppManagementStore, updateSelectedAppId} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {PermissionType, TriState, FakePageHandler, AppManagementStore, updateSelectedAppId, createTriStatePermission} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {flushTasks} from 'chrome://test/test_util.js';
 // #import {setupFakeHandler, replaceStore, replaceBody, getPermissionToggleByType } from './test_util.m.js';
 // clang-format on
@@ -23,14 +23,10 @@
     // Create a Web app which is installed and pinned by policy
     // and has location set to on and camera set to off by policy.
     const permissionOptions = {};
-    permissionOptions[PermissionType.kLocation] = {
-      permissionValue: TriState.kAllow,
-      isManaged: true,
-    };
-    permissionOptions[PermissionType.kCamera] = {
-      permissionValue: TriState.kBlock,
-      isManaged: true
-    };
+    permissionOptions[PermissionType.kLocation] = createTriStatePermission(
+        PermissionType.kLocation, TriState.kAllow, /*isManaged*/ true);
+    permissionOptions[PermissionType.kCamera] = createTriStatePermission(
+        PermissionType.kCamera, TriState.kBlock, /*isManaged*/ true);
     const policyAppOptions = {
       type: apps.mojom.AppType.kWeb,
       isPinned: apps.mojom.OptionalBool.kTrue,
diff --git a/chrome/test/data/webui/settings/chromeos/app_management/plugin_vm_detail_view_test.js b/chrome/test/data/webui/settings/chromeos/app_management/plugin_vm_detail_view_test.js
index 6b1afc4..8023d6a 100644
--- a/chrome/test/data/webui/settings/chromeos/app_management/plugin_vm_detail_view_test.js
+++ b/chrome/test/data/webui/settings/chromeos/app_management/plugin_vm_detail_view_test.js
@@ -5,7 +5,7 @@
 // clang-format off
 // #import 'chrome://os-settings/chromeos/os_settings.js';
 
-// #import {PluginVmBrowserProxyImpl, PermissionType, createPermission, PermissionValueType, Bool, AppManagementStore, updateSelectedAppId, getPermissionValueBool, convertOptionalBoolToBool} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {PluginVmBrowserProxyImpl, PermissionType, createBoolPermission, AppManagementStore, updateSelectedAppId, getPermissionValueBool, convertOptionalBoolToBool} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {TestPluginVmBrowserProxy} from './test_plugin_vm_browser_proxy.m.js';
 // #import {setupFakeHandler, replaceStore, replaceBody, getPermissionCrToggleByType, getPermissionToggleByType} from './test_util.m.js';
 // clang-format on
@@ -123,9 +123,8 @@
       PermissionType.kMicrophone,
     ];
     for (const permissionType of permissionTypes) {
-      permissions[permissionType] = app_management.util.createPermission(
-          permissionType, PermissionValueType.kBool, Bool.kTrue,
-          false /*is_managed*/);
+      permissions[permissionType] =
+          createBoolPermission(permissionType, true, false /*is_managed*/);
     }
 
     pluginVmBrowserProxy.pluginVmRunning = false;
diff --git a/chrome/test/data/webui/settings/chromeos/app_notifications_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/app_notifications_subpage_tests.js
index bd59f6c..9e74e37 100644
--- a/chrome/test/data/webui/settings/chromeos/app_notifications_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/app_notifications_subpage_tests.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {setAppNotificationProviderForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
+import {createBoolPermission, getBoolPermissionValue, isBoolValue, setAppNotificationProviderForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -221,22 +221,6 @@
   }
 
   /**
-   * @param {!apps.mojom.PermissionType} permissionType
-   * @param {!apps.mojom.PermissionValueType} value_type
-   * @param {number} value
-   * @param {boolean} is_managed
-   * @return {!apps.mojom.Permission}
-   */
-  function createPermission(permissionType, value_type, value, is_managed) {
-    return {
-      permissionType: permissionType,
-      valueType: value_type,
-      value: value,
-      isManaged: is_managed
-    };
-  }
-
-  /**
    * @param {string} id
    * @param {string} title
    * @param {!apps.mojom.Permission} permission
@@ -254,12 +238,12 @@
   }
 
   test('loadAppListAndClickToggle', async () => {
-    const permission1 = createPermission(
-        /**permissionType=*/ 1, /**value_type=*/ 0,
-        /**value=*/ 0, /**is_managed=*/ false);
-    const permission2 = createPermission(
-        /**permissionType=*/ 2, /**value_type=*/ 0,
-        /**value=*/ 1, /**is_managed=*/ false);
+    const permission1 = createBoolPermission(
+        /**permissionType=*/ 1,
+        /**value=*/ false, /**is_managed=*/ false);
+    const permission2 = createBoolPermission(
+        /**permissionType=*/ 2,
+        /**value=*/ true, /**is_managed=*/ false);
     const app1 = createApp('1', 'App1', permission1);
     const app2 = createApp('2', 'App2', permission2);
 
@@ -292,18 +276,18 @@
     assertEquals('1', mojoApi_.getLastUpdatedAppId());
     const lastUpdatedPermission = mojoApi_.getLastUpdatedPermission();
     assertEquals(1, lastUpdatedPermission.permissionType);
-    assertEquals(0, lastUpdatedPermission.valueType);
+    assertTrue(isBoolValue(lastUpdatedPermission.value));
     assertEquals(false, lastUpdatedPermission.isManaged);
-    assertEquals(1, lastUpdatedPermission.value);
+    assertTrue(getBoolPermissionValue(lastUpdatedPermission.value));
   });
 
   test('RemovedApp', async () => {
-    const permission1 = createPermission(
-        /**permissionType=*/ 1, /**value_type=*/ 0,
-        /**value=*/ 0, /**is_managed=*/ false);
-    const permission2 = createPermission(
-        /**permissionType=*/ 2, /**value_type=*/ 0,
-        /**value=*/ 1, /**is_managed=*/ false);
+    const permission1 = createBoolPermission(
+        /**permissionType=*/ 1,
+        /**value=*/ false, /**is_managed=*/ false);
+    const permission2 = createBoolPermission(
+        /**permissionType=*/ 2,
+        /**value=*/ true, /**is_managed=*/ false);
     const app1 = createApp('1', 'App1', permission1);
     const app2 = createApp('2', 'App2', permission2);
 
@@ -334,12 +318,12 @@
   test('Each app-notification-row displays correctly', async () => {
     const appTitle1 = 'Files';
     const appTitle2 = 'Chrome';
-    const permission1 = createPermission(
-        /**permissionType=*/ 1, /**value_type=*/ 0,
-        /**value=*/ 0, /**is_managed=*/ true);
-    const permission2 = createPermission(
-        /**permissionType=*/ 2, /**value_type=*/ 0,
-        /**value=*/ 1, /**is_managed=*/ false);
+    const permission1 = createBoolPermission(
+        /**permissionType=*/ 1,
+        /**value=*/ false, /**is_managed=*/ true);
+    const permission2 = createBoolPermission(
+        /**permissionType=*/ 2,
+        /**value=*/ true, /**is_managed=*/ false);
     const app1 = createApp('file-id', appTitle1, permission1);
     const app2 = createApp('chrome-id', appTitle2, permission2);
 
diff --git a/chrome/test/data/webui/settings/chromeos/apps_page_test.js b/chrome/test/data/webui/settings/chromeos/apps_page_test.js
index ef6bd8a..ece55ab 100644
--- a/chrome/test/data/webui/settings/chromeos/apps_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/apps_page_test.js
@@ -7,7 +7,7 @@
 
 // #import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 // #import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
-// #import {AndroidAppsBrowserProxyImpl, Router, routes, setAppNotificationProviderForTesting} from 'chrome://os-settings/chromeos/os_settings.js';
+// #import {AndroidAppsBrowserProxyImpl, Router, routes, setAppNotificationProviderForTesting, createBoolPermission} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {TestAndroidAppsBrowserProxy} from './test_android_apps_browser_proxy.m.js';
 // #import {flush} from'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // #import {waitAfterNextRender, flushTasks} from 'chrome://test/test_util.js';
@@ -192,22 +192,6 @@
   let mojoApi_;
 
   /**
-   * @param {number} id
-   * @param {!apps.mojom.PermissionValueType} value_type
-   * @param {number} value
-   * @param {boolean} is_managed
-   * @return {!apps.mojom.Permission}
-   */
-  function createPermission(id, value_type, value, is_managed) {
-    return {
-      permissionId: id,
-      valueType: value_type,
-      value: value,
-      isManaged: is_managed
-    };
-  }
-
-  /**
    * @param {string} id
    * @param {string} title
    * @param {!apps.mojom.Permission} permission
@@ -307,12 +291,12 @@
       // Test default is to have 0 apps.
       assertEquals('0 apps', rowLink.subLabel);
 
-      const permission1 = createPermission(
-          /**id=*/ 1, /**value_type=*/ 0,
-          /**value=*/ 0, /**is_managed=*/ false);
-      const permission2 = createPermission(
-          /**id=*/ 2, /**value_type=*/ 0,
-          /**value=*/ 1, /**is_managed=*/ false);
+      const permission1 = createBoolPermission(
+          /**id=*/ 1,
+          /**value=*/ false, /**is_managed=*/ false);
+      const permission2 = createBoolPermission(
+          /**id=*/ 2,
+          /**value=*/ true, /**is_managed=*/ false);
       const app1 = createApp('1', 'App1', permission1);
       const app2 = createApp('2', 'App2', permission2);
 
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index c103657..92efef9 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -580,6 +580,8 @@
   kUnsupported = 2,
   kBrowserNotRunning = 3,
   kServiceDisconnected = 4,
+  [MinVersion=1] kProfileNotExist = 5,
+  [MinVersion=1] kBrowserWindowUnavailable = 6,
 };
 
 // BrowserInitParams is a set of parameters for initialization of browsers
diff --git a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
index 093ecbc5..eca29c8 100644
--- a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
+++ b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
@@ -23,24 +23,27 @@
 };
 
 // An enumeration of each category of information that cros_healthd can report.
+//
+// NextMinVersion: 1, NextIndex: 17
 [Extensible]
 enum ProbeCategoryEnum {
-  kBattery,
-  kNonRemovableBlockDevices,
-  kCpu,
-  kTimezone,
-  kMemory,
-  kBacklight,
-  kFan,
-  kStatefulPartition,
-  kBluetooth,
-  kSystem,
-  kNetwork,
-  kAudio,
-  kBootPerformance,
-  kBus,
-  kTpm,
-  kGraphics,
+  [Default] kUnknown = 16,
+  kBattery = 0,
+  kNonRemovableBlockDevices = 1,
+  kCpu = 2,
+  kTimezone = 3,
+  kMemory = 4,
+  kBacklight = 5,
+  kFan = 6,
+  kStatefulPartition = 7,
+  kBluetooth = 8,
+  kSystem = 9,
+  kNetwork = 10,
+  kAudio = 11,
+  kBootPerformance = 12,
+  kBus = 13,
+  kTpm = 14,
+  kGraphics = 15,
 
   // TODO(b/190459636): Rename it to kSystem after migration.
   kSystem2 = 0x10000,
@@ -48,16 +51,19 @@
 
 // An enumeration of the different categories of errors that can occur when
 // probing telemetry information.
+//
+// NextMinVersion: 1, NextIndex: 5
 [Extensible]
 enum ErrorType {
+  [Default] kUnknown = 4,
   // An error reading a system file.
-  kFileReadError,
+  kFileReadError = 0,
   // An error parsing data into a consumable form.
-  kParseError,
+  kParseError = 1,
   // An error using a system utility.
-  kSystemUtilityError,
+  kSystemUtilityError = 2,
   // The external service used to probe the information is not available.
-  kServiceUnavailable,
+  kServiceUnavailable = 3,
 };
 
 // Structure that contains error information for a telemetry probe.
diff --git a/components/app_restore/arc_save_handler.cc b/components/app_restore/arc_save_handler.cc
index f09c8f8..f0f1c6d 100644
--- a/components/app_restore/arc_save_handler.cc
+++ b/components/app_restore/arc_save_handler.cc
@@ -140,15 +140,11 @@
 
   auto task_it = task_id_to_app_id_.find(task_id);
   if (task_it != task_id_to_app_id_.end()) {
-    // During the system shutdown phase, the ARC instance connection is lost,
-    // and the ARC windows are destroyed, but the ARC tasks are not destroyed
-    // yet. This might cause window bounds lost, and ghost window can't be
-    // created after reboot due to no window bounds. So if the ARC instance
-    // connection is lost, don't remove the window info, but wait for the task
-    // destroyed to keep the window bounds info. Remove the window info only
-    // when the ARC instance is connected.
+    // Wait for the task to be destroyed to remove the full restore data for
+    // the task. Don't remove the window info, because it might affect the ghost
+    // window creating due to no window bounds. Send the window to background.
     if (is_connection_ready_) {
-      FullRestoreSaveHandler::GetInstance()->RemoveWindowInfo(
+      FullRestoreSaveHandler::GetInstance()->SendWindowToBackground(
           profile_path_, task_it->second, task_id);
     }
     return;
diff --git a/components/app_restore/full_restore_save_handler.cc b/components/app_restore/full_restore_save_handler.cc
index d18657c..71a6522 100644
--- a/components/app_restore/full_restore_save_handler.cc
+++ b/components/app_restore/full_restore_save_handler.cc
@@ -418,7 +418,7 @@
   MaybeStartSaveTimer(profile_path);
 }
 
-void FullRestoreSaveHandler::RemoveWindowInfo(
+void FullRestoreSaveHandler::SendWindowToBackground(
     const base::FilePath& profile_path,
     const std::string& app_id,
     int window_id) {
@@ -426,7 +426,7 @@
   if (it == profile_path_to_restore_data_.end())
     return;
 
-  it->second.RemoveWindowInfo(app_id, window_id);
+  it->second.SendWindowToBackground(app_id, window_id);
 
   pending_save_profile_paths_.insert(profile_path);
 
diff --git a/components/app_restore/full_restore_save_handler.h b/components/app_restore/full_restore_save_handler.h
index 61bd3ab..5b3e1db1 100644
--- a/components/app_restore/full_restore_save_handler.h
+++ b/components/app_restore/full_restore_save_handler.h
@@ -150,10 +150,10 @@
                             const std::string& app_id,
                             int window_id);
 
-  // Removes WindowInfo from |profile_path| for |app_id| and |window_id|.
-  void RemoveWindowInfo(const base::FilePath& profile_path,
-                        const std::string& app_id,
-                        int window_id);
+  // Sends the window for `profile_path` `app_id and `window_id` to background.
+  void SendWindowToBackground(const base::FilePath& profile_path,
+                              const std::string& app_id,
+                              int window_id);
 
   // Starts the timer, and when timeout, clears restore data for |profile_path|.
   void ClearRestoreData(const base::FilePath& profile_path);
diff --git a/components/app_restore/restore_data.cc b/components/app_restore/restore_data.cc
index 34d95709..d5477ba5 100644
--- a/components/app_restore/restore_data.cc
+++ b/components/app_restore/restore_data.cc
@@ -173,7 +173,7 @@
   // When a chrome app has multiple windows, all windows will be sent to the
   // background.
   for (auto& data_it : it->second)
-    data_it.second->activation_index = INT32_MIN;
+    data_it.second->activation_index = INT32_MAX;
 }
 
 void RestoreData::RemoveAppRestoreData(const std::string& app_id,
@@ -186,10 +186,11 @@
     app_id_to_launch_list_.erase(app_id);
 }
 
-void RestoreData::RemoveWindowInfo(const std::string& app_id, int window_id) {
+void RestoreData::SendWindowToBackground(const std::string& app_id,
+                                         int window_id) {
   auto* app_restore_data = GetAppRestoreDataMutable(app_id, window_id);
   if (app_restore_data)
-    app_restore_data->ClearWindowInfo();
+    app_restore_data->activation_index = INT32_MAX;
 }
 
 void RestoreData::RemoveApp(const std::string& app_id) {
diff --git a/components/app_restore/restore_data.h b/components/app_restore/restore_data.h
index 409c8b6..1623fd3 100644
--- a/components/app_restore/restore_data.h
+++ b/components/app_restore/restore_data.h
@@ -126,8 +126,8 @@
   // Removes a AppRestoreData with |window_id| for |app_id|.
   void RemoveAppRestoreData(const std::string& app_id, int window_id);
 
-  // Clears the window info for |app_id| and |window_id|.
-  void RemoveWindowInfo(const std::string& app_id, int window_id);
+  // Sends the window for |app_id| and |window_id| to background.
+  void SendWindowToBackground(const std::string& app_id, int window_id);
 
   // Removes the launch list for |app_id|.
   void RemoveApp(const std::string& app_id);
diff --git a/components/app_restore/restore_data_unittest.cc b/components/app_restore/restore_data_unittest.cc
index 04a4f398..f904dd43 100644
--- a/components/app_restore/restore_data_unittest.cc
+++ b/components/app_restore/restore_data_unittest.cc
@@ -481,22 +481,22 @@
   EXPECT_EQ(0u, app_id_to_launch_list().size());
 }
 
-TEST_F(RestoreDataTest, RemoveWindowInfo) {
+TEST_F(RestoreDataTest, SendWindowToBackground) {
   AddAppLaunchInfos();
   ModifyWindowInfos();
   ModifyThemeColors();
   VerifyRestoreData(restore_data());
 
-  // Remove kAppId1.
-  restore_data().RemoveWindowInfo(kAppId1, kWindowId1);
+  restore_data().SendWindowToBackground(kAppId1, kWindowId1);
 
   auto window_info = restore_data().GetWindowInfo(kAppId1, kWindowId1);
   EXPECT_TRUE(window_info);
-  EXPECT_FALSE(window_info->activation_index.has_value());
-  EXPECT_FALSE(window_info->desk_id.has_value());
-  EXPECT_FALSE(window_info->current_bounds.has_value());
-  EXPECT_FALSE(window_info->window_state_type.has_value());
-  EXPECT_FALSE(window_info->arc_extra_info.has_value());
+  EXPECT_TRUE(window_info->activation_index.has_value());
+  EXPECT_EQ(INT32_MAX, window_info->activation_index.value());
+  EXPECT_TRUE(window_info->desk_id.has_value());
+  EXPECT_TRUE(window_info->current_bounds.has_value());
+  EXPECT_TRUE(window_info->window_state_type.has_value());
+  EXPECT_TRUE(window_info->arc_extra_info.has_value());
 }
 
 TEST_F(RestoreDataTest, RemoveApp) {
@@ -674,19 +674,19 @@
 
   restore_data().SetNextRestoreWindowIdForChromeApp(kAppId1);
 
-  // Verify that the activation index is modified as INT32_MIN.
+  // Verify that the activation index is modified as INT32_MAX.
   EXPECT_EQ(kWindowId1, restore_data().FetchRestoreWindowId(kAppId1));
   window_info = restore_data().GetWindowInfo(kAppId1, kWindowId1);
   EXPECT_TRUE(window_info);
   EXPECT_TRUE(window_info->activation_index.has_value());
-  EXPECT_EQ(INT32_MIN, window_info->activation_index.value());
+  EXPECT_EQ(INT32_MAX, window_info->activation_index.value());
 
-  // Verify that the activation index is modified as INT32_MIN.
+  // Verify that the activation index is modified as INT32_MAX.
   EXPECT_EQ(kWindowId2, restore_data().FetchRestoreWindowId(kAppId1));
   window_info = restore_data().GetWindowInfo(kAppId1, kWindowId2);
   EXPECT_TRUE(window_info);
   EXPECT_TRUE(window_info->activation_index.has_value());
-  EXPECT_EQ(INT32_MIN, window_info->activation_index.value());
+  EXPECT_EQ(INT32_MAX, window_info->activation_index.value());
 
   EXPECT_EQ(0, restore_data().FetchRestoreWindowId(kAppId1));
 }
diff --git a/components/arc/arc_features.cc b/components/arc/arc_features.cc
index 894a4a1..1ea8593 100644
--- a/components/arc/arc_features.cc
+++ b/components/arc/arc_features.cc
@@ -106,7 +106,7 @@
 // Controls ARCVM real time vcpu feature on a device with 2 logical cores
 // online.
 const base::Feature kRtVcpuDualCore{"ArcRtVcpuDualCore",
-                                    base::FEATURE_ENABLED_BY_DEFAULT};
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Controls ARCVM real time vcpu feature on a device with 3+ logical cores
 // online.
diff --git a/components/arc/compat_mode/arc_resize_lock_manager.cc b/components/arc/compat_mode/arc_resize_lock_manager.cc
index 724977f..15964996 100644
--- a/components/arc/compat_mode/arc_resize_lock_manager.cc
+++ b/components/arc/compat_mode/arc_resize_lock_manager.cc
@@ -161,7 +161,8 @@
 };
 
 bool ShouldEnableResizeLock(ash::ArcResizeLockType type) {
-  return type != ash::ArcResizeLockType::RESIZABLE;
+  return type != ash::ArcResizeLockType::NONE &&
+         type != ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE;
 }
 
 }  // namespace
@@ -217,7 +218,7 @@
 
   // We need to always trigger UpdateCompatModeButton regardless of value
   // change because it need to be called even when the property is set to
-  // ArcResizeLockType::RESIZABLE, which is the the default value of
+  // ArcResizeLockType::NONE, which is the the default value of
   // kArcResizeLockTypeKey, and the new value is the same as |old| in that case.
   AppIdObserver::RunOnReady(
       window, base::BindOnce(&CompatModeButtonController::Update,
@@ -230,24 +231,20 @@
   if (new_value == old_value)
     return;
 
-  if (ShouldEnableResizeLock(new_value)) {
-    // Both the resize lock value and app id are needed to enable resize lock.
-    AppIdObserver::RunOnReady(
-        window, base::BindOnce(
-                    [](base::WeakPtr<ArcResizeLockManager> manager,
-                       aura::Window* window) {
-                      if (!manager)
-                        return;
-                      if (!ShouldEnableResizeLock(window->GetProperty(
-                              ash::kArcResizeLockTypeKey))) {
-                        return;
-                      }
+  AppIdObserver::RunOnReady(
+      window, base::BindOnce(
+                  [](base::WeakPtr<ArcResizeLockManager> manager,
+                     aura::Window* window) {
+                    if (!manager)
+                      return;
+                    if (ShouldEnableResizeLock(
+                            window->GetProperty(ash::kArcResizeLockTypeKey))) {
                       manager->EnableResizeLock(window);
-                    },
-                    weak_ptr_factory_.GetWeakPtr()));
-  } else {
-    DisableResizeLock(window);
-  }
+                    } else {
+                      manager->DisableResizeLock(window);
+                    }
+                  },
+                  weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ArcResizeLockManager::OnWindowBoundsChanged(
@@ -278,15 +275,16 @@
     const ash::ArcResizeLockType resize_lock_value =
         window->GetProperty(ash::kArcResizeLockTypeKey);
     switch (resize_lock_value) {
-      case ash::ArcResizeLockType::RESIZE_LIMITED:
+      case ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE:
         pref_delegate_->SetResizeLockState(*app_id,
                                            mojom::ArcResizeLockState::ON);
         break;
-      case ash::ArcResizeLockType::FULLY_LOCKED:
+      case ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE:
         pref_delegate_->SetResizeLockState(
             *app_id, mojom::ArcResizeLockState::FULLY_LOCKED);
         break;
-      case ash::ArcResizeLockType::RESIZABLE:
+      case ash::ArcResizeLockType::NONE:
+      case ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE:
         NOTREACHED();
     }
     // As we updated the resize lock state above, we need to update compat mode
@@ -296,7 +294,7 @@
     if (ShouldShowSplashScreenDialog(pref_delegate_)) {
       const bool is_for_unresizable =
           window->GetProperty(ash::kArcResizeLockTypeKey) ==
-          ash::ArcResizeLockType::FULLY_LOCKED;
+          ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE;
       WindowActivationObserver::RunOnActivated(
           window, base::BindOnce(&ArcSplashScreenDialogView::Show, window,
                                  is_for_unresizable));
@@ -313,7 +311,12 @@
   const bool erased = resize_lock_enabled_windows_.erase(window);
   if (!erased)
     return;
-
+  const auto app_id = GetAppId(window);
+  DCHECK(app_id);
+  if (window->GetProperty(ash::kArcResizeLockTypeKey) ==
+      ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE) {
+    pref_delegate_->SetResizeLockState(*app_id, mojom::ArcResizeLockState::OFF);
+  }
   window->SetProperty(ash::kResizeShadowTypeKey,
                       ash::ResizeShadowType::kUnlock);
   // Hide shadow effect on window. ash::Shell may not exist in tests.
diff --git a/components/arc/compat_mode/arc_resize_lock_manager_unittest.cc b/components/arc/compat_mode/arc_resize_lock_manager_unittest.cc
index 5eacb9cf..e750850 100644
--- a/components/arc/compat_mode/arc_resize_lock_manager_unittest.cc
+++ b/components/arc/compat_mode/arc_resize_lock_manager_unittest.cc
@@ -74,9 +74,11 @@
 
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kNonInterestedPropKey, false)
 
-constexpr std::array<ash::ArcResizeLockType, 3> kArcResizeLockTypes{
-    ash::ArcResizeLockType::RESIZABLE, ash::ArcResizeLockType::RESIZE_LIMITED,
-    ash::ArcResizeLockType::FULLY_LOCKED};
+constexpr std::array<ash::ArcResizeLockType, 4> kArcResizeLockTypes{
+    ash::ArcResizeLockType::NONE,
+    ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE,
+    ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE,
+    ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE};
 
 }  // namespace
 
@@ -139,31 +141,31 @@
 
   // Test EnableResizeLock will be called by the property change.
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZE_LIMITED);
+                          ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
   EXPECT_TRUE(IsResizeLockEnabled(arc_window.get()));
 
   // Test nothing will be called by the property overwrite with the same value.
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZE_LIMITED);
+                          ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
   EXPECT_TRUE(IsResizeLockEnabled(arc_window.get()));
 
   // Test DisableResizeLock will be called by the property change.
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZABLE);
+                          ash::ArcResizeLockType::NONE);
   EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
 
-  // Test if enabling/disabling |FULLY_LOCKED| toggles the resize lock state
-  // properly.
+  // Test if enabling/disabling |RESIZE_DISABLED_NONTOGGLABLE| toggles the
+  // resize lock state properly.
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::FULLY_LOCKED);
+                          ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE);
   EXPECT_TRUE(IsResizeLockEnabled(arc_window.get()));
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZABLE);
+                          ash::ArcResizeLockType::NONE);
   EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
 
   // Test nothing will be called by the property overwrite with the same value.
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZABLE);
+                          ash::ArcResizeLockType::NONE);
   EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
 
   // Test nothing will be called by the NON-interested property change.
@@ -178,7 +180,7 @@
   EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
 
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZE_LIMITED);
+                          ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
   EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
   // Should ignore null.
   arc_window->ClearProperty(ash::kAppIDKey);
@@ -200,11 +202,11 @@
   EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
 
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZE_LIMITED);
+                          ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
   EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
 
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZABLE);
+                          ash::ArcResizeLockType::NONE);
   EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
 
   arc_window->SetProperty(ash::kAppIDKey, app_id);
@@ -215,11 +217,12 @@
 TEST_F(ArcResizeLockManagerTest, TestNonArcWindow) {
   auto non_arc_window = CreateFakeWindow(false);
   EXPECT_FALSE(IsResizeLockEnabled(non_arc_window.get()));
-  non_arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                              ash::ArcResizeLockType::RESIZE_LIMITED);
+  non_arc_window->SetProperty(
+      ash::kArcResizeLockTypeKey,
+      ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
   EXPECT_FALSE(IsResizeLockEnabled(non_arc_window.get()));
   non_arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                              ash::ArcResizeLockType::RESIZABLE);
+                              ash::ArcResizeLockType::NONE);
   EXPECT_FALSE(IsResizeLockEnabled(non_arc_window.get()));
 }
 
@@ -231,24 +234,24 @@
   arc_window->SetProperty(ash::kAppIDKey, app_id);
   EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
 
-  // Test for RESIZE_LIMITED.
+  // Test for RESIZE_DISABLED_TOGGLABLE.
   pref_delegate()->SetResizeLockState(app_id, mojom::ArcResizeLockState::READY);
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZE_LIMITED);
+                          ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
   EXPECT_EQ(pref_delegate()->GetResizeLockState(app_id),
             mojom::ArcResizeLockState::ON);
 
   // Test for RESIZABLE.
   pref_delegate()->SetResizeLockState(app_id, mojom::ArcResizeLockState::READY);
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZABLE);
+                          ash::ArcResizeLockType::NONE);
   EXPECT_EQ(pref_delegate()->GetResizeLockState(app_id),
             mojom::ArcResizeLockState::READY);
 
-  // Test for FULLY_LOCKED.
+  // Test for RESIZE_DISABLED_NONTOGGLABLE.
   pref_delegate()->SetResizeLockState(app_id, mojom::ArcResizeLockState::READY);
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::FULLY_LOCKED);
+                          ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE);
   EXPECT_EQ(pref_delegate()->GetResizeLockState(app_id),
             mojom::ArcResizeLockState::FULLY_LOCKED);
 }
@@ -312,31 +315,31 @@
   // Locked for resize locked windows.
   resize_shadow_updated = false;
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZE_LIMITED);
+                          ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
   EXPECT_EQ(arc_window->GetProperty(ash::kResizeShadowTypeKey),
             ash::ResizeShadowType::kLock);
   EXPECT_TRUE(resize_shadow_updated);
   // No redundant property update.
   resize_shadow_updated = false;
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZE_LIMITED);
+                          ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
   EXPECT_FALSE(resize_shadow_updated);
   resize_shadow_updated = false;
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::FULLY_LOCKED);
+                          ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE);
   EXPECT_FALSE(resize_shadow_updated);
 
   // Unlocked for non-resize locked windows.
   resize_shadow_updated = false;
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZABLE);
+                          ash::ArcResizeLockType::NONE);
   EXPECT_EQ(arc_window->GetProperty(ash::kResizeShadowTypeKey),
             ash::ResizeShadowType::kUnlock);
   EXPECT_TRUE(resize_shadow_updated);
   // No redundant property update.
   resize_shadow_updated = false;
   arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZABLE);
+                          ash::ArcResizeLockType::NONE);
   EXPECT_FALSE(resize_shadow_updated);
 }
 
@@ -354,7 +357,7 @@
     auto arc_window = CreateFakeWindow(true);
     EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
     arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                            ash::ArcResizeLockType::RESIZE_LIMITED);
+                            ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
     EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
   }
 
@@ -363,7 +366,7 @@
     auto arc_window = CreateFakeWindow(true);
     EXPECT_FALSE(IsResizeLockEnabled(arc_window.get()));
     arc_window->SetProperty(ash::kArcResizeLockTypeKey,
-                            ash::ArcResizeLockType::RESIZE_LIMITED);
+                            ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
     arc_window->SetProperty(ash::kAppIDKey, std::string("app-id"));
     EXPECT_TRUE(IsResizeLockEnabled(arc_window.get()));
 
diff --git a/components/arc/compat_mode/compat_mode_button_controller.cc b/components/arc/compat_mode/compat_mode_button_controller.cc
index 6beccf2e..b2f6ac25d 100644
--- a/components/arc/compat_mode/compat_mode_button_controller.cc
+++ b/components/arc/compat_mode/compat_mode_button_controller.cc
@@ -66,10 +66,10 @@
   if (!app_id)
     return;
   auto* const frame_header = GetFrameHeader(window);
+  // TODO(b/200230343): Replace it with resize lock type.
   const auto resize_lock_state = pref_delegate->GetResizeLockState(*app_id);
   if (resize_lock_state == mojom::ArcResizeLockState::UNDEFINED ||
       resize_lock_state == mojom::ArcResizeLockState::READY) {
-    frame_header->SetCenterButton(nullptr);
     return;
   }
   auto* compat_mode_button = frame_header->GetCenterButton();
@@ -101,15 +101,17 @@
 
   const auto resize_lock_type = window->GetProperty(ash::kArcResizeLockTypeKey);
   switch (resize_lock_type) {
-    case ash::ArcResizeLockType::RESIZE_LIMITED:
-    case ash::ArcResizeLockType::RESIZABLE:
+    case ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE:
+    case ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE:
       compat_mode_button->SetEnabled(true);
       break;
-    case ash::ArcResizeLockType::FULLY_LOCKED:
+    case ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE:
       compat_mode_button->SetEnabled(false);
       compat_mode_button->SetTooltipText(l10n_util::GetStringUTF16(
           IDS_ASH_ARC_APP_COMPAT_DISABLED_COMPAT_MODE_BUTTON_TOOLTIP_PHONE));
       break;
+    case ash::ArcResizeLockType::NONE:
+      NOTREACHED();
   }
 
   UpdateAshAccelerator(pref_delegate, window);
@@ -136,13 +138,16 @@
 
   const auto resize_lock_type = window->GetProperty(ash::kArcResizeLockTypeKey);
   switch (resize_lock_type) {
-    case ash::ArcResizeLockType::RESIZE_LIMITED:
-    case ash::ArcResizeLockType::RESIZABLE:
+    case ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE:
+    // TODO(b/200230343): Call NOTREACHED() once the client has shifted to
+    // the new protocol.
+    case ash::ArcResizeLockType::NONE:
+    case ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE:
       frame_view->SetToggleResizeLockMenuCallback(base::BindRepeating(
           &CompatModeButtonController::ToggleResizeToggleMenu, GetWeakPtr(),
           window, pref_delegate));
       break;
-    case ash::ArcResizeLockType::FULLY_LOCKED:
+    case ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE:
       frame_view->ClearToggleResizeLockMenuCallback();
       break;
   }
diff --git a/components/arc/compat_mode/resize_util.cc b/components/arc/compat_mode/resize_util.cc
index 6094eeb..c9c187f 100644
--- a/components/arc/compat_mode/resize_util.cc
+++ b/components/arc/compat_mode/resize_util.cc
@@ -180,8 +180,9 @@
 }
 
 ResizeCompatMode PredictCurrentMode(const aura::Window* window) {
-  if (window->GetProperty(ash::kArcResizeLockTypeKey) ==
-      ash::ArcResizeLockType::RESIZABLE) {
+  const auto resize_lock_type = window->GetProperty(ash::kArcResizeLockTypeKey);
+  if (resize_lock_type == ash::ArcResizeLockType::NONE ||
+      resize_lock_type == ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE) {
     return ResizeCompatMode::kResizable;
   }
 
diff --git a/components/arc/compat_mode/test/compat_mode_test_base.cc b/components/arc/compat_mode/test/compat_mode_test_base.cc
index 36e51a0..aad6178b 100644
--- a/components/arc/compat_mode/test/compat_mode_test_base.cc
+++ b/components/arc/compat_mode/test/compat_mode_test_base.cc
@@ -121,19 +121,23 @@
   const auto app_id = GetAppId(window);
   switch (pref_delegate()->GetResizeLockState(*app_id)) {
     case mojom::ArcResizeLockState::UNDEFINED:
+      window->SetProperty(ash::kArcResizeLockTypeKey,
+                          ash::ArcResizeLockType::NONE);
+      break;
     case mojom::ArcResizeLockState::OFF:
       window->SetProperty(ash::kArcResizeLockTypeKey,
-                          ash::ArcResizeLockType::RESIZABLE);
+                          ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE);
       break;
     case mojom::ArcResizeLockState::ON:
     case mojom::ArcResizeLockState::READY:
     case mojom::ArcResizeLockState::FULLY_LOCKED:
       if (widget->widget_delegate()->CanResize()) {
         window->SetProperty(ash::kArcResizeLockTypeKey,
-                            ash::ArcResizeLockType::RESIZE_LIMITED);
+                            ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
       } else {
-        window->SetProperty(ash::kArcResizeLockTypeKey,
-                            ash::ArcResizeLockType::FULLY_LOCKED);
+        window->SetProperty(
+            ash::kArcResizeLockTypeKey,
+            ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE);
       }
       break;
   }
diff --git a/components/arc/metrics/arc_metrics_service.cc b/components/arc/metrics/arc_metrics_service.cc
index e33e933..01896b4 100644
--- a/components/arc/metrics/arc_metrics_service.cc
+++ b/components/arc/metrics/arc_metrics_service.cc
@@ -374,6 +374,12 @@
   base::UmaHistogramBoolean(metric_name, success);
 }
 
+void ArcMetricsService::ReportImageCopyPasteCompatAction(
+    mojom::ArcImageCopyPasteCompatAction action_type) {
+  base::UmaHistogramEnumeration("Arc.ImageCopyPasteCompatOperationType",
+                                action_type);
+}
+
 void ArcMetricsService::NotifyLowMemoryKill() {
   for (auto& obs : app_kill_observers_)
     obs.OnArcLowMemoryKill();
diff --git a/components/arc/metrics/arc_metrics_service.h b/components/arc/metrics/arc_metrics_service.h
index e3f3020..13f2bba 100644
--- a/components/arc/metrics/arc_metrics_service.h
+++ b/components/arc/metrics/arc_metrics_service.h
@@ -130,6 +130,8 @@
                                 uint32_t number_of_directories) override;
   void ReportMainAccountHashMigrationMetrics(
       mojom::MainAccountHashMigrationStatus status) override;
+  void ReportImageCopyPasteCompatAction(
+      mojom::ArcImageCopyPasteCompatAction action_type) override;
 
   // wm::ActivationChangeObserver overrides.
   // Records to UMA when a user has interacted with an ARC app window.
diff --git a/components/arc/mojom/metrics.mojom b/components/arc/mojom/metrics.mojom
index 51b49f4c..d4b46fc 100644
--- a/components/arc/mojom/metrics.mojom
+++ b/components/arc/mojom/metrics.mojom
@@ -1,7 +1,7 @@
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-// Next MinVersion: 15
+// Next MinVersion: 16
 
 module arc.mojom;
 
@@ -205,6 +205,14 @@
   kImageDragDropFromArc = 7,
 };
 
+[Extensible]
+enum ArcImageCopyPasteCompatAction {
+  kPasteFromFiles = 0,
+  kDragFromFiles = 1,
+  kPasteFromBrowser = 2,
+  kDragFromBrowser = 3,
+};
+
 // Enumerates variations of Low Latency Stylus Library.
 [Extensible]
 enum LowLatencyStylusLibraryType {
@@ -266,7 +274,7 @@
   // tools/metrics/histograms/enums.xml.
 };
 
-// Next method ID: 18
+// Next method ID: 19
 interface MetricsHost {
   // Reports boot progress events from ARC instance.
   ReportBootProgress@0(array<BootProgressEvent> events,
@@ -336,6 +344,15 @@
   // Reports main account hash migration status.
   [MinVersion=14] ReportMainAccountHashMigrationMetrics@17(
       MainAccountHashMigrationStatus status);
+
+  // Reports the operation type (copy & paste or drag & drop) and the source of
+  // the image (from browser or Files app) when image copy-paste app compat is
+  // triggered.
+  // Image copy-paste app compat is a feature that allows insertion of images to
+  // Android apps through commitContent IME API or Android intent, when the app
+  // don't support images in the clipboard.
+  [MinVersion=15] ReportImageCopyPasteCompatAction@18(
+      ArcImageCopyPasteCompatAction action_type);
 };
 
 // Next method ID: 3
diff --git a/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc b/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
index 7676792..c70807a 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
+++ b/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
@@ -171,8 +171,13 @@
       public ::testing::WithParamInterface<bool> {
  public:
   ContentAutofillDriverFactoryTest_WithOrWithoutBfCache() {
-    scoped_feature_list_.InitWithFeatureState(::features::kBackForwardCache,
-                                              use_bfcache());
+    std::vector<base::Feature> enabled;
+    // Allow BackForwardCache for all devices regardless of their memory.
+    std::vector<base::Feature> disabled{
+        ::features::kBackForwardCacheMemoryControls};
+    (use_bfcache() ? enabled : disabled)
+        .push_back(::features::kBackForwardCache);
+    scoped_feature_list_.InitWithFeatures(enabled, disabled);
   }
 
   bool use_bfcache() { return GetParam(); }
@@ -260,9 +265,13 @@
 
   ASSERT_NE(orig_rfh_id, main_rfh()->GetGlobalId());
   // A new driver for main_rfh() has been created and the |orig_rfh| has now
-  // been removed in ContentAutofillDriverFactory::RenderFrameDeleted().
-  EXPECT_EQ(factory_test_api().GetDriver(orig_rfh),
-            use_bfcache() ? orig_driver : nullptr);
+  // been removed in ContentAutofillDriverFactory::RenderFrameDeleted(), unless
+  // BFcache is enabled (or main_rfh() happens to have the same address as
+  // |orig_rfh|).
+  if (use_bfcache())
+    EXPECT_EQ(factory_test_api().GetDriver(orig_rfh), orig_driver);
+  else if (main_rfh() != orig_rfh)
+    EXPECT_EQ(factory_test_api().GetDriver(orig_rfh), nullptr);
   EXPECT_NE(factory_test_api().GetDriver(main_rfh()), nullptr);
   EXPECT_EQ(factory_test_api().num_drivers(), use_bfcache() ? 2u : 1u);
 }
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index c211adc..7109984e3 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -736,29 +736,41 @@
   return 1.f / scale_;
 }
 
-void ClientControlledShellSurface::SetResizeLock(bool resize_lock) {
-  TRACE_EVENT1("exo", "ClientControlledShellSurface::SetResizeLock",
-               "resize_lock", resize_lock);
-  pending_resize_lock_ = resize_lock;
+void ClientControlledShellSurface::SetResizeLockType(
+    ash::ArcResizeLockType resize_lock_type) {
+  TRACE_EVENT1("exo", "ClientControlledShellSurface::SetResizeLockType",
+               "resize_lock_type", resize_lock_type);
+  pending_resize_lock_type_ = resize_lock_type;
 }
 
 void ClientControlledShellSurface::UpdateResizability() {
   TRACE_EVENT0("exo", "ClientControlledShellSurface::updateCanResize");
-  ash::ArcResizeLockType resizeLockType = ash::ArcResizeLockType::RESIZABLE;
-  if (pending_resize_lock_) {
-    // CalculateCanResize() returns the "raw" resizability of the window,
-    // in which the influence of the resize lock state is excluded.
-    if (CalculateCanResize()) {
-      resizeLockType = ash::ArcResizeLockType::RESIZE_LIMITED;
-    } else {
-      resizeLockType = ash::ArcResizeLockType::FULLY_LOCKED;
+  ash::ArcResizeLockType resize_lock_type = pending_resize_lock_type_;
+  // TODO(b/200230343): Remove this once the client's switched to the
+  // new protocol.
+  if (resize_lock_type == ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE &&
+      !CalculateCanResize()) {
+    resize_lock_type = ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE;
+  } else if (resize_lock_type == ash::ArcResizeLockType::NONE) {
+    const auto current_resize_lock_type =
+        widget_->GetNativeWindow()->GetProperty(ash::kArcResizeLockTypeKey);
+    if (current_resize_lock_type ==
+        ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE) {
+      resize_lock_type = ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE;
+    } else if (current_resize_lock_type ==
+               ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE) {
+      resize_lock_type = ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE;
     }
   }
   widget_->GetNativeWindow()->SetProperty(ash::kArcResizeLockTypeKey,
-                                          resizeLockType);
+                                          resize_lock_type);
   // If resize lock is enabled, the window is explicitly marded as unresizable.
   // Otherwise, the decision is deferred to the parent class.
-  if (ash::features::IsArcResizeLockEnabled() && pending_resize_lock_) {
+  if (ash::features::IsArcResizeLockEnabled() &&
+      (pending_resize_lock_type_ ==
+           ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE ||
+       pending_resize_lock_type_ ==
+           ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE)) {
     SetCanResize(false);
     return;
   }
diff --git a/components/exo/client_controlled_shell_surface.h b/components/exo/client_controlled_shell_surface.h
index b1ae5ce..0aa43f4 100644
--- a/components/exo/client_controlled_shell_surface.h
+++ b/components/exo/client_controlled_shell_surface.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "ash/display/screen_orientation_controller.h"
+#include "ash/public/cpp/arc_resize_lock_type.h"
 #include "ash/wm/client_controlled_state.h"
 #include "base/callback.h"
 #include "base/gtest_prod_util.h"
@@ -241,10 +242,10 @@
   // Used to scale incoming coordinates from the client to DP.
   float GetClientToDpScale() const;
 
-  // Sets the resize lock state to the surface.
-  void SetResizeLock(bool resize_lock);
+  // Sets the resize lock type to the surface.
+  void SetResizeLockType(ash::ArcResizeLockType resize_lock_type);
 
-  // Update the resizability based on the resize lock state.
+  // Update the resizability based on the resize lock type.
   void UpdateResizability() override;
 
  protected:
@@ -374,7 +375,8 @@
   // Accessibility ID provided by client.
   absl::optional<int32_t> client_accessibility_id_;
 
-  bool pending_resize_lock_ = false;
+  ash::ArcResizeLockType pending_resize_lock_type_ =
+      ash::ArcResizeLockType::NONE;
 };
 
 }  // namespace exo
diff --git a/components/exo/client_controlled_shell_surface_unittest.cc b/components/exo/client_controlled_shell_surface_unittest.cc
index c56e33d8..68962298 100644
--- a/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/components/exo/client_controlled_shell_surface_unittest.cc
@@ -2749,7 +2749,8 @@
     scoped_feature_list.InitAndEnableFeature(ash::features::kArcResizeLock);
     EXPECT_TRUE(shell_surface->CanResize());
 
-    shell_surface->SetResizeLock(true);
+    shell_surface->SetResizeLockType(
+        ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
     surface->Commit();
     EXPECT_FALSE(shell_surface->CanResize());
 
@@ -2757,16 +2758,16 @@
     // of the window.
     aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
     EXPECT_EQ(window->GetProperty(ash::kArcResizeLockTypeKey),
-              ash::ArcResizeLockType::RESIZE_LIMITED);
+              ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
     shell_surface->SetMinimumSize(gfx::Size(1, 1));
     shell_surface->SetMaximumSize(gfx::Size(1, 1));
     surface->Commit();
     EXPECT_EQ(window->GetProperty(ash::kArcResizeLockTypeKey),
-              ash::ArcResizeLockType::FULLY_LOCKED);
+              ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE);
     shell_surface->SetMinimumSize(gfx::Size(0, 0));
     shell_surface->SetMaximumSize(gfx::Size(0, 0));
 
-    shell_surface->SetResizeLock(false);
+    shell_surface->SetResizeLockType(ash::ArcResizeLockType::NONE);
     surface->Commit();
     EXPECT_TRUE(shell_surface->CanResize());
   }
@@ -2779,7 +2780,8 @@
     scoped_feature_list.InitAndDisableFeature(ash::features::kArcResizeLock);
     EXPECT_TRUE(shell_surface->CanResize());
 
-    shell_surface->SetResizeLock(true);
+    shell_surface->SetResizeLockType(
+        ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
     surface->Commit();
     EXPECT_TRUE(shell_surface->CanResize());
   }
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index b3ebf8c..07f004d 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -1392,8 +1392,15 @@
 gfx::Rect ShellSurfaceBase::GetVisibleBounds() const {
   // Use |geometry_| if set, otherwise use the visual bounds of the surface.
   if (geometry_.IsEmpty()) {
-    return root_surface() ? gfx::Rect(root_surface()->content_size())
-                          : gfx::Rect();
+    gfx::Size size;
+    if (root_surface()) {
+      size = root_surface()->content_size();
+      if (client_submits_surfaces_in_pixel_coordinates()) {
+        int dsf = std::ceil(host_window()->layer()->device_scale_factor());
+        size = gfx::ScaleToRoundedSize(size, 1.0f / dsf);
+      }
+    }
+    return gfx::Rect(size);
   }
 
   const auto* screen = display::Screen::GetScreen();
diff --git a/components/exo/sub_surface.h b/components/exo/sub_surface.h
index 492c7aa6..963a9d5 100644
--- a/components/exo/sub_surface.h
+++ b/components/exo/sub_surface.h
@@ -83,6 +83,7 @@
   void SetInitialWorkspace(const char* initial_workspace) override {}
   void Pin(bool trusted) override {}
   void Unpin() override {}
+  void SetClientSubmitsSurfacesInPixelCoordinates(bool enabled) override {}
 
   // Overridden from SurfaceObserver:
   void OnSurfaceDestroying(Surface* surface) override;
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index 59df8c7..1eb4951 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -1528,4 +1528,9 @@
     delegate_->Unpin();
 }
 
+void Surface::SetClientSubmitsSurfacesInPixelCoordinates(bool enabled) {
+  if (delegate_)
+    delegate_->SetClientSubmitsSurfacesInPixelCoordinates(enabled);
+}
+
 }  // namespace exo
diff --git a/components/exo/surface.h b/components/exo/surface.h
index e4db588..96b97aa 100644
--- a/components/exo/surface.h
+++ b/components/exo/surface.h
@@ -392,6 +392,8 @@
   // Release the pinned mode and allows the user to do other things again.
   void Unpin();
 
+  void SetClientSubmitsSurfacesInPixelCoordinates(bool enabled);
+
  private:
   struct State {
     State();
diff --git a/components/exo/surface_delegate.h b/components/exo/surface_delegate.h
index 51a9010..ea1f2eb 100644
--- a/components/exo/surface_delegate.h
+++ b/components/exo/surface_delegate.h
@@ -101,6 +101,8 @@
   // Releases the pinned mode and allows the user to do other things again.
   virtual void Unpin() = 0;
 
+  virtual void SetClientSubmitsSurfacesInPixelCoordinates(bool enabled) = 0;
+
  protected:
   virtual ~SurfaceDelegate() {}
 };
diff --git a/components/exo/surface_tree_host.cc b/components/exo/surface_tree_host.cc
index 2c6ae1a..430b7d9 100644
--- a/components/exo/surface_tree_host.cc
+++ b/components/exo/surface_tree_host.cc
@@ -213,6 +213,10 @@
   UpdateDisplayOnTree();
 }
 
+void SurfaceTreeHost::SetClientSubmitsSurfacesInPixelCoordinates(bool enabled) {
+  client_submits_surfaces_in_pixel_coordinates_ = enabled;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // display::DisplayObserver:
 void SurfaceTreeHost::OnDisplayMetricsChanged(const display::Display& display,
@@ -319,6 +323,15 @@
   gfx::Rect bounds = root_surface_->surface_hierarchy_content_bounds();
   host_window_->SetBounds(
       gfx::Rect(host_window_->bounds().origin(), bounds.size()));
+  // TODO(yjliu): a) consolidate with ClientControlledShellSurface. b) use the
+  // scale factor the buffer is created for to set the transform for
+  // synchronization.
+  if (client_submits_surfaces_in_pixel_coordinates_) {
+    gfx::Transform tr;
+    float s = ceil(host_window_->layer()->device_scale_factor());
+    tr.Scale(1.0f / s, 1.0f / s);
+    host_window_->SetTransform(tr);
+  }
   const bool fills_bounds_opaquely =
       bounds.size() == root_surface_->content_size() &&
       root_surface_->FillsBoundsOpaquely();
diff --git a/components/exo/surface_tree_host.h b/components/exo/surface_tree_host.h
index 65082066..e07e799 100644
--- a/components/exo/surface_tree_host.h
+++ b/components/exo/surface_tree_host.h
@@ -115,6 +115,7 @@
   void SetInitialWorkspace(const char* initial_workspace) override {}
   void Pin(bool trusted) override {}
   void Unpin() override {}
+  void SetClientSubmitsSurfacesInPixelCoordinates(bool enabled) override;
 
   // display::DisplayObserver:
   void OnDisplayMetricsChanged(const display::Display& display,
@@ -138,6 +139,10 @@
   // not clipped.
   virtual void UpdateHostWindowBounds();
 
+  bool client_submits_surfaces_in_pixel_coordinates() const {
+    return client_submits_surfaces_in_pixel_coordinates_;
+  }
+
  private:
   viz::CompositorFrame PrepareToSubmitCompositorFrame();
 
@@ -172,6 +177,8 @@
 
   int64_t display_id_ = display::kInvalidDisplayId;
 
+  bool client_submits_surfaces_in_pixel_coordinates_ = false;
+
   base::WeakPtrFactory<SurfaceTreeHost> weak_ptr_factory_{this};
 };
 
diff --git a/components/exo/wayland/protocol/aura-shell.xml b/components/exo/wayland/protocol/aura-shell.xml
index 5118f0c2..3cbf2b2 100644
--- a/components/exo/wayland/protocol/aura-shell.xml
+++ b/components/exo/wayland/protocol/aura-shell.xml
@@ -24,7 +24,7 @@
     DEALINGS IN THE SOFTWARE.
   </copyright>
 
-  <interface name="zaura_shell" version="25">
+  <interface name="zaura_shell" version="26">
     <description summary="aura_shell">
       The global interface exposing aura shell capabilities is used to
       instantiate an interface extension for a wl_surface object.
@@ -108,6 +108,7 @@
       </description>
       <arg name="active_desk_index" type="int" summary="index of the active desk"/>
     </event>
+
     <!-- Version 24 additions -->
     <event name="activated" since="24">
       <description summary="activated surface changed">
@@ -116,6 +117,16 @@
       <arg name="gained_active" type="object" interface="wl_surface" allow-null="true"/>
       <arg name="lost_active" type="object" interface="wl_surface" allow-null="true"/>
     </event>
+
+    <!-- Version 26 additions -->
+    <request name="surface_submission_in_pixel_coordinates" since="26">
+      <description summary="surfaces will be submitted in pixel coordinates">
+        Informs the server that when submitting surfaces, this client will not
+        use wl_surface_set_buffer_scale to report the scales, nor will it apply
+        scale via vp_viewporter. Instead the server should apply an appropriate
+        scale transform to have the submitted buffers composited correctly.
+      </description>
+    </request>
   </interface>
 
   <interface name="zaura_surface" version="25">
diff --git a/components/exo/wayland/zaura_shell.cc b/components/exo/wayland/zaura_shell.cc
index 0f3a0c4..8aed4ea 100644
--- a/components/exo/wayland/zaura_shell.cc
+++ b/components/exo/wayland/zaura_shell.cc
@@ -766,6 +766,12 @@
                          const std::u16string& new_name) override {
     OnDesksChanged();
   }
+  void set_client_submits_surfaces_in_pixel_coordinates(bool enabled) {
+    client_submits_surfaces_in_pixel_coordinates_ = enabled;
+  }
+  bool client_submits_surfaces_in_pixel_coordinates() const {
+    return client_submits_surfaces_in_pixel_coordinates_;
+  }
 
  private:
   void OnDesksChanged() {
@@ -826,6 +832,8 @@
   // The aura shell resource associated with observer.
   wl_resource* const aura_shell_resource_;
 
+  bool client_submits_surfaces_in_pixel_coordinates_ = false;
+
   base::WeakPtrFactory<WaylandAuraShell> weak_ptr_factory_{this};
 };
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH))
@@ -842,6 +850,14 @@
     return;
   }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (WaylandAuraShell* aura_shell =
+          GetUserDataAs<WaylandAuraShell>(resource)) {
+    surface->SetClientSubmitsSurfacesInPixelCoordinates(
+        aura_shell->client_submits_surfaces_in_pixel_coordinates());
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH))
+
   wl_resource* aura_surface_resource = wl_resource_create(
       client, &zaura_surface_interface, wl_resource_get_version(resource), id);
 
@@ -866,11 +882,17 @@
   SetImplementation(aura_output_resource, nullptr, std::move(aura_output));
 }
 
-const struct zaura_shell_interface aura_shell_implementation = {
-    aura_shell_get_aura_surface,
-    aura_shell_get_aura_output,
-};
+void aura_shell_surface_submission_in_pixel_coordinates(wl_client* client,
+                                                        wl_resource* resource) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (WaylandAuraShell* aura_shell = GetUserDataAs<WaylandAuraShell>(resource))
+    aura_shell->set_client_submits_surfaces_in_pixel_coordinates(true);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH))
+}
 
+const struct zaura_shell_interface aura_shell_implementation = {
+    aura_shell_get_aura_surface, aura_shell_get_aura_output,
+    aura_shell_surface_submission_in_pixel_coordinates};
 }  // namespace
 
 void bind_aura_shell(wl_client* client,
diff --git a/components/exo/wayland/zaura_shell.h b/components/exo/wayland/zaura_shell.h
index 9c2a72e..f51497f 100644
--- a/components/exo/wayland/zaura_shell.h
+++ b/components/exo/wayland/zaura_shell.h
@@ -18,7 +18,7 @@
 namespace exo {
 namespace wayland {
 
-constexpr uint32_t kZAuraShellVersion = 25;
+constexpr uint32_t kZAuraShellVersion = 26;
 
 // Adds bindings to the Aura Shell. Normally this implies Ash on ChromeOS
 // builds. On non-ChromeOS builds the protocol provides access to Aura windowing
diff --git a/components/exo/wayland/zaura_shell_unittest.cc b/components/exo/wayland/zaura_shell_unittest.cc
index 24e16cac..ceb0fcd 100644
--- a/components/exo/wayland/zaura_shell_unittest.cc
+++ b/components/exo/wayland/zaura_shell_unittest.cc
@@ -117,6 +117,10 @@
               (override));
   MOCK_METHOD(void, Pin, (bool trusted), (override));
   MOCK_METHOD(void, Unpin, (), (override));
+  MOCK_METHOD(void,
+              SetClientSubmitsSurfacesInPixelCoordinates,
+              (bool),
+              (override));
 };
 
 }  // namespace
diff --git a/components/exo/wayland/zcr_remote_shell_impl.cc b/components/exo/wayland/zcr_remote_shell_impl.cc
index c36259f..66a491c 100644
--- a/components/exo/wayland/zcr_remote_shell_impl.cc
+++ b/components/exo/wayland/zcr_remote_shell_impl.cc
@@ -4,6 +4,7 @@
 
 #include "components/exo/wayland/zcr_remote_shell_impl.h"
 
+#include "ash/public/cpp/arc_resize_lock_type.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
 #include "ash/wm/window_resizer.h"
@@ -1400,12 +1401,14 @@
 }
 
 void remote_surface_set_resize_lock(wl_client* client, wl_resource* resource) {
-  GetUserDataAs<ClientControlledShellSurface>(resource)->SetResizeLock(true);
+  GetUserDataAs<ClientControlledShellSurface>(resource)->SetResizeLockType(
+      ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
 }
 
 void remote_surface_unset_resize_lock(wl_client* client,
                                       wl_resource* resource) {
-  GetUserDataAs<ClientControlledShellSurface>(resource)->SetResizeLock(false);
+  GetUserDataAs<ClientControlledShellSurface>(resource)->SetResizeLockType(
+      ash::ArcResizeLockType::NONE);
 }
 
 void remote_surface_set_bounds_in_output(wl_client* client,
@@ -1422,6 +1425,13 @@
       display_handler->id(), gfx::Rect(x, y, width, height));
 }
 
+void remote_surface_set_resize_lock_type(wl_client* client,
+                                         wl_resource* resource,
+                                         uint32_t type) {
+  GetUserDataAs<ClientControlledShellSurface>(resource)->SetResizeLockType(
+      static_cast<ash::ArcResizeLockType>(type));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // notification_surface_interface:
 
diff --git a/components/exo/wayland/zcr_remote_shell_impl.h b/components/exo/wayland/zcr_remote_shell_impl.h
index d532f21..75382de 100644
--- a/components/exo/wayland/zcr_remote_shell_impl.h
+++ b/components/exo/wayland/zcr_remote_shell_impl.h
@@ -334,6 +334,10 @@
                                          int32_t width,
                                          int32_t height);
 
+void remote_surface_set_resize_lock_type(wl_client* client,
+                                         wl_resource* resource,
+                                         uint32_t mode);
+
 void remote_surface_block_ime(wl_client* client, wl_resource* resource);
 
 void remote_surface_unblock_ime(wl_client* client, wl_resource* resource);
diff --git a/components/exo/wayland/zcr_remote_shell_v2.cc b/components/exo/wayland/zcr_remote_shell_v2.cc
index 7edfaa0..8d7196f 100644
--- a/components/exo/wayland/zcr_remote_shell_v2.cc
+++ b/components/exo/wayland/zcr_remote_shell_v2.cc
@@ -105,6 +105,7 @@
         zcr_remote_shell::remote_surface_set_resize_lock,
         zcr_remote_shell::remote_surface_unset_resize_lock,
         zcr_remote_shell::remote_surface_set_bounds_in_output,
+        zcr_remote_shell::remote_surface_set_resize_lock_type,
 };
 
 const struct zcr_notification_surface_v2_interface
diff --git a/components/keep_alive_registry/keep_alive_registry.cc b/components/keep_alive_registry/keep_alive_registry.cc
index 913402e..0f77ce7 100644
--- a/components/keep_alive_registry/keep_alive_registry.cc
+++ b/components/keep_alive_registry/keep_alive_registry.cc
@@ -23,7 +23,7 @@
 }
 
 bool KeepAliveRegistry::IsKeepingAlive() const {
-  return registered_count_ > 0;
+  return registered_count_ > 0 && !(IsRestartAllowed() && is_restarting_);
 }
 
 bool KeepAliveRegistry::IsKeepingAliveOnlyByBrowserOrigin() const {
@@ -88,6 +88,21 @@
   is_shutting_down_ = value;
 }
 
+bool KeepAliveRegistry::IsRestarting() const {
+  return is_restarting_;
+}
+
+void KeepAliveRegistry::SetRestarting() {
+  bool old_keeping_alive = IsKeepingAlive();
+  is_restarting_ = true;
+  bool new_keeping_alive = IsKeepingAlive();
+
+  // keep alive state can be updated by |is_restarting_| change.
+  // If that happens, notify observers.
+  if (old_keeping_alive != new_keeping_alive)
+    OnKeepAliveStateChanged(new_keeping_alive);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Private methods
 
diff --git a/components/keep_alive_registry/keep_alive_registry.h b/components/keep_alive_registry/keep_alive_registry.h
index e75d1ea..f80e223 100644
--- a/components/keep_alive_registry/keep_alive_registry.h
+++ b/components/keep_alive_registry/keep_alive_registry.h
@@ -51,6 +51,12 @@
   // Call when shutting down to ensure registering a new KeepAlive CHECKs.
   void SetIsShuttingDown(bool value = true);
 
+  // True if restarting is in progress.
+  bool IsRestarting() const;
+
+  // Called when restarting is triggered.
+  void SetRestarting();
+
  private:
   friend struct base::DefaultSingletonTraits<KeepAliveRegistry>;
   // Friend to be able to use Register/Unregister
@@ -100,6 +106,9 @@
   // Used to guard against registering during shutdown.
   bool is_shutting_down_ = false;
 
+  // Used to handle KeepAliveRestartOption::ENABLED.
+  bool is_restarting_ = false;
+
   base::ObserverList<KeepAliveStateObserver>::Unchecked observers_;
 };
 
diff --git a/components/keep_alive_registry/keep_alive_registry_unittest.cc b/components/keep_alive_registry/keep_alive_registry_unittest.cc
index cc4cba7..05bfbc3 100644
--- a/components/keep_alive_registry/keep_alive_registry_unittest.cc
+++ b/components/keep_alive_registry/keep_alive_registry_unittest.cc
@@ -137,6 +137,70 @@
   EXPECT_EQ(0, on_restart_forbidden_call_count_);
 }
 
+// Check that KeepAliveState is changed on attempting restarting,
+// if the remaining keepalive is only RestartOption::ENABLED.
+TEST_F(KeepAliveRegistryTest, AttemptRestarting) {
+  std::unique_ptr<ScopedKeepAlive> keep_alive, keep_alive_restart;
+
+  EXPECT_EQ(0, on_restart_allowed_call_count_);
+  EXPECT_EQ(0, on_restart_forbidden_call_count_);
+
+  // With a normal keep alive, restart should not be allowed
+  keep_alive = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::CHROME_APP_DELEGATE, KeepAliveRestartOption::DISABLED);
+  ASSERT_EQ(1, start_keep_alive_call_count_--);  // decrement to ack
+  ASSERT_EQ(1, on_restart_forbidden_call_count_--);
+
+  // Restart should not be allowed if all KA don't allow it.
+  keep_alive_restart = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::CHROME_APP_DELEGATE, KeepAliveRestartOption::ENABLED);
+  // No state change.
+  EXPECT_EQ(0, start_keep_alive_call_count_);
+  EXPECT_EQ(0, on_restart_allowed_call_count_);
+
+  // Now restart should be allowed, the only one left allows it.
+  keep_alive.reset();
+  EXPECT_EQ(0, start_keep_alive_call_count_);
+  ASSERT_EQ(1, on_restart_allowed_call_count_--);
+  EXPECT_TRUE(registry_->IsRestartAllowed());
+
+  // Trigger the restarting procedure.
+  registry_->SetRestarting();
+  ASSERT_EQ(1, stop_keep_alive_call_count_);
+}
+
+TEST_F(KeepAliveRegistryTest,
+       AttemptRestartingBeforeDestroyingDisabledKeepAlive) {
+  std::unique_ptr<ScopedKeepAlive> keep_alive, keep_alive_restart;
+
+  EXPECT_EQ(0, on_restart_allowed_call_count_);
+  EXPECT_EQ(0, on_restart_forbidden_call_count_);
+
+  // With a normal keep alive, restart should not be allowed
+  keep_alive = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::CHROME_APP_DELEGATE, KeepAliveRestartOption::DISABLED);
+  ASSERT_EQ(1, start_keep_alive_call_count_--);  // decrement to ack
+  ASSERT_EQ(1, on_restart_forbidden_call_count_--);
+
+  // Restart should not be allowed if all KA don't allow it.
+  keep_alive_restart = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::CHROME_APP_DELEGATE, KeepAliveRestartOption::ENABLED);
+  // No state change.
+  EXPECT_EQ(0, start_keep_alive_call_count_);
+  EXPECT_EQ(0, on_restart_allowed_call_count_);
+
+  // Trigger the restarting procedure, during normal keep alive is still
+  // active.
+  registry_->SetRestarting();
+  EXPECT_EQ(0, stop_keep_alive_call_count_);
+
+  // Now restart should be allowed, the only one left allows it.
+  // This also updates KeepAliveState.
+  keep_alive.reset();
+  ASSERT_EQ(1, stop_keep_alive_call_count_--);
+  ASSERT_EQ(1, on_restart_allowed_call_count_--);
+}
+
 TEST_F(KeepAliveRegistryTest, WouldRestartWithoutTest) {
   // WouldRestartWithout() should have the same results as IsRestartAllowed()
   // when called with an empty vector.
diff --git a/components/services/app_service/public/cpp/BUILD.gn b/components/services/app_service/public/cpp/BUILD.gn
index 06a432c..9eaf622 100644
--- a/components/services/app_service/public/cpp/BUILD.gn
+++ b/components/services/app_service/public/cpp/BUILD.gn
@@ -215,6 +215,14 @@
   deps = [ "//components/services/app_service/public/mojom" ]
 }
 
+source_set("permission_utils") {
+  sources = [
+    "permission_utils.cc",
+    "permission_utils.h",
+  ]
+  deps = [ "//components/services/app_service/public/mojom" ]
+}
+
 source_set("unit_tests") {
   testonly = true
 
diff --git a/components/services/app_service/public/cpp/app_update.cc b/components/services/app_service/public/cpp/app_update.cc
index 102867f..341843e 100644
--- a/components/services/app_service/public/cpp/app_update.cc
+++ b/components/services/app_service/public/cpp/app_update.cc
@@ -629,7 +629,13 @@
   out << "Permissions:" << std::endl;
   for (const auto& permission : app.Permissions()) {
     out << "  ID: " << permission->permission_type;
-    out << " value: " << permission->value;
+    out << " value: " << std::endl;
+    if (permission->value->is_bool_value()) {
+      out << " bool_value: " << permission->value->get_bool_value();
+    }
+    if (permission->value->is_tristate_value()) {
+      out << " tristate_value: " << permission->value->get_tristate_value();
+    }
     out << " is_managed: " << permission->is_managed << std::endl;
   }
 
diff --git a/components/services/app_service/public/cpp/app_update_unittest.cc b/components/services/app_service/public/cpp/app_update_unittest.cc
index 1a0fd60..c432eda9 100644
--- a/components/services/app_service/public/cpp/app_update_unittest.cc
+++ b/components/services/app_service/public/cpp/app_update_unittest.cc
@@ -101,8 +101,8 @@
       apps::mojom::TriState value) {
     apps::mojom::PermissionPtr permission = apps::mojom::Permission::New();
     permission->permission_type = permission_type;
-    permission->value_type = apps::mojom::PermissionValueType::kTriState;
-    permission->value = static_cast<uint32_t>(value);
+    permission->value = apps::mojom::PermissionValue::New();
+    permission->value->set_tristate_value(value);
     return permission;
   }
 
diff --git a/components/services/app_service/public/cpp/permission_utils.cc b/components/services/app_service/public/cpp/permission_utils.cc
new file mode 100644
index 0000000..fd6dd61
--- /dev/null
+++ b/components/services/app_service/public/cpp/permission_utils.cc
@@ -0,0 +1,20 @@
+// 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/services/app_service/public/cpp/permission_utils.h"
+
+namespace apps_util {
+
+bool IsPermissionEnabled(
+    const apps::mojom::PermissionValuePtr& permission_value) {
+  if (permission_value->is_tristate_value()) {
+    return permission_value->get_tristate_value() ==
+           apps::mojom::TriState::kAllow;
+  } else if (permission_value->is_bool_value()) {
+    return permission_value->get_bool_value();
+  }
+  return false;
+}
+
+}  // namespace apps_util
diff --git a/components/services/app_service/public/cpp/permission_utils.h b/components/services/app_service/public/cpp/permission_utils.h
new file mode 100644
index 0000000..cde4f45
--- /dev/null
+++ b/components/services/app_service/public/cpp/permission_utils.h
@@ -0,0 +1,20 @@
+// 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_SERVICES_APP_SERVICE_PUBLIC_CPP_PERMISSION_UTILS_H_
+#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PERMISSION_UTILS_H_
+
+#include "components/services/app_service/public/mojom/types.mojom.h"
+
+namespace apps_util {
+
+// Check whether the |permission_value| equavalent to permission enabled.
+// The permission value could be a TriState or a bool. If it is TriState,
+// only Allow represent permission enabled.
+bool IsPermissionEnabled(
+    const apps::mojom::PermissionValuePtr& permission_value);
+
+}  // namespace apps_util
+
+#endif  // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PERMISSION_UTILS_H_
diff --git a/components/services/app_service/public/mojom/types.mojom b/components/services/app_service/public/mojom/types.mojom
index 82a47a1..d73af3a 100644
--- a/components/services/app_service/public/mojom/types.mojom
+++ b/components/services/app_service/public/mojom/types.mojom
@@ -89,9 +89,7 @@
 
 struct Permission {
   PermissionType permission_type;
-  PermissionValueType value_type;
-  // The semantics of value depends on the value_type.
-  uint32 value;
+  PermissionValue value;
   // If the permission is managed by an enterprise policy.
   bool is_managed;
 };
@@ -282,9 +280,9 @@
   kAsk,
 };
 
-enum PermissionValueType {
-  kBool,                       // Permission.value is a Bool (either 0 or 1).
-  kTriState,                   // Permission.value is a TriState.
+union PermissionValue {
+  bool bool_value;
+  TriState tristate_value;
 };
 
 // MenuItems are used to populate context menus, e.g. in the app list or shelf.
diff --git a/components/test/data/web_package/24_responses.har b/components/test/data/web_package/24_responses.har
new file mode 100644
index 0000000..a997299
--- /dev/null
+++ b/components/test/data/web_package/24_responses.har
@@ -0,0 +1,439 @@
+{
+  "log": {
+    "version": "1.2",
+    "entries": [
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/0"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/1"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/2"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/3"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/4"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/5"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/6"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/7"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/8"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/9"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/10"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/11"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/12"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/13"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/14"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/15"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/16"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/17"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/18"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/19"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/20"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/21"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/22"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "https://test.example.org/23"
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-Type",
+              "value": "text/html;"
+            }
+          ],
+          "content": {
+            "text": "<p>Hello Web Bundles!</p>"
+          }
+        }
+      }
+    ]
+  }
+}
diff --git a/components/test/data/web_package/24_responses.wbn b/components/test/data/web_package/24_responses.wbn
new file mode 100644
index 0000000..7767b17
--- /dev/null
+++ b/components/test/data/web_package/24_responses.wbn
Binary files differ
diff --git a/components/test/data/web_package/generate-test-wbns.sh b/components/test/data/web_package/generate-test-wbns.sh
index ff98865..42504b5 100755
--- a/components/test/data/web_package/generate-test-wbns.sh
+++ b/components/test/data/web_package/generate-test-wbns.sh
@@ -39,6 +39,11 @@
   -o simple.wbn \
   -primaryURL https://test.example.org/ \
 
+gen-bundle \
+  -version b2 \
+  -har 24_responses.har
+  -o 24_responses.wbn
+
 sign-bundle \
   -i hello.wbn \
   -certificate $sxg_test_data_dir/test.example.org.public.pem.cbor \
diff --git a/components/web_package/BUILD.gn b/components/web_package/BUILD.gn
index 709d321..e868b36 100644
--- a/components/web_package/BUILD.gn
+++ b/components/web_package/BUILD.gn
@@ -6,6 +6,8 @@
 
 static_library("web_package") {
   sources = [
+    "web_bundle_builder.cc",
+    "web_bundle_builder.h",
     "web_bundle_parser.cc",
     "web_bundle_parser.h",
     "web_bundle_parser_factory.cc",
@@ -25,28 +27,15 @@
   public_deps = [ "//components/web_package/mojom" ]
 }
 
-static_library("test_support") {
-  testonly = true
-  sources = [
-    "test_support/web_bundle_builder.cc",
-    "test_support/web_bundle_builder.h",
-  ]
-
-  deps = [
-    "//base",
-    "//components/cbor",
-  ]
-}
-
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "web_bundle_builder_unittest.cc",
     "web_bundle_parser_factory_unittest.cc",
     "web_bundle_parser_unittest.cc",
     "web_bundle_utils_unittest.cc",
   ]
   deps = [
-    ":test_support",
     ":web_package",
     "//base/test:test_support",
     "//components/cbor",
diff --git a/components/web_package/test_support/web_bundle_builder.cc b/components/web_package/web_bundle_builder.cc
similarity index 63%
rename from components/web_package/test_support/web_bundle_builder.cc
rename to components/web_package/web_bundle_builder.cc
index 3f9b9a44..d538c70 100644
--- a/components/web_package/test_support/web_bundle_builder.cc
+++ b/components/web_package/web_bundle_builder.cc
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/web_package/test_support/web_bundle_builder.h"
+#include "components/web_package/web_bundle_builder.h"
 
 #include <ostream>
 
+#include "base/big_endian.h"
+
 namespace web_package {
-namespace test {
 
 namespace {
 
@@ -22,24 +23,45 @@
   return cbor::Value(std::move(map));
 }
 
+// TODO(myrzakereyms): replace this method with cbor::writer::GetNumUintBytes.
+uint64_t GetNumUintBytes(uint64_t value) {
+  if (value < 24) {
+    return 0;
+  } else if (value <= 0xFF) {
+    return 1;
+  } else if (value <= 0xFFFF) {
+    return 2;
+  } else if (value <= 0xFFFFFFFF) {
+    return 4;
+  }
+  return 8;
+}
+
 }  // namespace
 
 WebBundleBuilder::WebBundleBuilder(const std::string& fallback_url,
                                    const std::string& manifest_url,
-                                   BundleVersion version)
+                                   BundleVersion version,
+                                   bool allow_invalid_utf8_strings_for_testing)
     : fallback_url_(fallback_url), version_(version) {
-  writer_config_.allow_invalid_utf8_for_testing = true;
+  writer_config_.allow_invalid_utf8_for_testing =
+      allow_invalid_utf8_strings_for_testing;
   if (!manifest_url.empty()) {
-    AddSection("manifest",
-               cbor::Value::InvalidUTF8StringValueForTesting(manifest_url));
+    AddSection("manifest", GetCborValueOfURL(manifest_url));
   }
   if (version == BundleVersion::kB2 && !fallback_url_.empty()) {
-    AddSection("primary",
-               cbor::Value::InvalidUTF8StringValueForTesting(fallback_url_));
+    AddSection("primary", GetCborValueOfURL(fallback_url_));
   }
 }
 WebBundleBuilder::~WebBundleBuilder() = default;
 
+cbor::Value WebBundleBuilder::GetCborValueOfURL(base::StringPiece url) {
+  if (writer_config_.allow_invalid_utf8_for_testing) {
+    return cbor::Value::InvalidUTF8StringValueForTesting(url);
+  }
+  return cbor::Value(url);
+}
+
 void WebBundleBuilder::AddExchange(base::StringPiece url,
                                    const Headers& response_headers,
                                    base::StringPiece payload) {
@@ -49,11 +71,6 @@
 WebBundleBuilder::ResponseLocation WebBundleBuilder::AddResponse(
     const Headers& headers,
     base::StringPiece payload) {
-  // We assume that the size of the CBOR header of the responses array is 1,
-  // which is true only if the responses array has no more than 23 elements.
-  DCHECK_LT(responses_.size(), 23u)
-      << "WebBundleBuilder cannot create bundles with more than 23 responses";
-
   cbor::Value::ArrayValue response_array;
   response_array.emplace_back(Encode(CreateHeaderMap(headers)));
   response_array.emplace_back(CreateByteString(payload));
@@ -69,19 +86,13 @@
     base::StringPiece url,
     base::StringPiece variants_value,
     std::vector<ResponseLocation> response_locations) {
-  cbor::Value::ArrayValue index_value_array;
   // 'b2' version does not include |variants_value| in the response array.
-  if (version_ == BundleVersion::kB1) {
-    index_value_array.emplace_back(CreateByteString(variants_value));
-  } else {
+  if (version_ != BundleVersion::kB1) {
     DCHECK_EQ(response_locations.size(), 1u);
   }
-  for (const auto& location : response_locations) {
-    index_value_array.emplace_back(location.offset);
-    index_value_array.emplace_back(location.length);
-  }
-  index_.insert({cbor::Value::InvalidUTF8StringValueForTesting(url),
-                 cbor::Value(index_value_array)});
+  delayed_index_.insert(
+      {std::string(url), std::make_pair(std::string(variants_value),
+                                        std::move(response_locations))});
 }
 
 void WebBundleBuilder::AddSection(base::StringPiece name, cbor::Value section) {
@@ -99,7 +110,26 @@
 }
 
 std::vector<uint8_t> WebBundleBuilder::CreateBundle() {
-  AddSection("index", cbor::Value(index_));
+  // Now that we know how many responses will be in the bundle,
+  // we want to shift all the offsets by the bytes required
+  // for the CBOR Array header and actually construct the index
+  // section.
+  int64_t initial_offset = 1 + GetNumUintBytes(responses_.size());
+  cbor::Value::MapValue index;
+  for (auto& entry : delayed_index_) {
+    auto& index_entry = entry.second;
+    cbor::Value::ArrayValue index_value_array;
+    if (version_ == BundleVersion::kB1) {
+      index_value_array.emplace_back(CreateByteString(index_entry.first));
+    }
+    for (auto& location : index_entry.second) {
+      index_value_array.emplace_back(location.offset + initial_offset);
+      index_value_array.emplace_back(location.length);
+    }
+    index.insert(
+        {GetCborValueOfURL(entry.first), cbor::Value(index_value_array)});
+  }
+  AddSection("index", cbor::Value(index));
   if (!authorities_.empty() || !vouched_subsets_.empty()) {
     cbor::Value::ArrayValue signatures_section;
     signatures_section.emplace_back(std::move(authorities_));
@@ -107,7 +137,7 @@
     AddSection("signatures", cbor::Value(std::move(signatures_section)));
   }
   AddSection("responses", cbor::Value(responses_));
-  return Encode(CreateTopLevel());
+  return CreateTopLevel();
 }
 
 cbor::Value WebBundleBuilder::CreateEncodedSigned(
@@ -124,7 +154,7 @@
   subset_hash_value.emplace_back(payload_integrity_header);
 
   cbor::Value::MapValue subset_hashes;
-  subset_hashes.emplace(url, std::move(subset_hash_value));
+  subset_hashes.emplace(GetCborValueOfURL(url), std::move(subset_hash_value));
 
   cbor::Value::MapValue signed_subset;
   signed_subset.emplace("validity-url", validity_url);
@@ -135,23 +165,29 @@
   return cbor::Value(Encode(cbor::Value(signed_subset)));
 }
 
-cbor::Value WebBundleBuilder::CreateTopLevel() {
+std::vector<uint8_t> WebBundleBuilder::CreateTopLevel() {
   cbor::Value::ArrayValue toplevel_array;
   toplevel_array.emplace_back(
       CreateByteString(u8"\U0001F310\U0001F4E6"));  // "🌐📦"
   if (version_ == BundleVersion::kB1) {
     toplevel_array.emplace_back(
         CreateByteString(base::StringPiece("b1\0\0", 4)));
-    toplevel_array.emplace_back(
-        cbor::Value::InvalidUTF8StringValueForTesting(fallback_url_));
+    toplevel_array.emplace_back(GetCborValueOfURL(fallback_url_));
   } else {
     toplevel_array.emplace_back(
         CreateByteString(base::StringPiece("b2\0\0", 4)));
   }
   toplevel_array.emplace_back(Encode(cbor::Value(section_lengths_)));
   toplevel_array.emplace_back(sections_);
-  toplevel_array.emplace_back(CreateByteString(""));  // length (ignored)
-  return cbor::Value(toplevel_array);
+  // Put a dummy 8-byte bytestring.
+  toplevel_array.emplace_back(cbor::Value::BinaryValue(8, 0));
+
+  std::vector<uint8_t> bundle = Encode(cbor::Value(toplevel_array));
+  char encoded[8];
+  base::WriteBigEndian(encoded, static_cast<uint64_t>(bundle.size()));
+  // Overwrite the dummy bytestring with the actual size.
+  memcpy(bundle.data() + bundle.size() - 8, encoded, 8);
+  return bundle;
 }
 
 std::vector<uint8_t> WebBundleBuilder::Encode(const cbor::Value& value) {
@@ -162,5 +198,4 @@
   return Encode(value).size();
 }
 
-}  // namespace test
 }  // namespace web_package
diff --git a/components/web_package/test_support/web_bundle_builder.h b/components/web_package/web_bundle_builder.h
similarity index 79%
rename from components/web_package/test_support/web_bundle_builder.h
rename to components/web_package/web_bundle_builder.h
index 898301c..e7a649a 100644
--- a/components/web_package/test_support/web_bundle_builder.h
+++ b/components/web_package/web_bundle_builder.h
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_WEB_PACKAGE_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_
-#define COMPONENTS_WEB_PACKAGE_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_
+#ifndef COMPONENTS_WEB_PACKAGE_WEB_BUNDLE_BUILDER_H_
+#define COMPONENTS_WEB_PACKAGE_WEB_BUNDLE_BUILDER_H_
 
+#include <map>
 #include <string>
 #include <utility>
 #include <vector>
@@ -19,9 +20,7 @@
   kB2,
 };
 
-namespace test {
-
-// This class can be used to create a Web Bundle binary in tests.
+// This class can be used to create a Web Bundle.
 class WebBundleBuilder {
  public:
   using Headers = std::vector<std::pair<std::string, std::string>>;
@@ -33,7 +32,8 @@
 
   WebBundleBuilder(const std::string& fallback_url,
                    const std::string& manifest_url,
-                   BundleVersion version = BundleVersion::kB1);
+                   BundleVersion version = BundleVersion::kB1,
+                   bool allow_invalid_utf8_strings_for_testing = false);
 
   ~WebBundleBuilder();
 
@@ -64,8 +64,9 @@
                                   base::StringPiece payload_integrity_header);
 
  private:
-  cbor::Value CreateTopLevel();
+  std::vector<uint8_t> CreateTopLevel();
   std::vector<uint8_t> Encode(const cbor::Value& value);
+  cbor::Value GetCborValueOfURL(base::StringPiece url);
 
   int64_t EncodedLength(const cbor::Value& value);
 
@@ -73,17 +74,15 @@
   std::string fallback_url_;
   cbor::Value::ArrayValue section_lengths_;
   cbor::Value::ArrayValue sections_;
-  cbor::Value::MapValue index_;
+  std::map<std::string, std::pair<std::string, std::vector<ResponseLocation>>>
+      delayed_index_;
   cbor::Value::ArrayValue responses_;
   cbor::Value::ArrayValue authorities_;
   cbor::Value::ArrayValue vouched_subsets_;
   BundleVersion version_;
-
-  // 1 for the CBOR header byte. See the comment at the top of AddResponse().
-  int64_t current_responses_offset_ = 1;
+  int64_t current_responses_offset_ = 0;
 };
 
-}  // namespace test
 }  // namespace web_package
 
-#endif  // COMPONENTS_WEB_PACKAGE_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_
+#endif  // COMPONENTS_WEB_PACKAGE_WEB_BUNDLE_BUILDER_H_
diff --git a/components/web_package/web_bundle_builder_unittest.cc b/components/web_package/web_bundle_builder_unittest.cc
new file mode 100644
index 0000000..4f6eeb186
--- /dev/null
+++ b/components/web_package/web_bundle_builder_unittest.cc
@@ -0,0 +1,86 @@
+// 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/web_package/web_bundle_builder.h"
+
+#include "base/big_endian.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace web_package {
+
+namespace {
+
+std::string kFallbackUrl = "https://test.example.org/";
+
+std::string GetTestFileContents(const base::FilePath& path) {
+  base::FilePath test_data_dir;
+  base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir);
+  test_data_dir = test_data_dir.Append(
+      base::FilePath(FILE_PATH_LITERAL("components/test/data/web_package")));
+
+  std::string contents;
+  EXPECT_TRUE(base::ReadFileToString(test_data_dir.Append(path), &contents));
+  return contents;
+}
+
+std::vector<uint8_t> GetStringAsBytes(base::StringPiece contents) {
+  auto bytes = base::as_bytes(base::make_span(contents));
+  return std::vector<uint8_t>(bytes.begin(), bytes.end());
+}
+
+}  // namespace
+
+class WebBundleBuilderTest : public testing::Test {
+ private:
+  base::test::TaskEnvironment task_environment_;
+};
+
+TEST_F(WebBundleBuilderTest, CorrectWebBundleSizeIsWritten) {
+  WebBundleBuilder builder(kFallbackUrl, "");
+  builder.AddExchange("https://test.example.com/",
+                      {{":status", "200"}, {"content-type", "text/plain"}},
+                      "payload");
+  std::vector<uint8_t> bundle = builder.CreateBundle();
+  char written_size[8];
+  memcpy(written_size, bundle.data() + bundle.size() - 8, 8);
+  uint64_t written_size_int;
+  base::ReadBigEndian(written_size, &written_size_int);
+  EXPECT_EQ(bundle.size(), written_size_int);
+}
+
+TEST_F(WebBundleBuilderTest, ByteByByteComparison) {
+  WebBundleBuilder builder(kFallbackUrl, "");
+  builder.AddExchange(
+      "https://test.example.org/",
+      {{":status", "200"}, {"content-type", "text/html; charset=UTF-8"}},
+      "<a href='index.html'>click for web bundles</a>");
+  builder.AddExchange(
+      "https://test.example.org/index.html",
+      {{":status", "200"}, {"content-type", "text/html; charset=UTF-8"}},
+      "<p>Hello Web Bundles!</p>");
+  std::vector<uint8_t> bundle = builder.CreateBundle();
+  std::vector<uint8_t> expected_bundle = GetStringAsBytes(
+      GetTestFileContents(base::FilePath(FILE_PATH_LITERAL("simple.wbn"))));
+  EXPECT_EQ(bundle, expected_bundle);
+}
+
+TEST_F(WebBundleBuilderTest, MoreThan23ResponsesInABundle) {
+  WebBundleBuilder builder("", "", BundleVersion::kB2);
+  for (int i = 0; i < 24; ++i) {
+    builder.AddExchange("https://test.example.org/" + base::NumberToString(i),
+                        {{":status", "200"}, {"content-type", "text/html;"}},
+                        "<p>Hello Web Bundles!</p>");
+  }
+  std::vector<uint8_t> bundle = builder.CreateBundle();
+  std::vector<uint8_t> expected_bundle = GetStringAsBytes(GetTestFileContents(
+      base::FilePath(FILE_PATH_LITERAL("24_responses.wbn"))));
+  EXPECT_EQ(bundle, expected_bundle);
+}
+
+}  // namespace web_package
diff --git a/components/web_package/web_bundle_parser_unittest.cc b/components/web_package/web_bundle_parser_unittest.cc
index fa2ebd4..35ebbc5 100644
--- a/components/web_package/web_bundle_parser_unittest.cc
+++ b/components/web_package/web_bundle_parser_unittest.cc
@@ -10,7 +10,7 @@
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "components/cbor/writer.h"
-#include "components/web_package/test_support/web_bundle_builder.h"
+#include "components/web_package/web_bundle_builder.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -162,7 +162,7 @@
 };
 
 TEST_F(WebBundleParserTest, WrongMagic) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   std::vector<uint8_t> bundle = builder.CreateBundle();
   bundle[3] ^= 1;
   TestDataSource data_source(bundle);
@@ -174,7 +174,7 @@
 }
 
 TEST_F(WebBundleParserTest, UnknownVersion) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   std::vector<uint8_t> bundle = builder.CreateBundle();
   // Modify the version string from "b1\0\0" to "q1\0\0".
   ASSERT_EQ(bundle[11], 'b');
@@ -187,7 +187,8 @@
 }
 
 TEST_F(WebBundleParserTest, FallbackURLIsNotUTF8) {
-  test::WebBundleBuilder builder("https://test.example.com/\xcc", kManifestUrl);
+  WebBundleBuilder builder("https://test.example.com/\xcc", kManifestUrl,
+                           BundleVersion::kB1, true);
   std::vector<uint8_t> bundle = builder.CreateBundle();
   TestDataSource data_source(bundle);
 
@@ -198,8 +199,7 @@
 }
 
 TEST_F(WebBundleParserTest, FallbackURLHasFragment) {
-  test::WebBundleBuilder builder("https://test.example.com/#fragment",
-                                 kManifestUrl);
+  WebBundleBuilder builder("https://test.example.com/#fragment", kManifestUrl);
   std::vector<uint8_t> bundle = builder.CreateBundle();
   TestDataSource data_source(bundle);
 
@@ -210,7 +210,7 @@
 }
 
 TEST_F(WebBundleParserTest, SectionLengthsTooLarge) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   std::string too_long_section_name(8192, 'x');
   builder.AddSection(too_long_section_name, cbor::Value(0));
   TestDataSource data_source(builder.CreateBundle());
@@ -219,7 +219,7 @@
 }
 
 TEST_F(WebBundleParserTest, DuplicateSectionName) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddSection("foo", cbor::Value(0));
   builder.AddSection("foo", cbor::Value(0));
   TestDataSource data_source(builder.CreateBundle());
@@ -228,7 +228,7 @@
 }
 
 TEST_F(WebBundleParserTest, SingleEntry) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -248,7 +248,7 @@
 }
 
 TEST_F(WebBundleParserTest, InvalidRequestURL) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("", {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
   TestDataSource data_source(builder.CreateBundle());
@@ -257,7 +257,8 @@
 }
 
 TEST_F(WebBundleParserTest, RequestURLIsNotUTF8) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl, BundleVersion::kB1,
+                           true);
   builder.AddExchange("https://test.example.com/\xcc",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -267,7 +268,7 @@
 }
 
 TEST_F(WebBundleParserTest, RequestURLHasBadScheme) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("file:///tmp/foo",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -277,7 +278,7 @@
 }
 
 TEST_F(WebBundleParserTest, RequestURLHasCredentials) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://user:passwd@test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -287,7 +288,7 @@
 }
 
 TEST_F(WebBundleParserTest, RequestURLHasFragment) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/#fragment",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -298,7 +299,7 @@
 
 TEST_F(WebBundleParserTest, RequestURLIsValidUrnUuid) {
   const char urn_uuid[] = "urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6";
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange(urn_uuid,
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -313,7 +314,7 @@
 
 TEST_F(WebBundleParserTest, RequestURLIsInvalidUrnUuid) {
   const char urn_uuid[] = "urn:uuid:invalid";
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange(urn_uuid,
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -323,7 +324,7 @@
 }
 
 TEST_F(WebBundleParserTest, NoStatusInResponseHeaders) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{"content-type", "text/plain"}},
                       "payload");  // ":status" is missing.
@@ -337,7 +338,7 @@
 }
 
 TEST_F(WebBundleParserTest, InvalidResponseStatus) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "0200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -351,7 +352,7 @@
 }
 
 TEST_F(WebBundleParserTest, ExtraPseudoInResponseHeaders) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange(
       "https://test.example.com/",
       {{":status", "200"}, {":foo", ""}, {"content-type", "text/plain"}},
@@ -366,7 +367,7 @@
 }
 
 TEST_F(WebBundleParserTest, UpperCaseCharacterInHeaderName) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"Content-Type", "text/plain"}},
                       "payload");
@@ -380,7 +381,7 @@
 }
 
 TEST_F(WebBundleParserTest, InvalidHeaderValue) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "\n"}}, "payload");
   TestDataSource data_source(builder.CreateBundle());
@@ -393,7 +394,7 @@
 }
 
 TEST_F(WebBundleParserTest, NoContentTypeWithNonEmptyContent) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/", {{":status", "200"}},
                       "payload");
   TestDataSource data_source(builder.CreateBundle());
@@ -406,7 +407,7 @@
 }
 
 TEST_F(WebBundleParserTest, NoContentTypeWithEmptyContent) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/", {{":status", "301"}}, "");
   TestDataSource data_source(builder.CreateBundle());
 
@@ -418,7 +419,7 @@
 }
 
 TEST_F(WebBundleParserTest, Variants) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   auto location1 = builder.AddResponse(
       {{":status", "200"}, {"content-type", "text/html"}}, "payload1");
   auto location2 = builder.AddResponse(
@@ -447,7 +448,7 @@
 }
 
 TEST_F(WebBundleParserTest, EmptyIndexEntry) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddIndexEntry("https://test.example.com/", "", {});
   TestDataSource data_source(builder.CreateBundle());
 
@@ -455,7 +456,7 @@
 }
 
 TEST_F(WebBundleParserTest, EmptyIndexEntryWithVariants) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddIndexEntry("https://test.example.com/",
                         "Accept;text/html;text/plain", {});
   TestDataSource data_source(builder.CreateBundle());
@@ -464,7 +465,7 @@
 }
 
 TEST_F(WebBundleParserTest, MultipleResponsesWithoutVariantsValue) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   auto location1 = builder.AddResponse(
       {{":status", "200"}, {"content-type", "text/html"}}, "payload1");
   auto location2 = builder.AddResponse(
@@ -477,7 +478,7 @@
 }
 
 TEST_F(WebBundleParserTest, AllKnownSectionInCritical) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -494,7 +495,7 @@
 }
 
 TEST_F(WebBundleParserTest, UnknownSectionInCritical) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -507,7 +508,7 @@
 }
 
 TEST_F(WebBundleParserTest, NoManifest) {
-  test::WebBundleBuilder builder(kFallbackUrl, std::string());
+  WebBundleBuilder builder(kFallbackUrl, std::string());
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -518,7 +519,7 @@
 }
 
 TEST_F(WebBundleParserTest, InvalidManifestURL) {
-  test::WebBundleBuilder builder(kFallbackUrl, "not-an-absolute-url");
+  WebBundleBuilder builder(kFallbackUrl, "not-an-absolute-url");
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -528,11 +529,11 @@
 }
 
 TEST_F(WebBundleParserTest, EmptySignaturesSection) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
-  // test::WebBundleBuilder omits signatures section if empty, so create it
+  // WebBundleBuilder omits signatures section if empty, so create it
   // ourselves.
   cbor::Value::ArrayValue signatures_section;
   signatures_section.emplace_back(cbor::Value::ArrayValue());  // authorities
@@ -548,7 +549,7 @@
 }
 
 TEST_F(WebBundleParserTest, SignaturesSection) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -607,7 +608,7 @@
 }
 
 TEST_F(WebBundleParserTest, MultipleSignatures) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -757,8 +758,7 @@
 }
 
 TEST_F(WebBundleParserTest, B2BundleSingleEntry) {
-  test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl,
-                                 BundleVersion::kB2);
+  WebBundleBuilder builder(kFallbackUrl, kManifestUrl, BundleVersion::kB2);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -779,7 +779,7 @@
 }
 
 TEST_F(WebBundleParserTest, B2BundleNoPrimaryUrlSingleEntry) {
-  test::WebBundleBuilder builder("", kManifestUrl, BundleVersion::kB2);
+  WebBundleBuilder builder("", kManifestUrl, BundleVersion::kB2);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -803,8 +803,8 @@
   constexpr BundleVersion kVersions[] = {BundleVersion::kB1,
                                          BundleVersion::kB2};
   for (const auto& version : kVersions) {
-    test::WebBundleBuilder builder("path/to/primary_url", "path/to/manifest",
-                                   version);
+    WebBundleBuilder builder("path/to/primary_url", "path/to/manifest",
+                             version);
     builder.AddExchange("path/to/file.txt",
                         {{":status", "200"}, {"content-type", "text/plain"}},
                         "payload");
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index efea3b7c..656f9ee 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1737,7 +1737,6 @@
     "screenlock_monitor/screenlock_monitor_source.cc",
     "screenlock_monitor/screenlock_monitor_source.h",
     "service_process_host_impl.cc",
-    "service_sandbox_type.h",
     "service_worker/embedded_worker_instance.cc",
     "service_worker/embedded_worker_instance.h",
     "service_worker/embedded_worker_status.h",
diff --git a/content/browser/OWNERS b/content/browser/OWNERS
index 2dcab7c8..a273dc6 100644
--- a/content/browser/OWNERS
+++ b/content/browser/OWNERS
@@ -33,9 +33,6 @@
 per-file sandbox_ipc_linux.*=file://sandbox/linux/OWNERS
 per-file utility_process_sandbox_browsertest.cc=file://sandbox/linux/OWNERS
 
-# Service sandbox mappings require security review
-per-file service_sandbox_type.h=file://ipc/SECURITY_OWNERS
-
 # Utility sandbox delegate requires security review.
 per-file utility_sandbox_delegate.*=set noparent
 per-file utility_sandbox_delegate.*=file://ipc/SECURITY_OWNERS
diff --git a/content/browser/audio/audio_service.cc b/content/browser/audio/audio_service.cc
index cc211f8..6d263c0 100644
--- a/content/browser/audio/audio_service.cc
+++ b/content/browser/audio/audio_service.cc
@@ -12,7 +12,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/browser/browser_main_loop.h"
-#include "content/browser/service_sandbox_type.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -24,6 +23,7 @@
 #include "media/base/media_switches.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/audio/public/cpp/audio_system_to_service_adapter.h"
+#include "services/audio/public/mojom/audio_service.mojom.h"
 #include "services/audio/service.h"
 #include "services/audio/service_factory.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc
index 48d85fd..1923ddf 100644
--- a/content/browser/network_service_instance_impl.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -31,7 +31,6 @@
 #include "build/chromeos_buildflags.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/network_service_client.h"
-#include "content/browser/service_sandbox_type.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index ccf6ba9..5f60eb9 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -2176,13 +2176,10 @@
   FrameTreeNode* root = contents()->GetFrameTree()->root();
   EXPECT_EQ(url1, root->current_frame_host()->GetLastCommittedURL());
   EXPECT_EQ(url1, root->current_frame_host()->last_document_url_in_renderer());
-  EXPECT_EQ(url1, root->current_frame_host()->GetLastLoadingURLInRenderer());
   FrameTreeNode* iframe = root->child_at(0);
   EXPECT_EQ(iframe_url, iframe->current_frame_host()->GetLastCommittedURL());
   EXPECT_EQ(iframe_url,
             iframe->current_frame_host()->last_document_url_in_renderer());
-  EXPECT_EQ(iframe_url,
-            iframe->current_frame_host()->GetLastLoadingURLInRenderer());
 
   // 2) Do a document.open() on the iframe from the main frame.
   EXPECT_TRUE(ExecJs(
@@ -2195,7 +2192,6 @@
   EXPECT_EQ(iframe_url, iframe->current_frame_host()->GetLastCommittedURL());
   EXPECT_EQ(url1,
             iframe->current_frame_host()->last_document_url_in_renderer());
-  EXPECT_EQ(url1, iframe->current_frame_host()->GetLastLoadingURLInRenderer());
 
   // 3) Do a same-document navigation to `url_1_fragment` on the iframe (Note
   // that this is a same-document navigation because the iframe's document URL
@@ -2214,8 +2210,6 @@
             iframe->current_frame_host()->GetLastCommittedURL());
   EXPECT_EQ(url_1_fragment,
             iframe->current_frame_host()->last_document_url_in_renderer());
-  EXPECT_EQ(url_1_fragment,
-            iframe->current_frame_host()->GetLastLoadingURLInRenderer());
 
   // 4) Do a navigation to `url404`, which wil result in an error page.
   // The document URL will be set to the kUnreachableWebDataURL.
@@ -2224,7 +2218,6 @@
   EXPECT_EQ(url404, root->current_frame_host()->GetLastCommittedURL());
   EXPECT_EQ(GURL(kUnreachableWebDataURL),
             root->current_frame_host()->last_document_url_in_renderer());
-  EXPECT_EQ(url404, root->current_frame_host()->GetLastLoadingURLInRenderer());
 
   // 5) Do a same-document pushState on an error page without changing the URL
   // (otherwise it will result in an origin mismatch error). The URLs will stay
@@ -2239,7 +2232,6 @@
   EXPECT_EQ(url404, root->current_frame_host()->GetLastCommittedURL());
   EXPECT_EQ(GURL(kUnreachableWebDataURL),
             root->current_frame_host()->last_document_url_in_renderer());
-  EXPECT_EQ(url404, root->current_frame_host()->GetLastLoadingURLInRenderer());
 }
 
 // Tests various cases of replacements caused by error pages.
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index db136fe..40ff686f 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -891,6 +891,32 @@
   return parent_anonymous || frame->anonymous();
 }
 
+// Returns the "loading" URL in the renderer. This tries to replicate
+// RenderFrameImpl::GetLoadingUrl(). This might return a different URL from
+// what we get when calling GetLastCommittedURL() on `rfh`, in case the
+// document had changed its URL through document.open() before, or
+// when calling last_document_url_in_renderer(), in case of error pages and
+// loadDataWithBaseURL documents.
+// This function should only be used to preserve calculations that were
+// previously done in the renderer but got moved to the browser (e.g. URL
+// comparisons to determine if a navigation should do a replacement or not).
+const GURL& GetLastLoadingURLInRendererForNavigationReplacement(
+    RenderFrameHostImpl* rfh) {
+  // Handle some special cases:
+  // - The "loading URL" for an error page commit is the URL that it failed to
+  // load. This will be retained as long as the document stays the same.
+  // - For loadDataWithBaseURL() navigations the "loading URL" will be the
+  // last committed URL (the data: URL). This will also be retained as long as
+  // the document stays the same.
+  if (rfh->IsErrorDocument() ||
+      rfh->was_loaded_from_load_data_with_base_url()) {
+    return rfh->GetLastCommittedURL();
+  }
+
+  // Otherwise, return the last document URL.
+  return rfh->last_document_url_in_renderer();
+}
+
 }  // namespace
 
 NavigationRequest::PrerenderActivationNavigationState::
@@ -3729,7 +3755,29 @@
       // soon be restarted as a normal history navigation.
       return;
     }
-    CHECK(rfh_restored_from_back_forward_cache_);
+    if (!rfh_restored_from_back_forward_cache_ ||
+        rfh_restored_from_back_forward_cache_
+            ->is_evicted_from_back_forward_cache()) {
+      // We might also get here when the navigation is not marked as being
+      // restarted yet, but the RFH being restored is gone or is already marked
+      // as evicted. Do not continue the navigation, and trigger uploading of
+      // debug information to understand what led to this case, since it's still
+      // unknown.
+      // See https://crbug.com/1258523.
+      SCOPED_CRASH_KEY_BOOL("NoRestoredRFH", "rfh_exists",
+                            !!rfh_restored_from_back_forward_cache_);
+      SCOPED_CRASH_KEY_BOOL("NoRestoredRFH", "is_main_frame",
+                            frame_tree_node_->IsMainFrame());
+      SCOPED_CRASH_KEY_BOOL("NoRestoredRFH", "is_ftn_nav_req",
+                            (frame_tree_node_->navigation_request() == this));
+      BackForwardCacheImpl& back_forward_cache =
+          frame_tree_node_->frame_tree()->controller().GetBackForwardCache();
+      SCOPED_CRASH_KEY_NUMBER("NoRestoredRFH", "bfcache_entries_size",
+                              back_forward_cache.GetEntries().size());
+      CaptureTraceForNavigationDebugScenario(
+          DebugScenario::kDebugNoRestoredRFHOnNonRestartedNavigation);
+      return;
+    }
     loader_type = NavigationURLLoader::LoaderType::kNoopForBackForwardCache;
     cached_response_head =
         rfh_restored_from_back_forward_cache_->last_response_head()->Clone();
@@ -6845,15 +6893,15 @@
 // https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigating-across-documents:hh-replace
 bool NavigationRequest::ShouldReplaceCurrentEntryForSameUrlNavigation() const {
   DCHECK_LE(state_, WILL_START_NAVIGATION);
-  // Not a same-url navigation. Note that this is comparing against the last
-  // history URL since this is what was used in the renderer check that was
+  // Not a same-url navigation. Note that this is comparing against the "last
+  // loading URL" since this is what was used in the renderer check that was
   // moved here. This means for error pages we should compare against the URL
   // that failed to load (the last committed URL), while for other navigations
   // we should compare against the last document URL, which might be different
-  // from the last committed URL due to document.open() changing the URL. To
-  // handle that, we compare against the "loading URL".
+  // from the last committed URL due to document.open() changing the URL.
   if (common_params_->url !=
-      frame_tree_node_->current_frame_host()->GetLastLoadingURLInRenderer()) {
+      GetLastLoadingURLInRendererForNavigationReplacement(
+          frame_tree_node_->current_frame_host())) {
     return false;
   }
 
@@ -6966,8 +7014,9 @@
   // TODO(https://crbug.com/1188956): Reconsider whether these two cases should
   // do replacement or not, since we're just preserving old behavior here.
   return is_reload_or_history ||
-         (common_params_->url == frame_tree_node_->current_frame_host()
-                                     ->GetLastLoadingURLInRenderer());
+         (common_params_->url ==
+          GetLastLoadingURLInRendererForNavigationReplacement(
+              frame_tree_node_->current_frame_host()));
 }
 
 void NavigationRequest::RenderFallbackContentForObjectTag() {
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 199b2b1..754d634 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -968,7 +968,7 @@
     // failed (which is already accounted for in the error page case above).
     return request->common_params().base_url_for_data_url;
   }
-  if (renderer_url_info.is_loaded_from_load_data_with_base_url &&
+  if (renderer_url_info.was_loaded_from_load_data_with_base_url &&
       request->IsSameDocument()) {
     // If this is a same-document navigation on a document loaded from
     // loadDataWithBaseURL(), it is not currently possible to figure out the
@@ -2070,21 +2070,6 @@
   return last_committed_origin_;
 }
 
-const GURL& RenderFrameHostImpl::GetLastLoadingURLInRenderer() const {
-  // Handle some special cases:
-  // - The "loading URL" for an error page commit is the URL that it failed to
-  // load. This will be retained as long as the document stays the same.
-  // - For loadDataWithBaseURL() navigations the "loading URL" will be the
-  // last committed URL (the data: URL). This will also be retained as long as
-  // the document stays the same.
-  if (is_error_page_ ||
-      renderer_url_info_.is_loaded_from_load_data_with_base_url) {
-    return last_committed_url_;
-  }
-  // Otherwise, return the last document URL.
-  return renderer_url_info_.last_document_url;
-}
-
 const net::NetworkIsolationKey& RenderFrameHostImpl::GetNetworkIsolationKey() {
   DCHECK(!isolation_info_.IsEmpty());
   return isolation_info_.network_isolation_key();
@@ -10006,13 +9991,13 @@
   // checks. We should also allow same-document navigations within pages loaded
   // with loadDataWithBaseURL. Since renderer-initiated same-document
   // navigations won't have a NavigationRequest at this point, we need to check
-  // |renderer_url_info_.is_loaded_from_load_data_with_base_url|.
+  // |renderer_url_info_.was_loaded_from_load_data_with_base_url|.
   DCHECK(navigation_request || is_same_document_navigation ||
          !frame_tree_node_->has_committed_real_load());
   bool bypass_checks_for_webview = false;
   if ((navigation_request && navigation_request->IsLoadDataWithBaseURL()) ||
       (is_same_document_navigation &&
-       renderer_url_info_.is_loaded_from_load_data_with_base_url)) {
+       renderer_url_info_.was_loaded_from_load_data_with_base_url)) {
     // Allow bypass if the process isn't locked. Otherwise run normal checks.
     bypass_checks_for_webview = !ChildProcessSecurityPolicyImpl::GetInstance()
                                      ->GetProcessLock(process->GetID())
@@ -10568,10 +10553,10 @@
       navigation_request->is_overriding_user_agent() && is_main_frame();
 
   // Mark whether then navigation was intended as a loadDataWithBaseURL or not.
-  // If |renderer_url_info_.is_loaded_from_load_data_with_base_url| is true, we
+  // If |renderer_url_info_.was_loaded_from_load_data_with_base_url| is true, we
   // will bypass checks in VerifyDidCommitParams for same-document navigations
   // in the loaded document.
-  renderer_url_info_.is_loaded_from_load_data_with_base_url =
+  renderer_url_info_.was_loaded_from_load_data_with_base_url =
       navigation_request->IsLoadDataWithBaseURL();
 
   // If we still have a PeakGpuMemoryTracker, then the loading it was observing
@@ -11421,9 +11406,7 @@
 
 // Calculates the "loading" URL for a given navigation. This tries to replicate
 // RenderFrameImpl::GetLoadingUrl() and is used to predict the value of "url" in
-// DidCommitProvisionalLoadParams. Note that this is a bit different from
-// GetLastDocumentLoadingURL(), which predicts the loading URL of an
-// already-committed document for URL comparison purposes.
+// DidCommitProvisionalLoadParams.
 GURL CalculateLoadingURL(
     NavigationRequest* request,
     const mojom::DidCommitProvisionalLoadParams& params,
@@ -11449,7 +11432,7 @@
 
   if (request->IsSameDocument() &&
       (last_document_is_error_page ||
-       last_renderer_url_info.is_loaded_from_load_data_with_base_url)) {
+       last_renderer_url_info.was_loaded_from_load_data_with_base_url)) {
     // Documents that have an "override" URL (loadDataWithBaseURL navigations,
     // error pages) will continue using that URL even after same-document
     // navigations.
@@ -11625,7 +11608,7 @@
 
   SCOPED_CRASH_KEY_BOOL(
       "VerifyDidCommit", "prev_ldwb",
-      renderer_url_info_.is_loaded_from_load_data_with_base_url);
+      renderer_url_info_.was_loaded_from_load_data_with_base_url);
   SCOPED_CRASH_KEY_STRING32(
       "VerifyDidCommit", "base_url_fdu_type",
       GetURLTypeForCrashKey(request->common_params().base_url_for_data_url));
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index a1a391e..4f3b88b 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -639,16 +639,11 @@
     return renderer_url_info_.last_document_url;
   }
 
-  // Returns the "loading" URL in the renderer. This tries to replicate
-  // RenderFrameImpl::GetLoadingUrl(). This might return a different URL from
-  // GetLastCommittedURL() in case the document had changed its URL through
-  // document.open() before, and last_document_url_in_renderer() in case of
-  // error pages and loadDataWithBaseURL documents. See comments in the
-  // implementation for details.
-  // This function should only be used to preserve calculations that were
-  // previously done in the renderer but got moved to the browser (e.g. URL
-  // comparisons to determine if a navigation should do a replacement or not).
-  const GURL& GetLastLoadingURLInRenderer() const;
+  // Whether the last committed document was loaded from loadDataWithBaseURL or
+  // not.
+  bool was_loaded_from_load_data_with_base_url() const {
+    return renderer_url_info_.was_loaded_from_load_data_with_base_url;
+  }
 
   // Saves the URLs and other URL-related information used in the renderer.
   // These values can be used to know the current state of URLs in the renderer.
@@ -676,7 +671,7 @@
 
     // Whether the currently committed document is a result of webview's
     // loadDataWithBaseURL API or not.
-    bool is_loaded_from_load_data_with_base_url = false;
+    bool was_loaded_from_load_data_with_base_url = false;
   };
 
   // Returns the storage key for the last committed document in this
diff --git a/content/browser/service_process_host_impl.cc b/content/browser/service_process_host_impl.cc
index 946af5b..65749a5 100644
--- a/content/browser/service_process_host_impl.cc
+++ b/content/browser/service_process_host_impl.cc
@@ -16,7 +16,9 @@
 #include "content/common/child_process.mojom.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/service_process_host.h"
+#include "content/public/common/content_client.h"
 #include "mojo/public/cpp/bindings/generic_pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
@@ -24,6 +26,15 @@
 
 namespace {
 
+// Changes to this function should be reviewed by a security person.
+bool ShouldEnableSandbox(sandbox::mojom::Sandbox sandbox) {
+  if (sandbox == sandbox::mojom::Sandbox::kAudio)
+    return GetContentClient()->browser()->ShouldSandboxAudioService();
+  if (sandbox == sandbox::mojom::Sandbox::kNetwork)
+    return GetContentClient()->browser()->ShouldSandboxNetworkService();
+  return true;
+}
+
 // Internal helper to track running service processes.
 class ServiceProcessTracker {
  public:
@@ -162,14 +173,17 @@
 // TODO(crbug.com/977637): Once UtilityProcessHost is used only by service
 // processes, its logic can be inlined here.
 void LaunchServiceProcess(mojo::GenericPendingReceiver receiver,
-                          ServiceProcessHost::Options options) {
+                          ServiceProcessHost::Options options,
+                          sandbox::mojom::Sandbox sandbox) {
   UtilityProcessHost* host = new UtilityProcessHost(
       std::make_unique<UtilityProcessClient>(*receiver.interface_name()));
   host->SetName(!options.display_name.empty()
                     ? options.display_name
                     : base::UTF8ToUTF16(*receiver.interface_name()));
   host->SetMetricsName(*receiver.interface_name());
-  host->SetSandboxType(options.sandbox_type);
+  if (!ShouldEnableSandbox(sandbox))
+    sandbox = sandbox::mojom::Sandbox::kNoSandbox;
+  host->SetSandboxType(sandbox::policy::MapToSandboxType(sandbox));
   host->SetExtraCommandLineSwitches(std::move(options.extra_switches));
   if (options.child_flags)
     host->set_child_flags(*options.child_flags);
@@ -196,14 +210,15 @@
 
 // static
 void ServiceProcessHost::Launch(mojo::GenericPendingReceiver receiver,
-                                Options options) {
+                                Options options,
+                                sandbox::mojom::Sandbox sandbox) {
   DCHECK(receiver.interface_name().has_value());
   if (GetUIThreadTaskRunner({})->BelongsToCurrentThread()) {
-    LaunchServiceProcess(std::move(receiver), std::move(options));
+    LaunchServiceProcess(std::move(receiver), std::move(options), sandbox);
   } else {
     GetUIThreadTaskRunner({})->PostTask(
         FROM_HERE, base::BindOnce(&LaunchServiceProcess, std::move(receiver),
-                                  std::move(options)));
+                                  std::move(options), sandbox));
   }
 }
 
diff --git a/content/browser/service_sandbox_type.h b/content/browser/service_sandbox_type.h
deleted file mode 100644
index 4498948..0000000
--- a/content/browser/service_sandbox_type.h
+++ /dev/null
@@ -1,45 +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 CONTENT_BROWSER_SERVICE_SANDBOX_TYPE_H_
-#define CONTENT_BROWSER_SERVICE_SANDBOX_TYPE_H_
-
-#include "content/browser/network_service_instance_impl.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/service_process_host.h"
-#include "content/public/common/content_client.h"
-#include "sandbox/policy/sandbox_type.h"
-
-// This file maps service classes to sandbox types. See
-// ServiceProcessHost::Launch() for how these templates are consumed.
-
-// audio::mojom::AudioService
-namespace audio {
-namespace mojom {
-class AudioService;
-}
-}  // namespace audio
-template <>
-inline sandbox::policy::SandboxType
-content::GetServiceSandboxType<audio::mojom::AudioService>() {
-  return GetContentClient()->browser()->ShouldSandboxAudioService()
-             ? sandbox::policy::SandboxType::kAudio
-             : sandbox::policy::SandboxType::kNoSandbox;
-}
-
-// network::mojom::NetworkService
-namespace network {
-namespace mojom {
-class NetworkService;
-}
-}  // namespace network
-template <>
-inline sandbox::policy::SandboxType
-content::GetServiceSandboxType<network::mojom::NetworkService>() {
-  return GetContentClient()->browser()->ShouldSandboxNetworkService()
-             ? sandbox::policy::SandboxType::kNetwork
-             : sandbox::policy::SandboxType::kNoSandbox;
-}
-
-#endif  // CONTENT_BROWSER_SERVICE_SANDBOX_TYPE_H_
diff --git a/content/browser/web_package/link_web_bundle_browsertest.cc b/content/browser/web_package/link_web_bundle_browsertest.cc
index e039da03..8fc0e61 100644
--- a/content/browser/web_package/link_web_bundle_browsertest.cc
+++ b/content/browser/web_package/link_web_bundle_browsertest.cc
@@ -10,7 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
-#include "components/web_package/test_support/web_bundle_builder.h"
+#include "components/web_package/web_bundle_builder.h"
 #include "components/web_package/web_bundle_utils.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/content_browser_client.h"
@@ -176,8 +176,8 @@
           request.relative_url == "/web_bundle/huge2.wbn"))
       return nullptr;
     GURL primary_url(https_server_.GetURL("/web_bundle/huge.txt"));
-    web_package::test::WebBundleBuilder builder(primary_url.spec(),
-                                                "" /* manifest_url */);
+    web_package::WebBundleBuilder builder(primary_url.spec(),
+                                          "" /* manifest_url */);
     builder.AddExchange(
         primary_url.spec(),
         {{":status", "200"}, {"content-type", "text/plain"}},
diff --git a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
index a4f3984f..9668e98 100644
--- a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
@@ -585,8 +585,9 @@
       UsePrefetch() ? 2 : 1);
 }
 
-// https://crbug.com/966820. Fails pretty often on Android. Fails flakily
-// on all platforms with Synchronous HTML Parsing enabled.
+// TODO(crbug.com/966820): Fails pretty often on Android.
+// TODO(crbug.com/1258886): Fails flakily on all platforms with Synchronous HTML
+// Parsing enabled.
 IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest,
                        DISABLED_BadMICE) {
   InstallMockCertChainInterceptor();
diff --git a/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc b/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
index 0451ccbac..a8da74e 100644
--- a/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_subresource_prefetch_browsertest.cc
@@ -1343,6 +1343,8 @@
       1 /* expected_script_fetch_count */);
 }
 
+// TODO(crbug.com/1258886): Fails flakily on all platforms with Synchronous HTML
+// Parsing enabled.
 IN_PROC_BROWSER_TEST_F(SignedExchangeSubresourcePrefetchBrowserTest,
                        DISABLED_ImageSrcsetAndSizes) {
   const char* prefetch_path = "/prefetch.html";
diff --git a/content/browser/web_package/web_bundle_browsertest_base.cc b/content/browser/web_package/web_bundle_browsertest_base.cc
index 2a8318e0..a25f3d1 100644
--- a/content/browser/web_package/web_bundle_browsertest_base.cc
+++ b/content/browser/web_package/web_bundle_browsertest_base.cc
@@ -362,7 +362,7 @@
 }
 
 std::string CreateSimpleWebBundle(const GURL& primary_url) {
-  web_package::test::WebBundleBuilder builder(primary_url.spec(), "");
+  web_package::WebBundleBuilder builder(primary_url.spec(), "");
   builder.AddExchange(primary_url.spec(),
                       {{":status", "200"}, {"content-type", "text/html"}},
                       "<title>Ready</title>");
@@ -370,7 +370,7 @@
   return std::string(bundle.begin(), bundle.end());
 }
 
-void AddHtmlFile(web_package::test::WebBundleBuilder* builder,
+void AddHtmlFile(web_package::WebBundleBuilder* builder,
                  const GURL& base_url,
                  const std::string& path,
                  const std::string& content) {
@@ -379,7 +379,7 @@
                        content);
 }
 
-void AddScriptFile(web_package::test::WebBundleBuilder* builder,
+void AddScriptFile(web_package::WebBundleBuilder* builder,
                    const GURL& base_url,
                    const std::string& path,
                    const std::string& content) {
@@ -391,7 +391,7 @@
 
 std::string CreatePathTestWebBundle(const GURL& base_url) {
   const std::string primary_url_path = "/web_bundle/path_test/in_scope/";
-  web_package::test::WebBundleBuilder builder(
+  web_package::WebBundleBuilder builder(
       base_url.Resolve(primary_url_path).spec(), "");
   AddHtmlFile(&builder, base_url, primary_url_path, "<title>Ready</title>");
   AddHtmlFile(
@@ -470,7 +470,7 @@
   *primary_url_origin = primary_server->GetURL("/");
   *third_party_origin = third_party_server->GetURL("/");
 
-  web_package::test::WebBundleBuilder builder(
+  web_package::WebBundleBuilder builder(
       primary_url_origin->Resolve("/top").spec(), "");
   AddHtmlFile(&builder, *primary_url_origin, "/top", R"(
     <script>
@@ -588,11 +588,10 @@
                             script_info.c_str());
 }
 
-void AddHtmlAndScriptForNavigationTest(
-    web_package::test::WebBundleBuilder* builder,
-    const GURL& base_url,
-    const std::string& path,
-    const std::string& additional_html) {
+void AddHtmlAndScriptForNavigationTest(web_package::WebBundleBuilder* builder,
+                                       const GURL& base_url,
+                                       const std::string& path,
+                                       const std::string& additional_html) {
   AddHtmlFile(builder, base_url, path,
               CreateHtmlForNavigationTest(path + " from wbn", additional_html));
   AddScriptFile(builder, base_url, path + "script",
@@ -684,7 +683,7 @@
                                 GURL* url_origin,
                                 std::string* web_bundle_content) {
   SetUpNavigationTestServer(server, url_origin);
-  web_package::test::WebBundleBuilder builder(
+  web_package::WebBundleBuilder builder(
       url_origin->Resolve("/top-page/").spec(), "");
   for (const auto& path : pathes)
     AddHtmlAndScriptForNavigationTest(&builder, *url_origin, path, "");
@@ -1052,7 +1051,7 @@
                                GURL* url_origin,
                                std::string* web_bundle_content) {
   SetUpNavigationTestServer(server, url_origin);
-  web_package::test::WebBundleBuilder builder(
+  web_package::WebBundleBuilder builder(
       url_origin->Resolve("/top-page/").spec(), "");
   const std::vector<std::string> pathes = {"/top-page/", "/1-page/",
                                            "/2-page/"};
diff --git a/content/browser/web_package/web_bundle_browsertest_base.h b/content/browser/web_package/web_bundle_browsertest_base.h
index d6b09ee4..77692897 100644
--- a/content/browser/web_package/web_bundle_browsertest_base.h
+++ b/content/browser/web_package/web_bundle_browsertest_base.h
@@ -9,7 +9,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_piece.h"
 #include "build/build_config.h"
-#include "components/web_package/test_support/web_bundle_builder.h"
+#include "components/web_package/web_bundle_builder.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_context.h"
@@ -242,12 +242,12 @@
 
 std::string CreateSimpleWebBundle(const GURL& primary_url);
 
-void AddHtmlFile(web_package::test::WebBundleBuilder* builder,
+void AddHtmlFile(web_package::WebBundleBuilder* builder,
                  const GURL& base_url,
                  const std::string& path,
                  const std::string& content);
 
-void AddScriptFile(web_package::test::WebBundleBuilder* builder,
+void AddScriptFile(web_package::WebBundleBuilder* builder,
                    const GURL& base_url,
                    const std::string& path,
                    const std::string& content);
@@ -313,11 +313,10 @@
 
 std::string CreateScriptForNavigationTest(const std::string& script_info);
 
-void AddHtmlAndScriptForNavigationTest(
-    web_package::test::WebBundleBuilder* builder,
-    const GURL& base_url,
-    const std::string& path,
-    const std::string& additional_html);
+void AddHtmlAndScriptForNavigationTest(web_package::WebBundleBuilder* builder,
+                                       const GURL& base_url,
+                                       const std::string& path,
+                                       const std::string& additional_html);
 
 std::string GetLoadResultForNavigationTest(const ToRenderFrameHost& adapter);
 
diff --git a/content/browser/web_package/web_bundle_network_browsertest.cc b/content/browser/web_package/web_bundle_network_browsertest.cc
index 0aaf218d..2336fa4 100644
--- a/content/browser/web_package/web_bundle_network_browsertest.cc
+++ b/content/browser/web_package/web_bundle_network_browsertest.cc
@@ -134,7 +134,7 @@
   const GURL script_url =
       embedded_test_server()->GetURL("/web_bundle/script.js");
 
-  web_package::test::WebBundleBuilder builder(primary_url.spec(), "");
+  web_package::WebBundleBuilder builder(primary_url.spec(), "");
   builder.AddExchange(primary_url.spec(),
                       {{":status", "200"}, {"content-type", "text/html"}},
                       "<script src=\"script.js\"></script>");
@@ -228,7 +228,7 @@
   const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
   const GURL inner_url =
       embedded_test_server()->GetURL("/web_bundle/inner.html");
-  web_package::test::WebBundleBuilder builder(primary_url.spec(), "");
+  web_package::WebBundleBuilder builder(primary_url.spec(), "");
   builder.AddExchange(inner_url.spec(),
                       {{":status", "200"}, {"content-type", "text/html"}},
                       "<title>Ready</title>");
@@ -695,8 +695,8 @@
       embedded_test_server(), "");
   ASSERT_TRUE(embedded_test_server()->Start());
   const GURL origin = embedded_test_server()->GetURL("/");
-  web_package::test::WebBundleBuilder builder(
-      origin.Resolve(primary_url_path).spec(), "");
+  web_package::WebBundleBuilder builder(origin.Resolve(primary_url_path).spec(),
+                                        "");
   web_bundle_browsertest_utils::AddHtmlFile(&builder, origin, primary_url_path,
                                             R"(
     <script>
diff --git a/content/common/debug_utils.h b/content/common/debug_utils.h
index 13d02428..1f724fd 100644
--- a/content/common/debug_utils.h
+++ b/content/common/debug_utils.h
@@ -55,9 +55,14 @@
   // RenderFrameProxyHost::SetFocusedFrame().
   kDebugNoRenderFrameProxyHostOnSetFocusedFrame = 9,
 
+  // The RenderFrameHost to be restored from the back/forward cache no longer
+  // exists for a navigation that is not marked as being restarted.
+  // See https://crbug.com/1258523.
+  kDebugNoRestoredRFHOnNonRestartedNavigation = 10,
+
   // After making changes, you MUST update the histograms xml by running:
   // "python tools/metrics/histograms/update_debug_scenarios.py"
-  kMaxValue = kDebugNoRenderFrameProxyHostOnSetFocusedFrame,
+  kMaxValue = kDebugNoRestoredRFHOnNonRestartedNavigation,
 };
 
 // The tracing categories enabled for debugging navigation scenarios can be
diff --git a/content/public/browser/service_process_host.h b/content/public/browser/service_process_host.h
index 11c2c6f..d4cab88 100644
--- a/content/public/browser/service_process_host.h
+++ b/content/public/browser/service_process_host.h
@@ -30,17 +30,16 @@
 
 // Sandbox type for ServiceProcessHost::Launch<remote>() is found by
 // template matching on |remote|. Consult security-dev@chromium.org and
-// add a [ServiceSandbox=type] mojom attribute, or an appropriate
-// |service_sandbox_type.h|.
+// add a [ServiceSandbox=type] mojom attribute.
 template <typename Interface>
-inline sandbox::policy::SandboxType GetServiceSandboxType() {
+inline sandbox::mojom::Sandbox GetServiceSandboxType() {
   using ProvidedSandboxType = decltype(Interface::kServiceSandbox);
   static_assert(
       std::is_same<ProvidedSandboxType, const sandbox::mojom::Sandbox>::value,
       "This interface does not declare a proper ServiceSandbox attribute. See "
       "//docs/mojo_and_services.md (Specifying a sandbox).");
 
-  return sandbox::policy::MapToSandboxType(Interface::kServiceSandbox);
+  return Interface::kServiceSandbox;
 }
 
 // ServiceProcessHost is used to launch new service processes given basic
@@ -87,8 +86,6 @@
     // to |Launch()|.
     Options Pass();
 
-    sandbox::policy::SandboxType sandbox_type =
-        sandbox::policy::SandboxType::kUtility;
     std::u16string display_name;
     absl::optional<int> child_flags;
     std::vector<std::string> extra_switches;
@@ -130,9 +127,8 @@
   template <typename Interface>
   static void Launch(mojo::PendingReceiver<Interface> receiver,
                      Options options = {}) {
-    options.sandbox_type = content::GetServiceSandboxType<Interface>();
     Launch(mojo::GenericPendingReceiver(std::move(receiver)),
-           std::move(options));
+           std::move(options), content::GetServiceSandboxType<Interface>());
   }
 
   // Same as above but creates a new |Interface| pipe on the caller's behalf and
@@ -141,9 +137,9 @@
   // May be called from any thread.
   template <typename Interface>
   static mojo::Remote<Interface> Launch(Options options = {}) {
-    options.sandbox_type = content::GetServiceSandboxType<Interface>();
     mojo::Remote<Interface> remote;
-    Launch(remote.BindNewPipeAndPassReceiver(), std::move(options));
+    Launch(remote.BindNewPipeAndPassReceiver(), std::move(options),
+           content::GetServiceSandboxType<Interface>());
     return remote;
   }
 
@@ -163,7 +159,9 @@
   // Launches a new service process and asks it to bind a receiver for the
   // service interface endpoint carried by |receiver|, which should be connected
   // to a Remote of the same interface type.
-  static void Launch(mojo::GenericPendingReceiver receiver, Options options);
+  static void Launch(mojo::GenericPendingReceiver receiver,
+                     Options options,
+                     sandbox::mojom::Sandbox sandbox);
 };
 
 // DEPRECATED. DO NOT USE THIS. This is a helper for any remaining service
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 5ca2f21..fac3e7446 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1400,7 +1400,6 @@
     "//components/viz/host",
     "//components/viz/test:test_support",
     "//components/web_package",
-    "//components/web_package:test_support",
     "//content/app:for_content_tests",
     "//content/browser:for_content_tests",
     "//content/browser/background_sync:background_sync_proto",
diff --git a/device/fido/cros/authenticator.cc b/device/fido/cros/authenticator.cc
index fdf86d49..21c4c52 100644
--- a/device/fido/cros/authenticator.cc
+++ b/device/fido/cros/authenticator.cc
@@ -24,8 +24,10 @@
 namespace device {
 
 ChromeOSAuthenticator::ChromeOSAuthenticator(
-    base::RepeatingCallback<uint32_t()> generate_request_id_callback)
+    base::RepeatingCallback<uint32_t()> generate_request_id_callback,
+    ChromeOSAuthenticator::Config config)
     : generate_request_id_callback_(std::move(generate_request_id_callback)),
+      config_(config),
       weak_factory_(this) {}
 
 ChromeOSAuthenticator::~ChromeOSAuthenticator() {}
@@ -73,10 +75,11 @@
     MakeCredentialOptions request_options,
     MakeCredentialCallback callback) {
   u2f::MakeCredentialRequest req;
-  // Requests with UserPresence get upgraded to UserVerification unless
-  // verification is explicitly discouraged.
+  // Requests will be UserPresence if uv is not available or discouraged,
+  // and be UserVerification otherwise.
   req.set_verification_type(
-      (request.user_verification == UserVerificationRequirement::kDiscouraged)
+      (request.user_verification == UserVerificationRequirement::kDiscouraged &&
+       config_.power_button_enabled)
           ? u2f::VERIFICATION_USER_PRESENCE
           : u2f::VERIFICATION_USER_VERIFICATION);
   req.set_rp_id(request.rp.id);
@@ -185,10 +188,11 @@
                                          CtapGetAssertionOptions options,
                                          GetAssertionCallback callback) {
   u2f::GetAssertionRequest req;
-  // Requests with UserPresence get upgraded to UserVerification unless
-  // verification is explicitly discouraged.
+  // Requests will be UserPresence if uv is not available or discouraged,
+  // and be UserVerification otherwise.
   req.set_verification_type(
-      (request.user_verification == UserVerificationRequirement::kDiscouraged)
+      (request.user_verification == UserVerificationRequirement::kDiscouraged &&
+       config_.power_button_enabled)
           ? u2f::VERIFICATION_USER_PRESENCE
           : u2f::VERIFICATION_USER_VERIFICATION);
   req.set_rp_id(request.rp_id);
diff --git a/device/fido/cros/authenticator.h b/device/fido/cros/authenticator.h
index ecc0743..2798583b 100644
--- a/device/fido/cros/authenticator.h
+++ b/device/fido/cros/authenticator.h
@@ -25,8 +25,17 @@
 class COMPONENT_EXPORT(DEVICE_FIDO) ChromeOSAuthenticator
     : public FidoAuthenticator {
  public:
-  explicit ChromeOSAuthenticator(
-      base::RepeatingCallback<uint32_t()> generate_request_id_callback);
+  struct COMPONENT_EXPORT(DEVICE_FIDO) Config {
+    // Whether uv platform authenticator is available (PIN or fingerprint
+    // enrolled).
+    bool uv_available;
+    // Whether using the power button for user presence checking is enabled.
+    bool power_button_enabled;
+  };
+
+  ChromeOSAuthenticator(
+      base::RepeatingCallback<uint32_t()> generate_request_id_callback,
+      Config config);
   ~ChromeOSAuthenticator() override;
 
   static void HasCredentialForGetAssertionRequest(
@@ -37,7 +46,7 @@
       const CtapGetAssertionRequest& request,
       base::OnceCallback<void(bool has_credential)> callback);
 
-  // Invokes |callback| with a bool indicating  whether the platform
+  // Invokes |callback| with a bool indicating whether the platform
   // authenticator is available, which is true if the current user has a PIN set
   // up or biometrics enrolled.
   static void IsUVPlatformAuthenticatorAvailable(
@@ -94,6 +103,7 @@
 
   // Callback to set request_id in the window property.
   base::RepeatingCallback<uint32_t()> generate_request_id_callback_;
+  const Config config_;
   base::WeakPtrFactory<ChromeOSAuthenticator> weak_factory_;
 };
 
diff --git a/device/fido/cros/discovery.cc b/device/fido/cros/discovery.cc
index c56704e..435388a2 100644
--- a/device/fido/cros/discovery.cc
+++ b/device/fido/cros/discovery.cc
@@ -43,13 +43,6 @@
     return;
   }
 
-  if (require_power_button_mode_) {
-    ChromeOSAuthenticator::IsPowerButtonModeEnabled(
-        base::BindOnce(&FidoChromeOSDiscovery::MaybeAddAuthenticator,
-                       weak_factory_.GetWeakPtr()));
-    return;
-  }
-
   if (get_assertion_request_) {
     ChromeOSAuthenticator::HasLegacyU2fCredentialForGetAssertionRequest(
         *get_assertion_request_,
@@ -58,31 +51,45 @@
     return;
   }
 
-  ChromeOSAuthenticator::IsUVPlatformAuthenticatorAvailable(
-      base::BindOnce(&FidoChromeOSDiscovery::MaybeAddAuthenticator,
-                     weak_factory_.GetWeakPtr()));
+  CheckAuthenticators();
 }
 
-void FidoChromeOSDiscovery::MaybeAddAuthenticator(bool is_available) {
-  if (!is_available) {
+void FidoChromeOSDiscovery::CheckAuthenticators() {
+  ChromeOSAuthenticator::IsPowerButtonModeEnabled(base::BindOnce(
+      &FidoChromeOSDiscovery::CheckUVPlatformAuthenticatorAvailable,
+      weak_factory_.GetWeakPtr()));
+}
+
+void FidoChromeOSDiscovery::CheckUVPlatformAuthenticatorAvailable(
+    bool is_enabled) {
+  ChromeOSAuthenticator::IsUVPlatformAuthenticatorAvailable(base::BindOnce(
+      &FidoChromeOSDiscovery::MaybeAddAuthenticator, weak_factory_.GetWeakPtr(),
+      /*power_button_enabled=*/is_enabled));
+}
+
+void FidoChromeOSDiscovery::MaybeAddAuthenticator(bool power_button_enabled,
+                                                  bool uv_available) {
+  if (require_power_button_mode_) {
+    uv_available = false;
+  }
+
+  if (!uv_available && !power_button_enabled) {
     observer()->DiscoveryStarted(this, /*success=*/false);
     return;
   }
-  authenticator_ =
-      std::make_unique<ChromeOSAuthenticator>(generate_request_id_callback_);
+  authenticator_ = std::make_unique<ChromeOSAuthenticator>(
+      generate_request_id_callback_,
+      ChromeOSAuthenticator::Config{
+          .uv_available = uv_available,
+          .power_button_enabled = power_button_enabled});
   observer()->DiscoveryStarted(this, /*success=*/true, {authenticator_.get()});
 }
 
 void FidoChromeOSDiscovery::OnHasLegacyU2fCredential(bool has_credential) {
   DCHECK(!authenticator_);
-  if (!has_credential) {
-    ChromeOSAuthenticator::IsUVPlatformAuthenticatorAvailable(
-        base::BindOnce(&FidoChromeOSDiscovery::MaybeAddAuthenticator,
-                       weak_factory_.GetWeakPtr()));
-    return;
-  }
-
-  MaybeAddAuthenticator(/*is_available=*/true);
+  ChromeOSAuthenticator::IsUVPlatformAuthenticatorAvailable(base::BindOnce(
+      &FidoChromeOSDiscovery::MaybeAddAuthenticator, weak_factory_.GetWeakPtr(),
+      /*power_button_enabled=*/has_credential));
 }
 
 }  // namespace device
diff --git a/device/fido/cros/discovery.h b/device/fido/cros/discovery.h
index a8cf869..54d4a19 100644
--- a/device/fido/cros/discovery.h
+++ b/device/fido/cros/discovery.h
@@ -33,7 +33,9 @@
 
  private:
   void OnU2FServiceAvailable(bool u2f_service_available);
-  void MaybeAddAuthenticator(bool is_available);
+  void CheckAuthenticators();
+  void CheckUVPlatformAuthenticatorAvailable(bool is_enabled);
+  void MaybeAddAuthenticator(bool power_button_enabled, bool uv_available);
   void OnHasLegacyU2fCredential(bool has_credential);
 
   base::RepeatingCallback<uint32_t()> generate_request_id_callback_;
diff --git a/docs/mojo_and_services.md b/docs/mojo_and_services.md
index 65a1571..45234f6 100644
--- a/docs/mojo_and_services.md
+++ b/docs/mojo_and_services.md
@@ -444,14 +444,9 @@
 that the sandbox is only applied if the interface is launched
 out-of-process using `content::ServiceProcessHost::Launch()`.
 
-Dynamic or feature based mapping to an underlying platform sandbox can be
-achieved using `sandbox::policy::MapToSandboxType()`. As a last resort, specify
-a service's sandbox by specialization of `GetServiceSandboxType()` in an
-appropriate `service_sandbox_type.h` such as
-[`//chrome/browser/service_sandbox_type.h`](https://cs.chromium.org/chromium/src/chrome/browser/service_sandbox_type.h)
-or
-[`//content/browser/service_sandbox_type.h`](https://cs.chromium.org/chromium/src/content/browser/service_sandbox_type.h).
-This must be included where `ServiceProcessHost::Launch()` is called.
+As a last resort, dynamic or feature based mapping to an underlying platform
+sandbox can be achieved but requires plumbing through ContentBrowserClient
+(e.g. `ShouldEnableNetworkServiceSandbox()`).
 
 ## Content-Layer Services Overview
 
diff --git a/docs/testing/web_tests.md b/docs/testing/web_tests.md
index 7fcc071..2968f47 100644
--- a/docs/testing/web_tests.md
+++ b/docs/testing/web_tests.md
@@ -286,6 +286,13 @@
 under `web_tests/compositing` and `web_tests/fast/repaint`, respectively,
 and pass `--blocking-repaint` to `content_shell` when they are run.
 
+Note that you can run the tests with the following command line:
+
+```bash
+third_party/blink/tools/run_web_tests.py virtual/blocking_repaint/compositing \
+  virtual/blocking_repaint/fast/repaint
+```
+
 These virtual tests exist in addition to the original `compositing/...` and
 `fast/repaint/...` tests. They can have their own expectations in
 `web_tests/TestExpectations`, and their own baselines. The test harness will
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 5f66af8..83efb76 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -174,10 +174,6 @@
 // Chrome Web Store.
 constexpr const char kPrefFromWebStore[] = "from_webstore";
 
-// A preference that indicates whether the extension was installed from a
-// mock App created from a bookmark.
-constexpr const char kPrefFromBookmark[] = "from_bookmark";
-
 // A preference that indicates whether the extension was installed as a
 // default app.
 constexpr const char kPrefWasInstalledByDefault[] = "was_installed_by_default";
@@ -1686,21 +1682,11 @@
   return false;
 }
 
-bool ExtensionPrefs::IsFromBookmark(
-    const std::string& extension_id) const {
-  const base::DictionaryValue* dictionary = GetExtensionPref(extension_id);
-  if (dictionary)
-    return dictionary->FindBoolKey(kPrefFromBookmark).value_or(false);
-  return false;
-}
-
 int ExtensionPrefs::GetCreationFlags(const std::string& extension_id) const {
   int creation_flags = Extension::NO_FLAGS;
   if (!ReadPrefAsInteger(extension_id, kPrefCreationFlags, &creation_flags)) {
     // Since kPrefCreationFlags was added later, it will be missing for
     // previously installed extensions.
-    if (IsFromBookmark(extension_id))
-      creation_flags |= Extension::FROM_BOOKMARK;
     if (IsFromWebStore(extension_id))
       creation_flags |= Extension::FROM_WEBSTORE;
     if (WasInstalledByDefault(extension_id))
@@ -2331,7 +2317,6 @@
                              static_cast<int>(extension->location()));
   extension_dict->SetInteger(kPrefCreationFlags, extension->creation_flags());
   extension_dict->SetBoolean(kPrefFromWebStore, extension->from_webstore());
-  extension_dict->SetBoolean(kPrefFromBookmark, extension->from_bookmark());
   extension_dict->SetBoolean(kPrefWasInstalledByDefault,
                              extension->was_installed_by_default());
   extension_dict->SetBoolean(kPrefWasInstalledByOem,
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h
index c9905767..e240db89 100644
--- a/extensions/browser/extension_prefs.h
+++ b/extensions/browser/extension_prefs.h
@@ -586,10 +586,6 @@
   // Returns true if the extension was installed from the Chrome Web Store.
   bool IsFromWebStore(const std::string& extension_id) const;
 
-  // Returns true if the extension was installed from an App generated from a
-  // bookmark.
-  bool IsFromBookmark(const std::string& extension_id) const;
-
   // Returns true if the extension was installed as a default app.
   bool WasInstalledByDefault(const std::string& extension_id) const;
 
diff --git a/ios/chrome/browser/ui/recent_tabs/BUILD.gn b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
index 125934d..d180051 100644
--- a/ios/chrome/browser/ui/recent_tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
@@ -92,6 +92,7 @@
     "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/authentication:signin_presenter",
     "//ios/chrome/browser/ui/authentication/cells",
+    "//ios/chrome/browser/ui/authentication/enterprise:enterprise_utils",
     "//ios/chrome/browser/ui/authentication/signin:signin_headers",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/settings/sync/utils",
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
index 7c0fe7a..8c47f86 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -34,6 +34,7 @@
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_configurator.h"
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_consumer.h"
 #import "ios/chrome/browser/ui/authentication/cells/table_view_signin_promo_item.h"
+#import "ios/chrome/browser/ui/authentication/enterprise/enterprise_utils.h"
 #import "ios/chrome/browser/ui/authentication/signin/signin_utils.h"
 #import "ios/chrome/browser/ui/authentication/signin_presenter.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h"
@@ -250,8 +251,11 @@
 // Returns YES if the user cannot turn on sync for enterprise policy reasons.
 - (BOOL)isSyncDisabledByAdministrator {
   DCHECK(self.syncService);
-  return self.syncService->GetDisableReasons().Has(
+  bool syncDisabledPolicy = self.syncService->GetDisableReasons().Has(
       syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY);
+  bool syncTypesDisabledPolicy =
+      IsManagedSyncDataType(self.browserState, SyncSetupService::kSyncOpenTabs);
+  return syncDisabledPolicy || syncTypesDisabledPolicy;
 }
 
 #pragma mark - SyncObserverModelBridge
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
index a5c93d0..6df0bcf 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -1160,11 +1160,19 @@
 
   DCHECK_EQ(state_, kIdle);
   DCHECK(decoder_display_queue_.empty());
+
+#if DCHECK_IS_ON()
   // All output buffers should've been returned from decoder and device by now.
   // The only remaining owner of surfaces may be display (client), and we will
   // dismiss them when destroying output buffers below.
+  const size_t num_imported_buffers =
+      std::count_if(output_buffer_map_.begin(), output_buffer_map_.end(),
+                    [](const OutputRecord& output_record) {
+                      return output_record.output_frame != nullptr;
+                    });
   DCHECK_EQ(output_queue_->FreeBuffersCount() + surfaces_at_display_.size(),
-            output_buffer_map_.size());
+            num_imported_buffers);
+#endif  // DCHECK_IS_ON()
 
   if (!StopDevicePoll()) {
     LOG(ERROR) << "Failed StopDevicePoll()";
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
index f43cd32..6eb64869 100644
--- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
+++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
@@ -1353,22 +1353,68 @@
                     "Invalid bitrate mode", );
 
   framerate = base::clamp(framerate, 1u, uint32_t{kMaxFrameRateNumerator});
+
   if (frame_rate_ != framerate) {
     HRESULT hr = MFSetAttributeRatio(imf_output_media_type_.Get(),
                                      MF_MT_FRAME_RATE, framerate, 1);
     RETURN_ON_HR_FAILURE(hr, "Couldn't set frame rate for output type", );
 
+    imf_output_media_type_->SetUINT32(MF_MT_AVG_BITRATE, bitrate.target());
+    RETURN_ON_HR_FAILURE(hr, "Couldn't set average bitrate for output type", );
+
     hr = MFSetAttributeRatio(imf_input_media_type_.Get(), MF_MT_FRAME_RATE,
                              framerate, 1);
     RETURN_ON_HR_FAILURE(hr, "Couldn't set frame rate for input type", );
 
-    hr = encoder_->SetOutputType(output_stream_id_,
-                                 imf_output_media_type_.Get(), 0);
-    RETURN_ON_HR_FAILURE(hr, "Couldn't set output media type", );
+    if (is_async_mft_) {
+      // Some HMFTs will reject output type change with MF_E_INVALIDTYPE due
+      // to temporary mismatch between output/input media types, so we always
+      // clear the input/output media types before reconfiguring them
+      // dynamically.
+      hr = encoder_->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
+      RETURN_ON_HR_FAILURE(
+          hr, "Couldn't process message MFT_MESSAGE_COMMAND_DRAIN", );
 
-    hr = encoder_->SetInputType(input_stream_id_, imf_input_media_type_.Get(),
-                                0);
-    RETURN_ON_HR_FAILURE(hr, "Couldn't set input media type", );
+      DrainPendingOutputs();
+
+      hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0);
+      RETURN_ON_HR_FAILURE(
+          hr, "Couldn't process message MFT_MESSAGE_NOTIFY_END_OF_STREAM", );
+
+      hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, 0);
+      RETURN_ON_HR_FAILURE(
+          hr, "Couldn't process message MFT_MESSAGE_NOTIFY_END_STREAMING", );
+
+      hr = encoder_->SetInputType(input_stream_id_, nullptr, 0);
+      RETURN_ON_HR_FAILURE(hr, "Couldn't clear input media type.", );
+
+      hr = encoder_->SetOutputType(output_stream_id_, nullptr, 0);
+      RETURN_ON_HR_FAILURE(hr, "Couldn't clear ouput media type.", );
+
+      hr = encoder_->SetOutputType(output_stream_id_,
+                                   imf_output_media_type_.Get(), 0);
+      RETURN_ON_HR_FAILURE(hr, "Couldn't set output media type", );
+
+      hr = encoder_->SetInputType(input_stream_id_, imf_input_media_type_.Get(),
+                                  0);
+      RETURN_ON_HR_FAILURE(hr, "Couldn't set input media type", );
+
+      hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
+      RETURN_ON_HR_FAILURE(
+          hr, "Couldn't process message MFT_MESSAGE_NOTIFY_BEGIN_STREAMING", );
+
+      hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
+      RETURN_ON_HR_FAILURE(
+          hr, "Couldn't process message MFT_MESSAGE_NOTIFY_START_OF_STREAM", );
+    } else {
+      hr = encoder_->SetOutputType(output_stream_id_,
+                                   imf_output_media_type_.Get(), 0);
+      RETURN_ON_HR_FAILURE(hr, "Couldn't set output media type", );
+
+      hr = encoder_->SetInputType(input_stream_id_, imf_input_media_type_.Get(),
+                                  0);
+      RETURN_ON_HR_FAILURE(hr, "Couldn't set input media type", );
+    }
     frame_rate_ = framerate;
   }
 
@@ -1583,4 +1629,22 @@
   return hr;
 }
 
+void MediaFoundationVideoEncodeAccelerator::DrainPendingOutputs() {
+  Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
+
+  while ((SUCCEEDED(
+      event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event)))) {
+    MediaEventType event_type;
+    HRESULT hr = media_event->GetType(&event_type);
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "Failed to get the type of media event.";
+      continue;
+    }
+
+    if (event_type == METransformHaveOutput) {
+      ProcessOutputAsync();
+    }
+  }
+}
+
 }  // namespace media
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.h b/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
index c6a5f49..be6473b1 100644
--- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
+++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
@@ -101,6 +101,9 @@
   void ProcessOutputAsync();
   void ProcessOutputSync();
 
+  // Drains pending output samples on |encoder_thread_|.
+  void DrainPendingOutputs();
+
   // Tries to deliver the input frame to the encoder.
   bool TryToDeliverInputFrame(scoped_refptr<VideoFrame> frame,
                               bool force_keyframe);
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
index 06647c6..5042367 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
@@ -43,7 +43,7 @@
 #include <stdint.h>
 {%- endif %}
 
-{% if structs|length -%}
+{% if structs|length or unions|length -%}
 #include "mojo/public/cpp/bindings/struct_forward.h"
 {%- endif %}
 
@@ -167,9 +167,9 @@
 {%  for union in unions %}
 class {{union.name}};
 {%    if union|should_inline_union %}
-typedef mojo::InlinedStructPtr<{{union.name}}> {{union.name}}Ptr;
+using {{union.name}}Ptr = mojo::InlinedStructPtr<{{union.name}}>;
 {%    else %}
-typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr;
+using {{union.name}}Ptr = mojo::StructPtr<{{union.name}}>;
 {%    endif %}
 {%- endfor %}
 
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index 5dafb61..960ac79 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -4023,3 +4023,14 @@
 // from preflight cache.
 // The parameters are the same as for CORS_PREFLIGHT_RESULT.
 EVENT_TYPE(CORS_PREFLIGHT_CACHED_RESULT)
+
+// ------------------------------------------------------------------------
+// Initiator
+// ------------------------------------------------------------------------
+
+// This event is logged to indicate the initiator of the network event.
+// The event contains the following parameters:
+//   {
+//     "source_dependency": <Source identifier for the attached event>
+//   }
+EVENT_TYPE(CREATED_BY)
diff --git a/net/log/net_log_source.cc b/net/log/net_log_source.cc
index 549f6d07..db42730 100644
--- a/net/log/net_log_source.cc
+++ b/net/log/net_log_source.cc
@@ -41,6 +41,10 @@
                            base::TimeTicks start_time)
     : type(type), id(id), start_time(start_time) {}
 
+bool NetLogSource::operator==(const NetLogSource& rhs) const {
+  return type == rhs.type && id == rhs.id && start_time == rhs.start_time;
+}
+
 bool NetLogSource::IsValid() const {
   return id != kInvalidId;
 }
diff --git a/net/log/net_log_source.h b/net/log/net_log_source.h
index 32aaf51..fd0b737 100644
--- a/net/log/net_log_source.h
+++ b/net/log/net_log_source.h
@@ -26,6 +26,9 @@
   NetLogSource();
   NetLogSource(NetLogSourceType type, uint32_t id);
   NetLogSource(NetLogSourceType type, uint32_t id, base::TimeTicks start_time);
+
+  bool operator==(const NetLogSource& rhs) const;
+
   bool IsValid() const;
 
   // Adds the source to a dictionary containing event parameters,
diff --git a/net/log/net_log_source_type.h b/net/log/net_log_source_type.h
index 8fe57e541..72ea14d 100644
--- a/net/log/net_log_source_type.h
+++ b/net/log/net_log_source_type.h
@@ -5,10 +5,12 @@
 #ifndef NET_LOG_NET_LOG_SOURCE_TYPE_H_
 #define NET_LOG_NET_LOG_SOURCE_TYPE_H_
 
+#include <stdint.h>
+
 namespace net {
 
 // The "source" identifies the entity that generated the log message.
-enum class NetLogSourceType {
+enum class NetLogSourceType : uint32_t {
 #define SOURCE_TYPE(label) label,
 #include "net/log/net_log_source_type_list.h"
 #undef SOURCE_TYPE
diff --git a/net/log/net_log_source_type_list.h b/net/log/net_log_source_type_list.h
index 3967235b..e9432bd 100644
--- a/net/log/net_log_source_type_list.h
+++ b/net/log/net_log_source_type_list.h
@@ -46,4 +46,3 @@
 SOURCE_TYPE(HTTP3_SESSION)
 SOURCE_TYPE(WEB_TRANSPORT_CLIENT)
 SOURCE_TYPE(NETWORK_SERVICE_HOST_RESOLVER)
-SOURCE_TYPE(CORS_URL_LOADER)
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 942504c..aecb553 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -133,11 +133,9 @@
 
 NetLogWithSource CreateNetLogWithSource(
     NetLog* net_log,
-    absl::optional<uint32_t> net_log_source_id) {
-  if (net_log_source_id) {
-    return NetLogWithSource::Make(
-        net_log,
-        NetLogSource(NetLogSourceType::URL_REQUEST, net_log_source_id.value()));
+    absl::optional<net::NetLogSource> net_log_source) {
+  if (net_log_source) {
+    return NetLogWithSource::Make(net_log, net_log_source.value());
   }
   return NetLogWithSource::Make(net_log, NetLogSourceType::URL_REQUEST);
 }
@@ -559,9 +557,9 @@
                        const URLRequestContext* context,
                        NetworkTrafficAnnotationTag traffic_annotation,
                        bool is_for_websockets,
-                       absl::optional<uint32_t> net_log_source_id)
+                       absl::optional<net::NetLogSource> net_log_source)
     : context_(context),
-      net_log_(CreateNetLogWithSource(context->net_log(), net_log_source_id)),
+      net_log_(CreateNetLogWithSource(context->net_log(), net_log_source)),
       url_chain_(1, url),
       force_ignore_site_for_cookies_(false),
       force_ignore_top_frame_party_for_cookies_(false),
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 308ec99f..8cca710 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -41,6 +41,7 @@
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
 #include "net/log/net_log_event_type.h"
+#include "net/log/net_log_source.h"
 #include "net/log/net_log_with_source.h"
 #include "net/net_buildflags.h"
 #include "net/socket/connection_attempts.h"
@@ -824,7 +825,7 @@
              const URLRequestContext* context,
              NetworkTrafficAnnotationTag traffic_annotation,
              bool is_for_websockets,
-             absl::optional<uint32_t> net_log_source_id);
+             absl::optional<net::NetLogSource> net_log_source);
 
   // Resumes or blocks a request paused by the NetworkDelegate::OnBeforeRequest
   // handler. If |blocked| is true, the request is blocked and an error page is
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index 382b195..045a1dc1 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -22,6 +22,7 @@
 #include "net/http/http_cache.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_transaction_factory.h"
+#include "net/log/net_log_source.h"
 #include "net/socket/ssl_client_socket_impl.h"
 #include "net/url_request/url_request.h"
 
@@ -102,10 +103,10 @@
     URLRequest::Delegate* delegate,
     NetworkTrafficAnnotationTag traffic_annotation,
     bool is_for_websockets,
-    const absl::optional<uint32_t> net_log_source_id) const {
+    const absl::optional<net::NetLogSource> net_log_source) const {
   return base::WrapUnique(new URLRequest(url, priority, delegate, this,
                                          traffic_annotation, is_for_websockets,
-                                         net_log_source_id));
+                                         net_log_source));
 }
 
 void URLRequestContext::set_cookie_store(CookieStore* cookie_store) {
diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h
index 472ab4d..bd1a35c 100644
--- a/net/url_request/url_request_context.h
+++ b/net/url_request/url_request_context.h
@@ -21,6 +21,7 @@
 #include "build/chromeos_buildflags.h"
 #include "net/base/net_export.h"
 #include "net/base/request_priority.h"
+#include "net/log/net_log_source.h"
 #include "net/net_buildflags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request.h"
@@ -115,7 +116,8 @@
       URLRequest::Delegate* delegate,
       NetworkTrafficAnnotationTag traffic_annotation,
       bool is_for_websockets = false,
-      const absl::optional<uint32_t> net_log_source_id = absl::nullopt) const;
+      const absl::optional<net::NetLogSource> net_log_source =
+          absl::nullopt) const;
 
   NetLog* net_log() const { return net_log_; }
 
diff --git a/remoting/protocol/BUILD.gn b/remoting/protocol/BUILD.gn
index 4d9adbc..4c8a9a7 100644
--- a/remoting/protocol/BUILD.gn
+++ b/remoting/protocol/BUILD.gn
@@ -273,11 +273,11 @@
       "webrtc_audio_stream.h",
       "webrtc_connection_to_client.cc",
       "webrtc_connection_to_client.h",
-      "webrtc_dummy_video_encoder.cc",
-      "webrtc_dummy_video_encoder.h",
       "webrtc_frame_scheduler.h",
       "webrtc_frame_scheduler_simple.cc",
       "webrtc_frame_scheduler_simple.h",
+      "webrtc_video_encoder_factory.cc",
+      "webrtc_video_encoder_factory.h",
       "webrtc_video_encoder_wrapper.cc",
       "webrtc_video_encoder_wrapper.h",
       "webrtc_video_frame_adapter.cc",
diff --git a/remoting/protocol/webrtc_connection_to_client.cc b/remoting/protocol/webrtc_connection_to_client.cc
index c1133ac..2e25a97c 100644
--- a/remoting/protocol/webrtc_connection_to_client.cc
+++ b/remoting/protocol/webrtc_connection_to_client.cc
@@ -23,8 +23,8 @@
 #include "remoting/protocol/message_pipe.h"
 #include "remoting/protocol/transport_context.h"
 #include "remoting/protocol/webrtc_audio_stream.h"
-#include "remoting/protocol/webrtc_dummy_video_encoder.h"
 #include "remoting/protocol/webrtc_transport.h"
+#include "remoting/protocol/webrtc_video_encoder_factory.h"
 #include "remoting/protocol/webrtc_video_stream.h"
 #include "third_party/webrtc/api/media_stream_interface.h"
 #include "third_party/webrtc/api/peer_connection_interface.h"
@@ -47,8 +47,7 @@
       audio_task_runner_(audio_task_runner),
       control_dispatcher_(new HostControlDispatcher()),
       event_dispatcher_(new HostEventDispatcher()) {
-  auto video_encoder_factory =
-      std::make_unique<WebrtcDummyVideoEncoderFactory>();
+  auto video_encoder_factory = std::make_unique<WebrtcVideoEncoderFactory>();
   video_encoder_factory_ = video_encoder_factory.get();
   transport_ = std::make_unique<WebrtcTransport>(
       jingle_glue::JingleThreadWrapper::current(), transport_context,
diff --git a/remoting/protocol/webrtc_connection_to_client.h b/remoting/protocol/webrtc_connection_to_client.h
index 9546067f..f3ada8f 100644
--- a/remoting/protocol/webrtc_connection_to_client.h
+++ b/remoting/protocol/webrtc_connection_to_client.h
@@ -22,7 +22,7 @@
 namespace remoting {
 namespace protocol {
 
-class WebrtcDummyVideoEncoderFactory;
+class WebrtcVideoEncoderFactory;
 class HostControlDispatcher;
 class HostEventDispatcher;
 
@@ -92,7 +92,7 @@
 
   std::unique_ptr<Session> session_;
 
-  WebrtcDummyVideoEncoderFactory* video_encoder_factory_;
+  WebrtcVideoEncoderFactory* video_encoder_factory_;
 
   scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
diff --git a/remoting/protocol/webrtc_frame_scheduler_simple.cc b/remoting/protocol/webrtc_frame_scheduler_simple.cc
index e31bd5b..f58d5fb 100644
--- a/remoting/protocol/webrtc_frame_scheduler_simple.cc
+++ b/remoting/protocol/webrtc_frame_scheduler_simple.cc
@@ -12,7 +12,6 @@
 #include "remoting/base/constants.h"
 #include "remoting/protocol/frame_stats.h"
 #include "remoting/protocol/webrtc_bandwidth_estimator.h"
-#include "remoting/protocol/webrtc_dummy_video_encoder.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
 
 namespace remoting {
diff --git a/remoting/protocol/webrtc_frame_scheduler_unittest.cc b/remoting/protocol/webrtc_frame_scheduler_unittest.cc
index be9c4850..1d0b8b2 100644
--- a/remoting/protocol/webrtc_frame_scheduler_unittest.cc
+++ b/remoting/protocol/webrtc_frame_scheduler_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/test/task_environment.h"
 #include "remoting/base/session_options.h"
 #include "remoting/protocol/frame_stats.h"
-#include "remoting/protocol/webrtc_dummy_video_encoder.h"
 #include "remoting/protocol/webrtc_frame_scheduler_simple.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
diff --git a/remoting/protocol/webrtc_transport_unittest.cc b/remoting/protocol/webrtc_transport_unittest.cc
index 2f7f771..354e22e 100644
--- a/remoting/protocol/webrtc_transport_unittest.cc
+++ b/remoting/protocol/webrtc_transport_unittest.cc
@@ -28,7 +28,7 @@
 #include "remoting/protocol/message_serialization.h"
 #include "remoting/protocol/network_settings.h"
 #include "remoting/protocol/transport_context.h"
-#include "remoting/protocol/webrtc_dummy_video_encoder.h"
+#include "remoting/protocol/webrtc_video_encoder_factory.h"
 #include "remoting/signaling/fake_signal_strategy.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
@@ -216,8 +216,7 @@
     host_transport_ = std::make_unique<WebrtcTransport>(
         jingle_glue::JingleThreadWrapper::current(),
         TransportContext::ForTests(TransportRole::SERVER),
-        std::make_unique<WebrtcDummyVideoEncoderFactory>(),
-        &host_event_handler_);
+        std::make_unique<WebrtcVideoEncoderFactory>(), &host_event_handler_);
 
     host_transport_->SetThreadJoinWatchdogForTests(
         std::make_unique<FakeThreadJoinWatchdog>(
diff --git a/remoting/protocol/webrtc_dummy_video_encoder.cc b/remoting/protocol/webrtc_video_encoder_factory.cc
similarity index 78%
rename from remoting/protocol/webrtc_dummy_video_encoder.cc
rename to remoting/protocol/webrtc_video_encoder_factory.cc
index 2edf5b09..78a21547 100644
--- a/remoting/protocol/webrtc_dummy_video_encoder.cc
+++ b/remoting/protocol/webrtc_video_encoder_factory.cc
@@ -1,8 +1,8 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// 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 "remoting/protocol/webrtc_dummy_video_encoder.h"
+#include "remoting/protocol/webrtc_video_encoder_factory.h"
 
 #include "base/bind.h"
 #include "base/logging.h"
@@ -15,7 +15,7 @@
 namespace remoting {
 namespace protocol {
 
-WebrtcDummyVideoEncoderFactory::WebrtcDummyVideoEncoderFactory()
+WebrtcVideoEncoderFactory::WebrtcVideoEncoderFactory()
     : main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
   formats_.push_back(webrtc::SdpVideoFormat("VP8"));
   formats_.push_back(webrtc::SdpVideoFormat("VP9"));
@@ -26,10 +26,10 @@
 #endif
 }
 
-WebrtcDummyVideoEncoderFactory::~WebrtcDummyVideoEncoderFactory() = default;
+WebrtcVideoEncoderFactory::~WebrtcVideoEncoderFactory() = default;
 
 std::unique_ptr<webrtc::VideoEncoder>
-WebrtcDummyVideoEncoderFactory::CreateVideoEncoder(
+WebrtcVideoEncoderFactory::CreateVideoEncoder(
     const webrtc::SdpVideoFormat& format) {
   webrtc::VideoCodecType type = webrtc::PayloadStringToCodecType(format.name);
   auto encoder = std::make_unique<WebrtcVideoEncoderWrapper>(
@@ -44,18 +44,18 @@
 }
 
 std::vector<webrtc::SdpVideoFormat>
-WebrtcDummyVideoEncoderFactory::GetSupportedFormats() const {
+WebrtcVideoEncoderFactory::GetSupportedFormats() const {
   return formats_;
 }
 
-void WebrtcDummyVideoEncoderFactory::RegisterEncoderSelectedCallback(
+void WebrtcVideoEncoderFactory::RegisterEncoderSelectedCallback(
     const base::RepeatingCallback<
         void(webrtc::VideoCodecType,
              const webrtc::SdpVideoFormat::Parameters&)>& callback) {
   encoder_created_callback_ = callback;
 }
 
-void WebrtcDummyVideoEncoderFactory::SetVideoChannelStateObserver(
+void WebrtcVideoEncoderFactory::SetVideoChannelStateObserver(
     base::WeakPtr<VideoChannelStateObserver> video_channel_state_observer) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   base::AutoLock lock(lock_);
diff --git a/remoting/protocol/webrtc_dummy_video_encoder.h b/remoting/protocol/webrtc_video_encoder_factory.h
similarity index 83%
rename from remoting/protocol/webrtc_dummy_video_encoder.h
rename to remoting/protocol/webrtc_video_encoder_factory.h
index aa18032e..183772f1a 100644
--- a/remoting/protocol/webrtc_dummy_video_encoder.h
+++ b/remoting/protocol/webrtc_video_encoder_factory.h
@@ -1,9 +1,9 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// 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 REMOTING_PROTOCOL_WEBRTC_DUMMY_VIDEO_ENCODER_H_
-#define REMOTING_PROTOCOL_WEBRTC_DUMMY_VIDEO_ENCODER_H_
+#ifndef REMOTING_PROTOCOL_WEBRTC_VIDEO_ENCODER_FACTORY_H_
+#define REMOTING_PROTOCOL_WEBRTC_VIDEO_ENCODER_FACTORY_H_
 
 #include <memory>
 #include <vector>
@@ -24,10 +24,10 @@
 // This is the encoder factory that is passed to WebRTC when the peer connection
 // is created. This factory creates the video encoder, which is an instance of
 // WebrtcVideoEncoderWrapper which wraps a video codec from remoting/codec.
-class WebrtcDummyVideoEncoderFactory : public webrtc::VideoEncoderFactory {
+class WebrtcVideoEncoderFactory : public webrtc::VideoEncoderFactory {
  public:
-  WebrtcDummyVideoEncoderFactory();
-  ~WebrtcDummyVideoEncoderFactory() override;
+  WebrtcVideoEncoderFactory();
+  ~WebrtcVideoEncoderFactory() override;
 
   // webrtc::VideoEncoderFactory interface.
   std::unique_ptr<webrtc::VideoEncoder> CreateVideoEncoder(
@@ -60,4 +60,4 @@
 }  // namespace protocol
 }  // namespace remoting
 
-#endif  // REMOTING_PROTOCOL_WEBRTC_DUMMY_VIDEO_ENCODER_H_
+#endif  // REMOTING_PROTOCOL_WEBRTC_VIDEO_ENCODER_FACTORY_H_
diff --git a/remoting/protocol/webrtc_video_stream.cc b/remoting/protocol/webrtc_video_stream.cc
index e9a37439..2c06210 100644
--- a/remoting/protocol/webrtc_video_stream.cc
+++ b/remoting/protocol/webrtc_video_stream.cc
@@ -15,9 +15,9 @@
 #include "remoting/codec/webrtc_video_encoder_vpx.h"
 #include "remoting/protocol/frame_stats.h"
 #include "remoting/protocol/host_video_stats_dispatcher.h"
-#include "remoting/protocol/webrtc_dummy_video_encoder.h"
 #include "remoting/protocol/webrtc_frame_scheduler_simple.h"
 #include "remoting/protocol/webrtc_transport.h"
+#include "remoting/protocol/webrtc_video_encoder_factory.h"
 #include "remoting/protocol/webrtc_video_frame_adapter.h"
 #include "remoting/protocol/webrtc_video_track_source.h"
 #include "third_party/webrtc/api/media_stream_interface.h"
@@ -88,7 +88,7 @@
 void WebrtcVideoStream::Start(
     std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer,
     WebrtcTransport* webrtc_transport,
-    WebrtcDummyVideoEncoderFactory* video_encoder_factory,
+    WebrtcVideoEncoderFactory* video_encoder_factory,
     scoped_refptr<base::SequencedTaskRunner> encode_task_runner) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(desktop_capturer);
@@ -104,7 +104,6 @@
 
   encode_task_runner_ = std::move(encode_task_runner);
   capturer_ = std::move(desktop_capturer);
-  video_encoder_factory_ = video_encoder_factory;
 
   video_encoder_factory->RegisterEncoderSelectedCallback(base::BindRepeating(
       &WebrtcVideoStream::OnEncoderCreated, weak_factory_.GetWeakPtr()));
@@ -339,7 +338,7 @@
   // removed.
 
   // This method is called when SDP has been negotiated and WebRTC has
-  // created a preferred encoder via the WebrtcDummyVideoEncoderFactory
+  // created a preferred encoder via the WebrtcVideoEncoderFactory
   // implementation.
   // Trigger recreating the encoder in case a previous one was being used and
   // SDP renegotiation selected a different one. The proper encoder will be
diff --git a/remoting/protocol/webrtc_video_stream.h b/remoting/protocol/webrtc_video_stream.h
index 11d0dd5a..824e7d97 100644
--- a/remoting/protocol/webrtc_video_stream.h
+++ b/remoting/protocol/webrtc_video_stream.h
@@ -35,7 +35,7 @@
 namespace protocol {
 
 class HostVideoStatsDispatcher;
-class WebrtcDummyVideoEncoderFactory;
+class WebrtcVideoEncoderFactory;
 class WebrtcFrameScheduler;
 class WebrtcTransport;
 
@@ -53,7 +53,7 @@
 
   void Start(std::unique_ptr<webrtc::DesktopCapturer> desktop_capturer,
              WebrtcTransport* webrtc_transport,
-             WebrtcDummyVideoEncoderFactory* video_encoder_factory,
+             WebrtcVideoEncoderFactory* video_encoder_factory,
              scoped_refptr<base::SequencedTaskRunner> encode_task_runner);
 
   // VideoStream interface.
@@ -108,8 +108,6 @@
 
   // Capturer used to capture the screen.
   std::unique_ptr<webrtc::DesktopCapturer> capturer_;
-  // Used to send across encoded frames.
-  WebrtcDummyVideoEncoderFactory* video_encoder_factory_;
 
   // Task runner used by software encoders.
   scoped_refptr<base::SequencedTaskRunner> encode_task_runner_;
diff --git a/sandbox/policy/mojom/sandbox.mojom b/sandbox/policy/mojom/sandbox.mojom
index 27d2b11..21a62aa 100644
--- a/sandbox/policy/mojom/sandbox.mojom
+++ b/sandbox/policy/mojom/sandbox.mojom
@@ -21,6 +21,9 @@
   // For instance, it allows dynamic code and wider access to APIs on Windows.
   kUtility,
 
+  // The audio service process. May be disabled by policy.
+  kAudio,
+
   // Hosts the content decryption module. Allows pre-loading of CDM libraries.
   // - On Windows, when `CdmServiceBroker` is connected the CDM was not
   // sandboxed to allow CDM preloading.
@@ -28,6 +31,9 @@
   // - On Linux/ChromeOS, the CDM is preloaded in the zygote sandbox.
   kCdm,
 
+  // The network service. May be disabled by policy.
+  kNetwork,
+
   // Runs with the same rights as the browser. Usually needed to improve
   // stability by hosting code that interacts with third party code in another
   // process.
diff --git a/sandbox/policy/sandbox_type.h b/sandbox/policy/sandbox_type.h
index 51ccf609..a2d3a4e 100644
--- a/sandbox/policy/sandbox_type.h
+++ b/sandbox/policy/sandbox_type.h
@@ -125,10 +125,14 @@
 inline constexpr sandbox::policy::SandboxType MapToSandboxType(
     sandbox::mojom::Sandbox mojo_sandbox) {
   switch (mojo_sandbox) {
+    case sandbox::mojom::Sandbox::kAudio:
+      return sandbox::policy::SandboxType::kAudio;
     case sandbox::mojom::Sandbox::kCdm:
       return sandbox::policy::SandboxType::kCdm;
     case sandbox::mojom::Sandbox::kGpu:
       return sandbox::policy::SandboxType::kGpu;
+    case sandbox::mojom::Sandbox::kNetwork:
+      return sandbox::policy::SandboxType::kNetwork;
     case sandbox::mojom::Sandbox::kNoSandbox:
       return sandbox::policy::SandboxType::kNoSandbox;
     case sandbox::mojom::Sandbox::kPrintCompositor:
diff --git a/services/audio/public/mojom/BUILD.gn b/services/audio/public/mojom/BUILD.gn
index 869517c3..c877f960 100644
--- a/services/audio/public/mojom/BUILD.gn
+++ b/services/audio/public/mojom/BUILD.gn
@@ -18,6 +18,7 @@
   public_deps = [
     "//media/mojo/mojom",
     "//mojo/public/mojom/base",
+    "//sandbox/policy/mojom",
   ]
 
   cpp_typemaps = [
diff --git a/services/audio/public/mojom/audio_service.mojom b/services/audio/public/mojom/audio_service.mojom
index 2c1f4cd..65f5a369 100644
--- a/services/audio/public/mojom/audio_service.mojom
+++ b/services/audio/public/mojom/audio_service.mojom
@@ -5,6 +5,7 @@
 module audio.mojom;
 
 import "media/mojo/mojom/audio_stream_factory.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
 import "services/audio/public/mojom/debug_recording.mojom";
 import "services/audio/public/mojom/device_notifications.mojom";
 import "services/audio/public/mojom/log_factory_manager.mojom";
@@ -12,7 +13,9 @@
 import "services/audio/public/mojom/testing_api.mojom";
 
 // The main interface to the Audio service. This is a privileged interface and
-// must only be bound by trusted processes, e.g. a browser process.
+// must only be bound by trusted processes, e.g. a browser process. Note that
+// the sandbox can be disabled (to kNoSandbox) by policy.
+[ServiceSandbox=sandbox.mojom.Sandbox.kAudio]
 interface AudioService {
   // Binds a SystemInfo interface receiver.
   BindSystemInfo(pending_receiver<SystemInfo> receiver);
@@ -33,4 +36,3 @@
   // environments.
   BindTestingApi(pending_receiver<TestingApi> receiver);
 };
-
diff --git a/services/data_decoder/BUILD.gn b/services/data_decoder/BUILD.gn
index d3cdd331..8bb7fc80 100644
--- a/services/data_decoder/BUILD.gn
+++ b/services/data_decoder/BUILD.gn
@@ -13,8 +13,6 @@
     "gzipper.h",
     "json_parser_impl.cc",
     "json_parser_impl.h",
-    "web_bundle_builder.cc",
-    "web_bundle_builder.h",
     "web_bundler.cc",
     "web_bundler.h",
     "xml_parser.cc",
@@ -65,7 +63,6 @@
     "public/cpp/json_sanitizer_unittest.cc",
     "public/cpp/safe_web_bundle_parser_unittest.cc",
     "public/cpp/safe_xml_parser_unittest.cc",
-    "web_bundle_builder_unittest.cc",
     "xml_parser_unittest.cc",
   ]
 
diff --git a/services/data_decoder/web_bundle_builder.cc b/services/data_decoder/web_bundle_builder.cc
deleted file mode 100644
index 63cb7bbe..0000000
--- a/services/data_decoder/web_bundle_builder.cc
+++ /dev/null
@@ -1,153 +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 "services/data_decoder/web_bundle_builder.h"
-
-#include "base/big_endian.h"
-#include "components/cbor/writer.h"
-
-namespace data_decoder {
-
-namespace {
-
-// TODO(myrzakereyms): replace this method with cbor::writer::GetNumUintBytes.
-uint64_t GetNumUintBytes(uint64_t value) {
-  if (value < 24) {
-    return 0;
-  } else if (value <= 0xFF) {
-    return 1;
-  } else if (value <= 0xFFFF) {
-    return 2;
-  } else if (value <= 0xFFFFFFFF) {
-    return 4;
-  }
-  return 8;
-}
-
-uint64_t GetEncodedByteSizeOfString(uint64_t size) {
-  return 1 + GetNumUintBytes(size);
-}
-
-uint64_t GetEncodedByteSizeOfHeaders(const WebBundleBuilder::Headers& headers) {
-  uint64_t byte_size = 1 + GetNumUintBytes(headers.size());
-  for (const auto& header : headers) {
-    byte_size +=
-        GetEncodedByteSizeOfString(header.first.size()) + header.first.size() +
-        GetEncodedByteSizeOfString(header.second.size()) + header.second.size();
-  }
-  return byte_size;
-}
-
-uint64_t GetEncodedByteSizeOfResponse(const WebBundleBuilder::Headers& headers,
-                                      uint64_t body_size) {
-  uint64_t encoded_header_map_size = GetEncodedByteSizeOfHeaders(headers);
-  return 1 /* size of header of array(2) */ +
-         GetEncodedByteSizeOfString(encoded_header_map_size) +
-         encoded_header_map_size + GetEncodedByteSizeOfString(body_size) +
-         body_size;
-}
-
-cbor::Value CreateByteString(base::StringPiece s) {
-  return cbor::Value(base::as_bytes(base::make_span(s)));
-}
-
-cbor::Value CreateHeaderMap(const WebBundleBuilder::Headers& headers) {
-  cbor::Value::MapValue map;
-  for (const auto& pair : headers)
-    map.insert({CreateByteString(pair.first), CreateByteString(pair.second)});
-  return cbor::Value(std::move(map));
-}
-
-std::vector<uint8_t> Encode(const cbor::Value& value) {
-  return *cbor::Writer::Write(value);
-}
-
-int64_t EncodedLength(const cbor::Value& value) {
-  return Encode(value).size();
-}
-}  // namespace
-
-WebBundleBuilder::WebBundleBuilder(const std::string& fallback_url)
-    : fallback_url_(fallback_url) {}
-
-WebBundleBuilder::~WebBundleBuilder() = default;
-
-void WebBundleBuilder::SetExchanges(
-    std::vector<mojom::SerializedResourceInfoPtr> resources,
-    std::vector<absl::optional<mojo_base::BigBuffer>> bodies) {
-  CHECK_EQ(resources.size(), bodies.size());
-  int64_t responses_offset = 1 + GetNumUintBytes(resources.size());
-  for (size_t i = 0; i < resources.size(); ++i) {
-    const auto& info = resources[i];
-    const auto& body = bodies[i];
-    Headers headers = {{":status", "200"}, {"content-type", info->mime_type}};
-    uint64_t response_length =
-        GetEncodedByteSizeOfResponse(headers, body ? body->size() : 0);
-    ResponseLocation location = {responses_offset,
-                                 static_cast<int64_t>(response_length)};
-    responses_offset += response_length;
-    cbor::Value::ArrayValue response_array;
-    response_array.emplace_back(Encode(CreateHeaderMap(headers)));
-    response_array.emplace_back(CreateByteString(
-        body ? base::StringPiece(reinterpret_cast<const char*>(body->data()),
-                                 body->size())
-             : ""));
-    cbor::Value response(response_array);
-    responses_.emplace_back(std::move(response));
-    GURL url = info->url;
-    GURL::Replacements replacements;
-    replacements.ClearRef();
-    url = url.ReplaceComponents(replacements);
-    AddIndexEntry(url.spec(), "", {location});
-  }
-}
-
-void WebBundleBuilder::AddIndexEntry(
-    base::StringPiece url,
-    base::StringPiece variants_value,
-    std::vector<ResponseLocation> response_locations) {
-  cbor::Value::ArrayValue index_value_array;
-  index_value_array.emplace_back(CreateByteString(variants_value));
-  for (const auto& location : response_locations) {
-    index_value_array.emplace_back(location.offset);
-    index_value_array.emplace_back(location.length);
-  }
-  index_.insert({cbor::Value(url), cbor::Value(index_value_array)});
-}
-
-void WebBundleBuilder::AddSection(base::StringPiece name, cbor::Value section) {
-  section_lengths_.emplace_back(name);
-  section_lengths_.emplace_back(EncodedLength(section));
-  sections_.emplace_back(std::move(section));
-}
-
-std::vector<uint8_t> WebBundleBuilder::CreateBundle(
-    std::vector<mojom::SerializedResourceInfoPtr> resources,
-    std::vector<absl::optional<mojo_base::BigBuffer>> bodies) {
-  SetExchanges(std::move(resources), std::move(bodies));
-  AddSection("index", cbor::Value(index_));
-  AddSection("responses", cbor::Value(responses_));
-  return CreateTopLevel();
-}
-
-std::vector<uint8_t> WebBundleBuilder::CreateTopLevel() {
-  cbor::Value::ArrayValue toplevel_array;
-  toplevel_array.emplace_back(
-      CreateByteString(u8"\U0001F310\U0001F4E6"));  // "🌐📦"
-  toplevel_array.emplace_back(CreateByteString(base::StringPiece("b1\0\0", 4)));
-  toplevel_array.emplace_back(cbor::Value(fallback_url_));
-  toplevel_array.emplace_back(Encode(cbor::Value(section_lengths_)));
-  toplevel_array.emplace_back(sections_);
-  // Put a dummy 8-byte bytestring.
-  toplevel_array.emplace_back(cbor::Value::BinaryValue(8, 0));
-
-  std::vector<uint8_t> bundle = Encode(cbor::Value(toplevel_array));
-  char encoded[8];
-  base::WriteBigEndian(encoded, static_cast<uint64_t>(bundle.size()));
-  // Overwrite the dummy bytestring with the actual size.
-  memcpy(bundle.data() + bundle.size() - 8, encoded, 8);
-
-  return bundle;
-}
-}  // namespace data_decoder
diff --git a/services/data_decoder/web_bundle_builder.h b/services/data_decoder/web_bundle_builder.h
deleted file mode 100644
index af461d7..0000000
--- a/services/data_decoder/web_bundle_builder.h
+++ /dev/null
@@ -1,56 +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 SERVICES_DATA_DECODER_WEB_BUNDLE_BUILDER_H_
-#define SERVICES_DATA_DECODER_WEB_BUNDLE_BUILDER_H_
-
-#include <vector>
-
-#include "base/files/file.h"
-#include "base/strings/string_piece.h"
-#include "components/cbor/values.h"
-#include "mojo/public/cpp/base/big_buffer.h"
-#include "services/data_decoder/public/mojom/resource_snapshot_for_web_bundle.mojom.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace data_decoder {
-
-class WebBundleBuilder {
- public:
-  using Headers = std::vector<std::pair<std::string, std::string>>;
-  struct ResponseLocation {
-    // /components/cbor uses int64_t for integer types.
-    int64_t offset;
-    int64_t length;
-  };
-
-  explicit WebBundleBuilder(const std::string& fallback_url);
-  ~WebBundleBuilder();
-
-  WebBundleBuilder(const WebBundleBuilder&) = delete;
-  WebBundleBuilder& operator=(const WebBundleBuilder&) = delete;
-
-  std::vector<uint8_t> CreateBundle(
-      std::vector<mojom::SerializedResourceInfoPtr> resources,
-      std::vector<absl::optional<mojo_base::BigBuffer>> bodies);
-
- private:
-  void SetExchanges(std::vector<mojom::SerializedResourceInfoPtr> resources,
-                    std::vector<absl::optional<mojo_base::BigBuffer>> bodies);
-  void AddIndexEntry(base::StringPiece url,
-                     base::StringPiece variants_value,
-                     std::vector<ResponseLocation> response_locations);
-  void AddSection(base::StringPiece name, cbor::Value section);
-  void WriteBundleLength(uint8_t bundle_length);
-  std::vector<uint8_t> CreateTopLevel();
-
-  std::string fallback_url_;
-  cbor::Value::ArrayValue section_lengths_;
-  cbor::Value::ArrayValue sections_;
-  cbor::Value::MapValue index_;
-  cbor::Value::ArrayValue responses_;
-};
-}  // namespace data_decoder
-
-#endif  // SERVICES_DATA_DECODER_WEB_BUNDLE_BUILDER_H_
diff --git a/services/data_decoder/web_bundle_builder_unittest.cc b/services/data_decoder/web_bundle_builder_unittest.cc
deleted file mode 100644
index 734511b6..0000000
--- a/services/data_decoder/web_bundle_builder_unittest.cc
+++ /dev/null
@@ -1,80 +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 "services/data_decoder/web_bundle_builder.h"
-
-#include "base/big_endian.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/path_service.h"
-#include "base/test/task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace data_decoder {
-
-namespace {
-
-std::string kFallbackUrl = "https://test.example.org/";
-
-std::string GetTestFileContents(const base::FilePath& path) {
-  base::FilePath test_data_dir;
-  base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir);
-  test_data_dir = test_data_dir.Append(
-      base::FilePath(FILE_PATH_LITERAL("components/test/data/web_package")));
-
-  std::string contents;
-  EXPECT_TRUE(base::ReadFileToString(test_data_dir.Append(path), &contents));
-  return contents;
-}
-
-std::vector<uint8_t> GetStringAsBytes(base::StringPiece contents) {
-  auto bytes = base::as_bytes(base::make_span(contents));
-  return std::vector<uint8_t>(bytes.begin(), bytes.end());
-}
-
-}  // namespace
-
-class WebBundleBuilderTest : public testing::Test {
- private:
-  base::test::TaskEnvironment task_environment_;
-};
-
-TEST_F(WebBundleBuilderTest, CorrectWebBundleSizeIsWritten) {
-  WebBundleBuilder builder(kFallbackUrl);
-  std::vector<mojom::SerializedResourceInfoPtr> exchanges;
-  mojom::SerializedResourceInfoPtr ptr = mojom::SerializedResourceInfo::New(
-      GURL("https://test.example.org/index.html"), "text/html", 0);
-  exchanges.emplace_back(std::move(ptr));
-  std::vector<absl::optional<mojo_base::BigBuffer>> bodies;
-  bodies.emplace_back();
-  std::vector<uint8_t> bundle =
-      builder.CreateBundle(std::move(exchanges), std::move(bodies));
-  char written_size[8];
-  memcpy(written_size, bundle.data() + bundle.size() - 8, 8);
-  uint64_t written_size_int;
-  base::ReadBigEndian(written_size, &written_size_int);
-  EXPECT_EQ(bundle.size(), written_size_int);
-}
-
-TEST_F(WebBundleBuilderTest, ByteByByteComparison) {
-  WebBundleBuilder builder(kFallbackUrl);
-  std::vector<mojom::SerializedResourceInfoPtr> exchanges;
-  std::vector<absl::optional<mojo_base::BigBuffer>> bodies;
-  exchanges.emplace_back(mojom::SerializedResourceInfo::New(
-      GURL("https://test.example.org/"), "text/html; charset=UTF-8", 46));
-  bodies.emplace_back(absl::optional<mojo_base::BigBuffer>(
-      GetStringAsBytes("<a href='index.html'>click for web bundles</a>")));
-  exchanges.emplace_back(mojom::SerializedResourceInfo::New(
-      GURL("https://test.example.org/index.html"), "text/html; charset=UTF-8",
-      25));
-  bodies.emplace_back(absl::optional<mojo_base::BigBuffer>(
-      GetStringAsBytes("<p>Hello Web Bundles!</p>")));
-  std::vector<uint8_t> bundle =
-      builder.CreateBundle(std::move(exchanges), std::move(bodies));
-  std::vector<uint8_t> expected_bundle = GetStringAsBytes(
-      GetTestFileContents(base::FilePath(FILE_PATH_LITERAL("simple.wbn"))));
-  EXPECT_EQ(bundle, expected_bundle);
-}
-
-}  // namespace data_decoder
diff --git a/services/data_decoder/web_bundler.cc b/services/data_decoder/web_bundler.cc
index 0d715a14..5d057893 100644
--- a/services/data_decoder/web_bundler.cc
+++ b/services/data_decoder/web_bundler.cc
@@ -7,7 +7,7 @@
 #include "base/big_endian.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_piece.h"
-#include "web_bundle_builder.h"
+#include "components/web_package/web_bundle_builder.h"
 
 namespace data_decoder {
 
@@ -93,7 +93,6 @@
   GURL::Replacements replacements;
   replacements.ClearRef();
   url = url.ReplaceComponents(replacements);
-  WebBundleBuilder builder(url.spec());
   std::set<GURL> url_set;
   CHECK_EQ(resources_.size(), bodies_.size());
   std::vector<mojom::SerializedResourceInfoPtr> resources;
@@ -113,8 +112,28 @@
       }
     }
   }
-  std::vector<uint8_t> bundle =
-      builder.CreateBundle(std::move(resources), std::move(bodies));
+
+  CHECK_EQ(resources.size(), bodies.size());
+  web_package::WebBundleBuilder builder(url.spec(), "",
+                                        web_package::BundleVersion::kB2);
+  for (size_t i = 0; i < resources.size(); ++i) {
+    const auto& info = resources[i];
+    const auto& body = bodies[i];
+    web_package::WebBundleBuilder::Headers headers = {
+        {":status", "200"}, {"content-type", info->mime_type}};
+    web_package::WebBundleBuilder::ResponseLocation response_location =
+        builder.AddResponse(
+            headers, body ? base::StringPiece(
+                                reinterpret_cast<const char*>(body->data()),
+                                body->size())
+                          : "");
+    GURL url = info->url;
+    GURL::Replacements replacements;
+    replacements.ClearRef();
+    url = url.ReplaceComponents(replacements);
+    builder.AddIndexEntry(url.spec(), "", {response_location});
+  }
+  std::vector<uint8_t> bundle = builder.CreateBundle();
   int written_size = file_.WriteAtCurrentPos(
       reinterpret_cast<const char*>(bundle.data()), bundle.size());
   DCHECK_EQ(static_cast<int>(bundle.size()), written_size);
diff --git a/services/network/BUILD.gn b/services/network/BUILD.gn
index bcb498b7..f8979929 100644
--- a/services/network/BUILD.gn
+++ b/services/network/BUILD.gn
@@ -412,7 +412,7 @@
     "//components/network_session_configurator/browser",
     "//components/prefs:test_support",
     "//components/variations:test_support",
-    "//components/web_package:test_support",
+    "//components/web_package",
     "//crypto",
     "//jingle:fake_ssl_socket",
     "//mojo/public/cpp/bindings",
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc
index 26984e7..1d8527448 100644
--- a/services/network/cors/cors_url_loader.cc
+++ b/services/network/cors/cors_url_loader.cc
@@ -255,8 +255,7 @@
 
   receiver_.set_disconnect_handler(
       base::BindOnce(&CorsURLLoader::OnMojoDisconnect, base::Unretained(this)));
-  request_.net_log_params =
-      network::ResourceRequest::NetLogParams(net_log_.source().id);
+  request_.net_log_create_info = net_log_.source();
   DCHECK(network_loader_factory_);
   DCHECK(origin_access_list_);
   DCHECK(preflight_controller_);
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc
index 04517719..61ae8232 100644
--- a/services/network/cors/cors_url_loader_factory.cc
+++ b/services/network/cors/cors_url_loader_factory.cc
@@ -513,10 +513,18 @@
     return false;
   }
 
-  // |net_log_params| field is expected to be used within network service.
-  if (request.net_log_params.has_value()) {
+  // `net_log_create_info` field is expected to be used within network
+  // service.
+  if (request.net_log_create_info) {
     mojo::ReportBadMessage(
-        "CorsURLLoaderFactory: net_log_params field is not expected.");
+        "CorsURLLoaderFactory: net_log_create_info field is not expected.");
+  }
+
+  // `net_log_reference_info` field is expected to be used within network
+  // service.
+  if (request.net_log_reference_info) {
+    mojo::ReportBadMessage(
+        "CorsURLLoaderFactory: net_log_reference_info field is not expected.");
   }
 
   if (request.target_ip_address_space != mojom::IPAddressSpace::kUnknown) {
diff --git a/services/network/cors/preflight_cache_unittest.cc b/services/network/cors/preflight_cache_unittest.cc
index 7bb828f..7f2024a 100644
--- a/services/network/cors/preflight_cache_unittest.cc
+++ b/services/network/cors/preflight_cache_unittest.cc
@@ -28,9 +28,9 @@
 class PreflightCacheTest : public testing::Test {
  public:
   PreflightCacheTest()
-      : net_log_(net::NetLogWithSource::Make(
-            net::NetLog::Get(),
-            net::NetLogSourceType::CORS_URL_LOADER)) {}
+      : net_log_(
+            net::NetLogWithSource::Make(net::NetLog::Get(),
+                                        net::NetLogSourceType::URL_REQUEST)) {}
 
  protected:
   size_t CountEntries() const { return cache_.CountEntriesForTesting(); }
@@ -242,7 +242,7 @@
   std::vector<net::NetLogEntry> entries = net_log_observer.GetEntries();
   ASSERT_EQ(entries.size(), 5u);
   for (const auto& entry : entries) {
-    EXPECT_EQ(entry.source.type, net::NetLogSourceType::CORS_URL_LOADER);
+    EXPECT_EQ(entry.source.type, net::NetLogSourceType::URL_REQUEST);
   }
   EXPECT_EQ(entries[0].type, net::NetLogEventType::CHECK_CORS_PREFLIGHT_CACHE);
   EXPECT_EQ(net::GetStringValueFromParams(entries[0], "status"),
diff --git a/services/network/cors/preflight_controller.cc b/services/network/cors/preflight_controller.cc
index 05b66b5..22b629e6 100644
--- a/services/network/cors/preflight_controller.cc
+++ b/services/network/cors/preflight_controller.cc
@@ -15,6 +15,8 @@
 #include "net/base/load_flags.h"
 #include "net/base/network_isolation_key.h"
 #include "net/http/http_request_headers.h"
+#include "net/log/net_log.h"
+#include "net/log/net_log_source.h"
 #include "net/log/net_log_with_source.h"
 #include "services/network/cors/cors_util.h"
 #include "services/network/network_service.h"
@@ -82,6 +84,7 @@
 std::unique_ptr<ResourceRequest> CreatePreflightRequest(
     const ResourceRequest& request,
     bool tainted,
+    const net::NetLogSource& net_log_source,
     const absl::optional<base::UnguessableToken>& devtools_request_id) {
   DCHECK(!request.url.has_username());
   DCHECK(!request.url.has_password());
@@ -153,6 +156,7 @@
   }
   preflight_request->is_fetch_like_api = request.is_fetch_like_api;
   preflight_request->is_favicon = request.is_favicon;
+  preflight_request->net_log_reference_info = net_log_source;
 
   return preflight_request;
 }
@@ -321,8 +325,8 @@
         net_log_(net_log) {
     if (devtools_observer_)
       devtools_request_id_ = base::UnguessableToken::Create();
-    auto preflight_request =
-        CreatePreflightRequest(request, tainted, devtools_request_id_);
+    auto preflight_request = CreatePreflightRequest(
+        request, tainted, net_log.source(), devtools_request_id_);
 
     if (devtools_observer_) {
       DCHECK(devtools_request_id_);
@@ -468,7 +472,11 @@
 PreflightController::CreatePreflightRequestForTesting(
     const ResourceRequest& request,
     bool tainted) {
-  return CreatePreflightRequest(request, tainted, absl::nullopt);
+  return CreatePreflightRequest(
+      request, tainted,
+      net::NetLogSource(net::NetLogSourceType::URL_REQUEST,
+                        net::NetLog::Get()->NextID()),
+      /*devtools_request_id=*/absl::nullopt);
 }
 
 // static
diff --git a/services/network/cors/preflight_controller_unittest.cc b/services/network/cors/preflight_controller_unittest.cc
index 3c23fb5..a974968f 100644
--- a/services/network/cors/preflight_controller_unittest.cc
+++ b/services/network/cors/preflight_controller_unittest.cc
@@ -13,6 +13,8 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
+#include "net/log/net_log.h"
+#include "net/log/net_log_source_type.h"
 #include "net/log/net_log_with_source.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
@@ -224,19 +226,21 @@
   network::ResourceRequest request;
   request.url = GURL("https://example.com/");
   request.request_initiator = url::Origin();
+  net::NetLogWithSource net_log = net::NetLogWithSource::Make(
+      net::NetLog::Get(), net::NetLogSourceType::URL_REQUEST);
   preflight_controller.PerformPreflightCheck(
       base::BindOnce([](int, absl::optional<CorsErrorStatus>, bool) {}),
       request, WithTrustedHeaderClient(false),
       WithNonWildcardRequestHeadersSupport(false), false /* tainted */,
       TRAFFIC_ANNOTATION_FOR_TESTS, &url_loader_factory, net::IsolationInfo(),
-      /*devtools_observer=*/mojo::NullRemote(), net::NetLogWithSource());
+      /*devtools_observer=*/mojo::NullRemote(), net_log);
 
   preflight_controller.PerformPreflightCheck(
       base::BindOnce([](int, absl::optional<CorsErrorStatus>, bool) {}),
       request, WithTrustedHeaderClient(true),
       WithNonWildcardRequestHeadersSupport(false), false /* tainted */,
       TRAFFIC_ANNOTATION_FOR_TESTS, &url_loader_factory, net::IsolationInfo(),
-      /*devtools_observer=*/mojo::NullRemote(), net::NetLogWithSource());
+      /*devtools_observer=*/mojo::NullRemote(), net_log);
 
   ASSERT_EQ(2, url_loader_factory.NumPending());
   EXPECT_EQ(mojom::kURLLoadOptionAsCorsPreflight,
@@ -443,7 +447,9 @@
         request, WithTrustedHeaderClient(false),
         with_non_wildcard_request_headers_support_, tainted,
         TRAFFIC_ANNOTATION_FOR_TESTS, url_loader_factory_remote_.get(),
-        isolation_info, devtools_observer_->Bind(), net::NetLogWithSource());
+        isolation_info, devtools_observer_->Bind(),
+        net::NetLogWithSource::Make(net::NetLog::Get(),
+                                    net::NetLogSourceType::URL_REQUEST));
     run_loop_->Run();
   }
 
diff --git a/services/network/public/cpp/resource_request.cc b/services/network/public/cpp/resource_request.cc
index a24972b..da7f97c 100644
--- a/services/network/public/cpp/resource_request.cc
+++ b/services/network/public/cpp/resource_request.cc
@@ -8,6 +8,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/load_flags.h"
+#include "net/log/net_log_source.h"
 #include "services/network/public/mojom/cookie_access_observer.mojom.h"
 #include "services/network/public/mojom/devtools_observer.mojom.h"
 #include "services/network/public/mojom/url_request.mojom.h"
@@ -79,6 +80,13 @@
          (lhs && rhs && lhs->EqualsForTesting(*rhs));  // IN-TEST
 }
 
+bool OptionalNetLogInfoEqualsForTesting(
+    const absl::optional<net::NetLogSource>& lhs,
+    const absl::optional<net::NetLogSource>& rhs) {
+  bool equal_members = lhs && rhs && lhs.value() == rhs.value();
+  return (!lhs && !rhs) || equal_members;
+}
+
 base::debug::CrashKeyString* GetRequestUrlCrashKey() {
   static auto* crash_key = base::debug::AllocateCrashKeyString(
       "request_url", base::debug::CrashKeySize::Size256);
@@ -188,10 +196,6 @@
   return new_remote;
 }
 
-ResourceRequest::NetLogParams::NetLogParams() = default;
-ResourceRequest::NetLogParams::NetLogParams(uint32_t id) : source_id(id) {}
-ResourceRequest::NetLogParams::~NetLogParams() = default;
-
 ResourceRequest::ResourceRequest() = default;
 ResourceRequest::ResourceRequest(const ResourceRequest& request) = default;
 ResourceRequest::~ResourceRequest() = default;
@@ -254,6 +258,10 @@
          trust_token_params == request.trust_token_params &&
          OptionalWebBundleTokenParamsEqualsForTesting(  // IN-TEST
              web_bundle_token_params, request.web_bundle_token_params) &&
+         OptionalNetLogInfoEqualsForTesting(net_log_create_info,
+                                            request.net_log_create_info) &&
+         OptionalNetLogInfoEqualsForTesting(net_log_reference_info,
+                                            request.net_log_reference_info) &&
          target_ip_address_space == request.target_ip_address_space;
 }
 
diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h
index 2857fb52..4900119 100644
--- a/services/network/public/cpp/resource_request.h
+++ b/services/network/public/cpp/resource_request.h
@@ -18,6 +18,7 @@
 #include "net/cookies/site_for_cookies.h"
 #include "net/filter/source_stream.h"
 #include "net/http/http_request_headers.h"
+#include "net/log/net_log_source.h"
 #include "net/url_request/referrer_policy.h"
 #include "services/network/public/cpp/optional_trust_token_params.h"
 #include "services/network/public/cpp/resource_request_body.h"
@@ -101,16 +102,6 @@
     int32_t render_process_id = -1;
   };
 
-  // Typemapped to network.mojom.NetLogParams, see comments there for
-  // details of each field.
-  struct COMPONENT_EXPORT(NETWORK_CPP_BASE) NetLogParams {
-    NetLogParams();
-    explicit NetLogParams(uint32_t id);
-    ~NetLogParams();
-
-    uint32_t source_id;
-  };
-
   ResourceRequest();
   ResourceRequest(const ResourceRequest& request);
   ~ResourceRequest();
@@ -186,7 +177,8 @@
   // decoding any non-listed stream types.
   absl::optional<std::vector<net::SourceStream::SourceType>>
       devtools_accepted_stream_types;
-  absl::optional<NetLogParams> net_log_params;
+  absl::optional<net::NetLogSource> net_log_create_info;
+  absl::optional<net::NetLogSource> net_log_reference_info;
   mojom::IPAddressSpace target_ip_address_space =
       mojom::IPAddressSpace::kUnknown;
 };
diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc
index 6bd3913..a1c4a34 100644
--- a/services/network/public/cpp/url_request_mojom_traits.cc
+++ b/services/network/public/cpp/url_request_mojom_traits.cc
@@ -9,10 +9,13 @@
 #include "base/debug/dump_without_crashing.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
+#include "base/time/time.h"
 #include "mojo/public/cpp/base/file_mojom_traits.h"
 #include "mojo/public/cpp/base/file_path_mojom_traits.h"
 #include "mojo/public/cpp/base/time_mojom_traits.h"
 #include "mojo/public/cpp/base/unguessable_token_mojom_traits.h"
+#include "net/log/net_log_source.h"
+#include "net/log/net_log_source_type.h"
 #include "services/network/public/cpp/crash_keys.h"
 #include "services/network/public/cpp/http_request_headers_mojom_traits.h"
 #include "services/network/public/cpp/isolation_info_mojom_traits.h"
@@ -115,11 +118,19 @@
   return true;
 }
 
-bool StructTraits<network::mojom::NetLogParamsDataView,
-                  network::ResourceRequest::NetLogParams>::
-    Read(network::mojom::NetLogParamsDataView data,
-         network::ResourceRequest::NetLogParams* out) {
-  out->source_id = data.source_id();
+bool StructTraits<network::mojom::NetLogSourceDataView, net::NetLogSource>::
+    Read(network::mojom::NetLogSourceDataView data, net::NetLogSource* out) {
+  if (data.source_type() >=
+      static_cast<uint32_t>(net::NetLogSourceType::COUNT)) {
+    return false;
+  }
+  base::TimeTicks start_time;
+  if (!data.ReadStartTime(&start_time)) {
+    return false;
+  }
+  *out =
+      net::NetLogSource(static_cast<net::NetLogSourceType>(data.source_type()),
+                        data.source_id(), start_time);
   return true;
 }
 
@@ -168,7 +179,8 @@
       !data.ReadWebBundleTokenParams(&out->web_bundle_token_params) ||
       !data.ReadDevtoolsAcceptedStreamTypes(
           &out->devtools_accepted_stream_types) ||
-      !data.ReadNetLogParams(&out->net_log_params)) {
+      !data.ReadNetLogCreateInfo(&out->net_log_create_info) ||
+      !data.ReadNetLogReferenceInfo(&out->net_log_reference_info)) {
     // Note that data.ReadTrustTokenParams is temporarily handled below.
     return false;
   }
diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h
index 86e6558f..2d9ba69 100644
--- a/services/network/public/cpp/url_request_mojom_traits.h
+++ b/services/network/public/cpp/url_request_mojom_traits.h
@@ -141,15 +141,19 @@
 
 template <>
 struct COMPONENT_EXPORT(NETWORK_CPP_BASE)
-    StructTraits<network::mojom::NetLogParamsDataView,
-                 network::ResourceRequest::NetLogParams> {
-  static uint32_t source_id(
-      const network::ResourceRequest::NetLogParams& params) {
-    return params.source_id;
+    StructTraits<network::mojom::NetLogSourceDataView, net::NetLogSource> {
+  static uint32_t source_id(const net::NetLogSource& params) {
+    return params.id;
+  }
+  static uint32_t source_type(const net::NetLogSource& params) {
+    return static_cast<uint32_t>(params.type);
+  }
+  static base::TimeTicks start_time(const net::NetLogSource& params) {
+    return params.start_time;
   }
 
-  static bool Read(network::mojom::NetLogParamsDataView data,
-                   network::ResourceRequest::NetLogParams* out);
+  static bool Read(network::mojom::NetLogSourceDataView data,
+                   net::NetLogSource* out);
 };
 
 template <>
@@ -333,9 +337,13 @@
   web_bundle_token_params(const network::ResourceRequest& request) {
     return request.web_bundle_token_params;
   }
-  static const absl::optional<network::ResourceRequest::NetLogParams>&
-  net_log_params(const network::ResourceRequest& request) {
-    return request.net_log_params;
+  static const absl::optional<net::NetLogSource>& net_log_create_info(
+      const network::ResourceRequest& request) {
+    return request.net_log_create_info;
+  }
+  static const absl::optional<net::NetLogSource>& net_log_reference_info(
+      const network::ResourceRequest& request) {
+    return request.net_log_reference_info;
   }
   static network::mojom::IPAddressSpace target_ip_address_space(
       const network::ResourceRequest& request) {
diff --git a/services/network/public/cpp/url_request_mojom_traits_unittest.cc b/services/network/public/cpp/url_request_mojom_traits_unittest.cc
index 15acead..ec93331 100644
--- a/services/network/public/cpp/url_request_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/url_request_mojom_traits_unittest.cc
@@ -10,6 +10,9 @@
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "net/base/isolation_info.h"
 #include "net/filter/source_stream.h"
+#include "net/log/net_log.h"
+#include "net/log/net_log_source.h"
+#include "net/log/net_log_source_type.h"
 #include "net/url_request/referrer_policy.h"
 #include "services/network/public/cpp/http_request_headers_mojom_traits.h"
 #include "services/network/public/cpp/network_ipc_param_traits.h"
@@ -93,8 +96,10 @@
       absl::make_optional(ResourceRequest::WebBundleTokenParams(
           GURL("https://bundle.test/"), base::UnguessableToken::Create(),
           mojo::PendingRemote<network::mojom::WebBundleHandle>()));
-  original.net_log_params =
-      absl::make_optional(ResourceRequest::NetLogParams());
+  original.net_log_create_info = absl::make_optional(net::NetLogSource(
+      net::NetLogSourceType::URL_REQUEST, net::NetLog::Get()->NextID()));
+  original.net_log_reference_info = absl::make_optional(net::NetLogSource(
+      net::NetLogSourceType::URL_REQUEST, net::NetLog::Get()->NextID()));
   original.devtools_accepted_stream_types =
       std::vector<net::SourceStream::SourceType>(
           {net::SourceStream::SourceType::TYPE_BROTLI,
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index f7bd3d8..76d478f6 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -363,8 +363,8 @@
           cpp = "::network::ResourceRequest::WebBundleTokenParams"
         },
         {
-          mojom = "network.mojom.NetLogParams"
-          cpp = "::network::ResourceRequest::NetLogParams"
+          mojom = "network.mojom.NetLogSource"
+          cpp = "::net::NetLogSource"
         },
         {
           mojom = "network.mojom.URLRequest"
@@ -891,6 +891,7 @@
     ":url_loader_base",
     ":websocket_mojom",
     "//mojo/public/mojom/base",
+    "//sandbox/policy/mojom",
     "//services/proxy_resolver/public/mojom",
     "//url/mojom:url_mojom_gurl",
     "//url/mojom:url_mojom_origin",
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index 59fbbde6..a5cfff15 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -13,6 +13,7 @@
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/unguessable_token.mojom";
 import "mojo/public/mojom/base/values.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
 import "services/network/public/mojom/cookie_manager.mojom";
 import "services/network/public/mojom/host_resolver.mojom";
 import "services/network/public/mojom/http_raw_headers.mojom";
@@ -150,6 +151,9 @@
 //
 // This is a trusted interface that only the browser process should have access
 // to. It must not be sent to any untrusted process like a renderer process.
+// The network sandbox may be disabled (to kNoSandbox) by policy, and may not
+// be available on all platforms.
+[ServiceSandbox=sandbox.mojom.Sandbox.kNetwork]
 interface NetworkService {
   // Sets the parameters and initializes the service.
   SetParams(NetworkServiceParams params);
diff --git a/services/network/public/mojom/url_request.mojom b/services/network/public/mojom/url_request.mojom
index 6ad6136..571ecc5 100644
--- a/services/network/public/mojom/url_request.mojom
+++ b/services/network/public/mojom/url_request.mojom
@@ -99,13 +99,12 @@
 };
 
 // Options that may be set when URLRequests need to take over NetLog related
-// parameters from CorsURLLoader. The URLRequest will reuse these parameters
-// on logging. If set, contains parameters to construct a NetLogWithSource for
-// the request. If not set, URLRequest constructs a NetLogWitSource with
-// default parameters.
-struct NetLogParams {
-  // The ID of NetLogSource.
+// parameters. The URLRequest will use these parameters on logging.
+// Typemapped to net::NetLogSource.
+struct NetLogSource {
+  uint32 source_type;
   uint32 source_id;
+  mojo_base.mojom.TimeTicks start_time;
 };
 
 // Typemapped to network::ResourceRequest.
@@ -431,15 +430,23 @@
   // decoding any non-listed stream types.
   array<SourceType>? devtools_accepted_stream_types;
 
-  // This field is set within the network service, more concretely by
-  // CorsURLLoader, and solely used there unless the loader factory is
-  // overridden. Setting this field by the process outside the network service
-  // is currently not expected and is invalid.
-  // If set correctly, it contains NetLog related parameters. This value is
-  // filled and used when network service creates a new request for preflights
-  // (which happens in CorsURLLoader). See the comment of NetLogParams for
-  // details.
-  NetLogParams? net_log_params;
+  // This field is filled and used when network service creates a new request
+  // for preflights (which happens in CorsURLLoader). So this field is set
+  // within the network service, more concretely by CorsURLLoader, and solely
+  // used there unless the loader factory is overridden. Setting this field by
+  // the process outside the network service is currently not expected and is
+  // invalid.
+  // If set, it contains NetLog related parameters to construct a NetLogSource
+  // for the request. If not set, URLRequest constructs a NetLogSource with
+  // default parameters.
+  NetLogSource? net_log_create_info;
+
+  // This field is filled and used within network service. Setting this field
+  // by the process outside the network service is currently not expected and
+  // is invalid.
+  // If set, it contains NetLog related parameters to identify the initiator
+  // of the request in NetLog.
+  NetLogSource? net_log_reference_info;
 
   // The IP address space to which the target endpoint of this request should
   // belong, or `kUnknown` if there is no such requirement.
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 0fb3bda..d8af3893 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -44,6 +44,8 @@
 #include "net/cookies/static_cookie_policy.h"
 #include "net/dns/public/secure_dns_policy.h"
 #include "net/http/http_request_headers.h"
+#include "net/log/net_log_source_type.h"
+#include "net/log/net_log_with_source.h"
 #include "net/ssl/client_cert_store.h"
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "net/ssl/ssl_private_key.h"
@@ -542,10 +544,7 @@
       base::BindOnce(&URLLoader::OnMojoDisconnect, base::Unretained(this)));
   url_request_ = url_request_context_->CreateRequest(
       GURL(request.url), request.priority, this, traffic_annotation,
-      /*is_for_websockets=*/false,
-      request.net_log_params
-          ? absl::make_optional(request.net_log_params->source_id)
-          : absl::nullopt);
+      /*is_for_websockets=*/false, request.net_log_create_info);
 
   url_request_->set_method(request.method);
   url_request_->set_site_for_cookies(request.site_for_cookies);
@@ -653,6 +652,13 @@
         *factory_params_->top_frame_id, keepalive_request_size_);
   }
 
+  if (request.net_log_reference_info) {
+    // Log source object that created the request, if avairable.
+    url_request_->net_log().AddEventReferencingSource(
+        net::NetLogEventType::CREATED_BY,
+        request.net_log_reference_info.value());
+  }
+
 #if defined(OS_ANDROID)
   if (base::FeatureList::IsEnabled(features::kRecordRadioWakeupTrigger)) {
     RadioMonitorAndroid::GetInstance().MaybeRecordURLLoaderAnnotationId(
diff --git a/services/network/web_bundle/web_bundle_manager_unittest.cc b/services/network/web_bundle/web_bundle_manager_unittest.cc
index b2ca317..222b1c9 100644
--- a/services/network/web_bundle/web_bundle_manager_unittest.cc
+++ b/services/network/web_bundle/web_bundle_manager_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/unguessable_token.h"
-#include "components/web_package/test_support/web_bundle_builder.h"
+#include "components/web_package/web_bundle_builder.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
@@ -35,8 +35,7 @@
 const int32_t process_id2 = 200;
 
 std::string CreateSmallBundleString() {
-  web_package::test::WebBundleBuilder builder(kResourceUrl,
-                                              "" /* manifest_url */);
+  web_package::WebBundleBuilder builder(kResourceUrl, "" /* manifest_url */);
   builder.AddExchange(kResourceUrl,
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "body");
@@ -398,8 +397,7 @@
 
   // Create a not small size bundle to trigger the quota error while reading the
   // body of the subresource.
-  web_package::test::WebBundleBuilder builder(kResourceUrl,
-                                              "" /* manifest_url */);
+  web_package::WebBundleBuilder builder(kResourceUrl, "" /* manifest_url */);
   builder.AddExchange(kResourceUrl,
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       std::string(10000, 'X'));
diff --git a/services/network/web_bundle/web_bundle_url_loader_factory_unittest.cc b/services/network/web_bundle/web_bundle_url_loader_factory_unittest.cc
index 3598a87..b0d05e2 100644
--- a/services/network/web_bundle/web_bundle_url_loader_factory_unittest.cc
+++ b/services/network/web_bundle/web_bundle_url_loader_factory_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/callback_helpers.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
-#include "components/web_package/test_support/web_bundle_builder.h"
+#include "components/web_package/web_bundle_builder.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
@@ -45,8 +45,7 @@
 using ::testing::Pointee;
 
 std::vector<uint8_t> CreateSmallBundle() {
-  web_package::test::WebBundleBuilder builder(kResourceUrl,
-                                              "" /* manifest_url */);
+  web_package::WebBundleBuilder builder(kResourceUrl, "" /* manifest_url */);
   builder.AddExchange(kResourceUrl,
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "body");
@@ -54,8 +53,7 @@
 }
 
 std::vector<uint8_t> CreateLargeBundle() {
-  web_package::test::WebBundleBuilder builder(kResourceUrl,
-                                              "" /* manifest_url */);
+  web_package::WebBundleBuilder builder(kResourceUrl, "" /* manifest_url */);
   builder.AddExchange(kResourceUrl,
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "body");
@@ -69,8 +67,8 @@
 }
 
 std::vector<uint8_t> CreateCrossOriginBundle() {
-  web_package::test::WebBundleBuilder builder(kCrossOriginJsonUrl,
-                                              "" /* manifest_url */);
+  web_package::WebBundleBuilder builder(kCrossOriginJsonUrl,
+                                        "" /* manifest_url */);
   builder.AddExchange(
       kCrossOriginJsonUrl,
       {{":status", "200"}, {"content-type", "application/json"}},
@@ -304,8 +302,7 @@
 }
 
 TEST_F(WebBundleURLLoaderFactoryTest, ResponseParseError) {
-  web_package::test::WebBundleBuilder builder(kResourceUrl,
-                                              "" /* manifest_url */);
+  web_package::WebBundleBuilder builder(kResourceUrl, "" /* manifest_url */);
   // An invalid response.
   builder.AddExchange(kResourceUrl, {{":status", "0"}}, "body");
   WriteBundle(builder.CreateBundle());
@@ -357,8 +354,7 @@
 }
 
 TEST_F(WebBundleURLLoaderFactoryTest, RedirectResponseIsNotAllowed) {
-  web_package::test::WebBundleBuilder builder(kResourceUrl,
-                                              "" /* manifest_url */);
+  web_package::WebBundleBuilder builder(kResourceUrl, "" /* manifest_url */);
   builder.AddExchange(kResourceUrl,
                       {{":status", "301"}, {"location", kResourceUrl2}}, "");
   builder.AddExchange(kResourceUrl2,
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 3b59a117..de24201 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -5782,7 +5782,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M95",
-              "revision": "version:95.0.4638.47"
+              "revision": "version:95.0.4638.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -5869,7 +5869,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M96",
-              "revision": "version:96.0.4664.5"
+              "revision": "version:96.0.4664.6"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -6043,7 +6043,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M95",
-              "revision": "version:95.0.4638.47"
+              "revision": "version:95.0.4638.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -6130,7 +6130,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M96",
-              "revision": "version:96.0.4664.5"
+              "revision": "version:96.0.4664.6"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 76ee7126..3ee72f4 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -58019,7 +58019,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M95",
-              "revision": "version:95.0.4638.47"
+              "revision": "version:95.0.4638.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -58107,7 +58107,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M96",
-              "revision": "version:96.0.4664.5"
+              "revision": "version:96.0.4664.6"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -58283,7 +58283,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M95",
-              "revision": "version:95.0.4638.47"
+              "revision": "version:95.0.4638.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -58371,7 +58371,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M96",
-              "revision": "version:96.0.4664.5"
+              "revision": "version:96.0.4664.6"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -58620,7 +58620,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M95",
-              "revision": "version:95.0.4638.47"
+              "revision": "version:95.0.4638.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -58707,7 +58707,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M96",
-              "revision": "version:96.0.4664.5"
+              "revision": "version:96.0.4664.6"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -58881,7 +58881,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M95",
-              "revision": "version:95.0.4638.47"
+              "revision": "version:95.0.4638.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -58968,7 +58968,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M96",
-              "revision": "version:96.0.4664.5"
+              "revision": "version:96.0.4664.6"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -59217,7 +59217,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M95",
-              "revision": "version:95.0.4638.47"
+              "revision": "version:95.0.4638.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -59304,7 +59304,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M96",
-              "revision": "version:96.0.4664.5"
+              "revision": "version:96.0.4664.6"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -59478,7 +59478,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M95",
-              "revision": "version:95.0.4638.47"
+              "revision": "version:95.0.4638.48"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -59565,7 +59565,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M96",
-              "revision": "version:96.0.4664.5"
+              "revision": "version:96.0.4664.6"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 69d05b6..470771d 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5898,21 +5898,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 97.0.4666.0",
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 97.0.4667.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v97.0.4666.0",
-              "revision": "version:97.0.4666.0"
+              "location": "lacros_version_skew_tests_v97.0.4667.0",
+              "revision": "version:97.0.4667.0"
             }
           ],
           "dimension_sets": [
@@ -6006,21 +6006,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 97.0.4666.0",
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 97.0.4667.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v97.0.4666.0",
-              "revision": "version:97.0.4666.0"
+              "location": "lacros_version_skew_tests_v97.0.4667.0",
+              "revision": "version:97.0.4667.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.dev.json b/testing/buildbot/chromium.dev.json
index 2da8f7c..c0d4542 100644
--- a/testing/buildbot/chromium.dev.json
+++ b/testing/buildbot/chromium.dev.json
@@ -709,6 +709,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
+              "cores": "8|12",
               "cpu": "x86-64",
               "os": "Mac-10.15"
             }
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 461f2354..22e26823 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -91676,7 +91676,7 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "isolate_profile_data": true,
@@ -91684,14 +91684,14 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 97.0.4666.0",
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 97.0.4667.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v97.0.4666.0",
-              "revision": "version:97.0.4666.0"
+              "location": "lacros_version_skew_tests_v97.0.4667.0",
+              "revision": "version:97.0.4667.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -91768,7 +91768,7 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "isolate_profile_data": true,
@@ -91776,14 +91776,14 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 97.0.4666.0",
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 97.0.4667.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v97.0.4666.0",
-              "revision": "version:97.0.4666.0"
+              "location": "lacros_version_skew_tests_v97.0.4667.0",
+              "revision": "version:97.0.4667.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -93161,21 +93161,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 97.0.4666.0",
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 97.0.4667.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v97.0.4666.0",
-              "revision": "version:97.0.4666.0"
+              "location": "lacros_version_skew_tests_v97.0.4667.0",
+              "revision": "version:97.0.4667.0"
             }
           ],
           "dimension_sets": [
@@ -93273,21 +93273,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 97.0.4666.0",
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 97.0.4667.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v97.0.4666.0",
-              "revision": "version:97.0.4666.0"
+              "location": "lacros_version_skew_tests_v97.0.4667.0",
+              "revision": "version:97.0.4667.0"
             }
           ],
           "dimension_sets": [
@@ -94834,21 +94834,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 97.0.4666.0",
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 97.0.4667.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v97.0.4666.0",
-              "revision": "version:97.0.4666.0"
+              "location": "lacros_version_skew_tests_v97.0.4667.0",
+              "revision": "version:97.0.4667.0"
             }
           ],
           "dimension_sets": [
@@ -94946,21 +94946,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 97.0.4666.0",
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 97.0.4667.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v97.0.4666.0",
-              "revision": "version:97.0.4666.0"
+              "location": "lacros_version_skew_tests_v97.0.4667.0",
+              "revision": "version:97.0.4667.0"
             }
           ],
           "dimension_sets": [
@@ -95703,21 +95703,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 97.0.4666.0",
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 97.0.4667.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v97.0.4666.0",
-              "revision": "version:97.0.4666.0"
+              "location": "lacros_version_skew_tests_v97.0.4667.0",
+              "revision": "version:97.0.4667.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -95799,21 +95799,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 97.0.4666.0",
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 97.0.4667.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v97.0.4666.0",
-              "revision": "version:97.0.4666.0"
+              "location": "lacros_version_skew_tests_v97.0.4667.0",
+              "revision": "version:97.0.4667.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 01728ec..a53e748 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1340,6 +1340,17 @@
           '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.content_browsertests.filter',
         ],
       },
+      # https://crbug.com/1255940
+      'mac-rel-swarming': {
+        'swarming': {
+          'dimension_sets': [
+            {
+              # These test would time out when running on 4 cores instances.
+              'cores': '8|12',
+            }
+          ],
+        },
+      },
     },
   },
   'content_browsertests_wayland': {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index a1dbab1fe..37db846 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -52,16 +52,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4666.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v97.0.4667.0/test_ash_chrome',
       '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter',
     ],
-    'identifier': 'Lacros version skew testing ash 97.0.4666.0',
+    'identifier': 'Lacros version skew testing ash 97.0.4667.0',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v97.0.4666.0',
-          'revision': 'version:97.0.4666.0',
+          'location': 'lacros_version_skew_tests_v97.0.4667.0',
+          'revision': 'version:97.0.4667.0',
         },
       ],
     },
@@ -387,7 +387,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M96',
-          'revision': 'version:96.0.4664.5',
+          'revision': 'version:96.0.4664.6',
         }
       ],
     },
@@ -411,7 +411,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M95',
-          'revision': 'version:95.0.4638.47',
+          'revision': 'version:95.0.4638.48',
         }
       ],
     },
@@ -459,7 +459,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M96',
-          'revision': 'version:96.0.4664.5',
+          'revision': 'version:96.0.4664.6',
         }
       ],
     },
@@ -483,7 +483,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M95',
-          'revision': 'version:95.0.4638.47',
+          'revision': 'version:95.0.4638.48',
         }
       ],
     },
@@ -531,7 +531,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M96',
-          'revision': 'version:96.0.4664.5',
+          'revision': 'version:96.0.4664.6',
         }
       ],
     },
@@ -555,7 +555,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M95',
-          'revision': 'version:95.0.4638.47',
+          'revision': 'version:95.0.4638.48',
         }
       ],
     },
diff --git a/third_party/blink/public/devtools_protocol/OWNERS b/third_party/blink/public/devtools_protocol/OWNERS
index 30b2e7e7..cacd3c83 100644
--- a/third_party/blink/public/devtools_protocol/OWNERS
+++ b/third_party/blink/public/devtools_protocol/OWNERS
@@ -1,3 +1,4 @@
+alexrudenko@chromium.org
 caseq@chromium.org
 dgozman@chromium.org
 pfeldman@chromium.org
diff --git a/third_party/blink/renderer/core/css/cssom/css_rotate.cc b/third_party/blink/renderer/core/css/cssom/css_rotate.cc
index afb154a..86f5a82e 100644
--- a/third_party/blink/renderer/core/css/cssom/css_rotate.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_rotate.cc
@@ -163,11 +163,6 @@
 }
 
 const CSSFunctionValue* CSSRotate::ToCSSValue() const {
-  DCHECK(x_->to(CSSPrimitiveValue::UnitType::kNumber));
-  DCHECK(y_->to(CSSPrimitiveValue::UnitType::kNumber));
-  DCHECK(z_->to(CSSPrimitiveValue::UnitType::kNumber));
-  DCHECK(angle_->to(CSSPrimitiveValue::UnitType::kRadians));
-
   CSSFunctionValue* result = MakeGarbageCollected<CSSFunctionValue>(
       is2D() ? CSSValueID::kRotate : CSSValueID::kRotate3d);
   if (!is2D()) {
@@ -186,6 +181,11 @@
   if (!angle)
     return nullptr;
 
+  DCHECK(x_->to(CSSPrimitiveValue::UnitType::kNumber));
+  DCHECK(y_->to(CSSPrimitiveValue::UnitType::kNumber));
+  DCHECK(z_->to(CSSPrimitiveValue::UnitType::kNumber));
+  DCHECK(angle_->to(CSSPrimitiveValue::UnitType::kRadians));
+
   result->Append(*angle);
   return result;
 }
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
index 8d7186a..161fb0a 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -854,8 +854,8 @@
 
   // Ensure the scroll contents size matches the frame view's size.
   EXPECT_EQ(gfx::Size(320, 240), visual_viewport.LayerForScrolling()->bounds());
-  EXPECT_EQ(gfx::Size(320, 240),
-            visual_viewport.GetScrollNode()->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 320, 240),
+            visual_viewport.GetScrollNode()->ContentsRect());
 
   // Ensure the location and scale were reset.
   EXPECT_EQ(FloatSize(), visual_viewport.GetScrollOffset());
@@ -1720,7 +1720,7 @@
   float scale = GetFrame()->GetPage()->GetVisualViewport().Scale();
   EXPECT_EQ(gfx::Size(100 / scale, 150 / scale),
             scroll_node->ContainerRect().size());
-  EXPECT_EQ(gfx::Size(1500, 2400), scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 1500, 2400), scroll_node->ContentsRect());
 }
 
 // Tests that resizing the visual viepwort keeps its bounds within the outer
@@ -2517,20 +2517,20 @@
   VisualViewport& visual_viewport = WebView().GetPage()->GetVisualViewport();
   EXPECT_EQ(gfx::Size(320, 480), visual_viewport.LayerForScrolling()->bounds());
 
-  EXPECT_EQ(gfx::Size(400, 600),
-            visual_viewport.GetScrollNode()->ContainerRect().size());
-  EXPECT_EQ(gfx::Size(320, 480),
-            visual_viewport.GetScrollNode()->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 400, 600),
+            visual_viewport.GetScrollNode()->ContainerRect());
+  EXPECT_EQ(gfx::Rect(0, 0, 320, 480),
+            visual_viewport.GetScrollNode()->ContentsRect());
 
   WebView().MainFrameViewWidget()->ApplyViewportChangesForTesting(
       {gfx::Vector2dF(1, 1), gfx::Vector2dF(), 2, false, 1, 0,
        cc::BrowserControlsState::kBoth});
   EXPECT_EQ(gfx::Size(320, 480), visual_viewport.LayerForScrolling()->bounds());
 
-  EXPECT_EQ(gfx::Size(400, 600),
-            visual_viewport.GetScrollNode()->ContainerRect().size());
-  EXPECT_EQ(gfx::Size(320, 480),
-            visual_viewport.GetScrollNode()->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 400, 600),
+            visual_viewport.GetScrollNode()->ContainerRect());
+  EXPECT_EQ(gfx::Rect(0, 0, 320, 480),
+            visual_viewport.GetScrollNode()->ContentsRect());
 }
 
 class VisualViewportScrollIntoViewTest : public VisualViewportSimTest {
diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc
index b178e732..f8afaf0 100644
--- a/third_party/blink/renderer/core/frame/web_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_test.cc
@@ -2051,8 +2051,8 @@
   auto* scroll_node = visual_viewport.GetScrollTranslationNode()->ScrollNode();
   EXPECT_EQ(gfx::Rect(viewport_width, viewport_height),
             scroll_node->ContainerRect());
-  EXPECT_EQ(gfx::Size(viewport_width, viewport_height),
-            scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(viewport_width, viewport_height),
+            scroll_node->ContentsRect());
 }
 
 TEST_F(WebFrameTest, SetForceZeroLayoutHeight) {
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.cc b/third_party/blink/renderer/core/html/forms/file_input_type.cc
index 3b04500..71bd003 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.cc
@@ -126,7 +126,7 @@
   auto* file_list = MakeGarbageCollected<FileList>();
   for (const auto& file : file_vector)
     file_list->Append(file);
-  SetFilesAndDispatchEvents(file_list);
+  SetFiles(file_list);
 }
 
 void FileInputType::AppendToFormData(FormData& form_data) const {
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 1bbd7b6..b7c229e4 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -22,6 +22,7 @@
 
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
 #include "services/network/public/mojom/content_security_policy.mojom-blink-forward.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
@@ -196,12 +197,26 @@
   return allowed_websites;
 }
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class AutomaticLazyLoadFrame {
+  kFeatureNotEnabled = 0,
+  kTargetFramesNotFound = 1,
+  kTargetFramesFound = 2,
+  kMaxValue = kTargetFramesFound,
+};
+
 // Checks if the passed `url` is in the allowlist for automatic lazy-loading.
 // Returns true if the feature flag is enabled and the url is in the list.
 bool IsLazyLoadableUrl(KURL url) {
+  constexpr const char kAutomaticLazyLoadFrameHistogram[] =
+      "Blink.AutomaticLazyLoadFrame";
   if (!base::FeatureList::IsEnabled(
-          features::kAutomaticLazyFrameLoadingToEmbeds))
+          features::kAutomaticLazyFrameLoadingToEmbeds)) {
+    base::UmaHistogramEnumeration(kAutomaticLazyLoadFrameHistogram,
+                                  AutomaticLazyLoadFrame::kFeatureNotEnabled);
     return false;
+  }
 
   scoped_refptr<const SecurityOrigin> origin = SecurityOrigin::Create(url);
   for (const auto& it : AllowedWebsitesForLazyLoading()) {
@@ -214,10 +229,17 @@
     // test. That will affect the test reliability.
     if ((origin.get()->Protocol() == it.first->Protocol() &&
          origin.get()->Host() == it.first->Host()) &&
-        (url.GetPath().Contains(it.second) || url.Query().Contains(it.second)))
+        (url.GetPath().Contains(it.second) ||
+         url.Query().Contains(it.second))) {
+      base::UmaHistogramEnumeration(kAutomaticLazyLoadFrameHistogram,
+                                    AutomaticLazyLoadFrame::kTargetFramesFound);
       return true;
+    }
   }
 
+  base::UmaHistogramEnumeration(kAutomaticLazyLoadFrameHistogram,
+                                AutomaticLazyLoadFrame::kTargetFramesNotFound);
+
   return false;
 }
 
diff --git a/third_party/blink/renderer/core/html/html_marquee_element.cc b/third_party/blink/renderer/core/html/html_marquee_element.cc
index a6be9d67..af29bf4f 100644
--- a/third_party/blink/renderer/core/html/html_marquee_element.cc
+++ b/third_party/blink/renderer/core/html/html_marquee_element.cc
@@ -56,7 +56,7 @@
 HTMLMarqueeElement::HTMLMarqueeElement(Document& document)
     : HTMLElement(html_names::kMarqueeTag, document) {
   UseCounter::Count(document, WebFeature::kHTMLMarqueeElement);
-  EnsureUserAgentShadowRoot();
+  EnsureUserAgentShadowRoot().EnableNameBasedSlotAssignment();
 }
 
 void HTMLMarqueeElement::DidAddUserAgentShadowRoot(ShadowRoot& shadow_root) {
@@ -73,8 +73,7 @@
   auto* mover = MakeGarbageCollected<HTMLDivElement>(GetDocument());
   shadow_root.AppendChild(mover);
 
-  mover->AppendChild(
-      HTMLSlotElement::CreateUserAgentDefaultSlot(GetDocument()));
+  mover->AppendChild(MakeGarbageCollected<HTMLSlotElement>(GetDocument()));
   mover_ = mover;
 }
 
diff --git a/third_party/blink/renderer/core/html/html_meter_element.cc b/third_party/blink/renderer/core/html/html_meter_element.cc
index 3349032..b8a24f34 100644
--- a/third_party/blink/renderer/core/html/html_meter_element.cc
+++ b/third_party/blink/renderer/core/html/html_meter_element.cc
@@ -40,7 +40,7 @@
 HTMLMeterElement::HTMLMeterElement(Document& document)
     : HTMLElement(html_names::kMeterTag, document) {
   UseCounter::Count(document, WebFeature::kMeterElement);
-  EnsureUserAgentShadowRoot();
+  EnsureUserAgentShadowRoot().EnableNameBasedSlotAssignment();
 }
 
 HTMLMeterElement::~HTMLMeterElement() = default;
@@ -191,8 +191,7 @@
   inner->AppendChild(bar);
 
   auto* fallback = MakeGarbageCollected<HTMLDivElement>(GetDocument());
-  fallback->AppendChild(
-      HTMLSlotElement::CreateUserAgentDefaultSlot(GetDocument()));
+  fallback->AppendChild(MakeGarbageCollected<HTMLSlotElement>(GetDocument()));
   fallback->SetShadowPseudoId(AtomicString("-internal-fallback"));
   root.AppendChild(fallback);
 }
diff --git a/third_party/blink/renderer/core/layout/layout_tree_as_text.cc b/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
index aee7d50..edb3f74 100644
--- a/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
@@ -459,7 +459,7 @@
       ts << " state=(" << fragment->LocalBorderBoxProperties().ToString()
          << ")";
     }
-    if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
+    if (RuntimeEnabledFeatures::CullRectUpdateEnabled() && o.HasLayer()) {
       ts << " cull_rect=(" << fragment->GetCullRect().ToString()
          << ") contents_cull_rect=("
          << fragment->GetContentsCullRect().ToString() << ")";
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc
index 8ce470ec..0960068 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc
@@ -357,8 +357,8 @@
       line[new_index].rect.offset.inline_offset +=
           line[index_right].inline_size - line[new_index].inline_size;
       PlaceEllipsisNextTo(line_box, &line[new_index]);
-      available_width_left +=
-          available_width_right - line[new_index].inline_size;
+      available_width_left += available_width_right -
+                              line[new_index].inline_size.ClampNegativeToZero();
     }
     LayoutUnit ellipsis_offset = line[line.size() - 1].InlineOffset();
 
diff --git a/third_party/blink/renderer/core/paint/box_painter_test.cc b/third_party/blink/renderer/core/paint/box_painter_test.cc
index e423f58..53ca4ec 100644
--- a/third_party/blink/renderer/core/paint/box_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/box_painter_test.cc
@@ -287,7 +287,7 @@
   const auto& contents_transform =
       ToUnaliased(contents_chunk.properties.Transform());
   const auto* contents_scroll = contents_transform.ScrollNode();
-  EXPECT_EQ(gfx::Size(200, 300), contents_scroll->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 200, 300), contents_scroll->ContentsRect());
   EXPECT_EQ(gfx::Rect(0, 0, 200, 200), contents_scroll->ContainerRect());
   const auto& contents_clip = ToUnaliased(contents_chunk.properties.Clip());
   EXPECT_EQ(FloatRect(0, 0, 200, 200), contents_clip.PaintClipRect().Rect());
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index bcd4091..152192f 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -221,7 +221,7 @@
   const auto* scroll = scroller_properties->ScrollTranslation()->ScrollNode();
   EXPECT_EQ(DocScroll(), scroll->Parent());
   EXPECT_EQ(gfx::Rect(0, 0, 413, 317), scroll->ContainerRect());
-  EXPECT_EQ(gfx::Size(660, 10200), scroll->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 660, 10200), scroll->ContentsRect());
   EXPECT_FALSE(scroll->UserScrollableHorizontal());
   EXPECT_TRUE(scroll->UserScrollableVertical());
   EXPECT_EQ(gfx::Vector2dF(120, 340),
@@ -397,7 +397,7 @@
   EXPECT_EQ(scroll, scroll_translation->ScrollNode());
   // 10: border width. 85: container client size (== 100 - scrollbar width).
   EXPECT_EQ(gfx::Rect(10, 10, 85, 85), scroll->ContainerRect());
-  EXPECT_EQ(gfx::Size(400, 400), scroll->ContentsSize());
+  EXPECT_EQ(gfx::Rect(10, 10, 400, 400), scroll->ContentsRect());
   EXPECT_EQ(PhysicalOffset(), scroller->FirstFragment().PaintOffset());
   EXPECT_EQ(IntPoint(315, 0), scroller->ScrollOrigin());
   EXPECT_EQ(PhysicalOffset(10, 10), content->FirstFragment().PaintOffset());
@@ -416,7 +416,7 @@
   // Other properties are the same as before.
   EXPECT_EQ(scroll, scroll_translation->ScrollNode());
   EXPECT_EQ(gfx::Rect(10, 10, 85, 85), scroll->ContainerRect());
-  EXPECT_EQ(gfx::Size(400, 400), scroll->ContentsSize());
+  EXPECT_EQ(gfx::Rect(10, 10, 400, 400), scroll->ContentsRect());
   EXPECT_EQ(PhysicalOffset(), scroller->FirstFragment().PaintOffset());
   EXPECT_EQ(IntPoint(315, 0), scroller->ScrollOrigin());
   EXPECT_EQ(PhysicalOffset(10, 10), content->FirstFragment().PaintOffset());
@@ -450,7 +450,7 @@
   // 25: border width (10) + scrollbar (on the left) width (15).
   // 85: container client size (== 100 - scrollbar width).
   EXPECT_EQ(gfx::Rect(25, 10, 85, 85), scroll->ContainerRect());
-  EXPECT_EQ(gfx::Size(400, 400), scroll->ContentsSize());
+  EXPECT_EQ(gfx::Rect(25, 10, 400, 400), scroll->ContentsRect());
   EXPECT_EQ(PhysicalOffset(), scroller->FirstFragment().PaintOffset());
   EXPECT_EQ(IntPoint(315, 0), scroller->ScrollOrigin());
   EXPECT_EQ(PhysicalOffset(25, 10), content->FirstFragment().PaintOffset());
@@ -469,7 +469,7 @@
   // Other properties are the same as before.
   EXPECT_EQ(scroll, scroll_translation->ScrollNode());
   EXPECT_EQ(gfx::Rect(25, 10, 85, 85), scroll->ContainerRect());
-  EXPECT_EQ(gfx::Size(400, 400), scroll->ContentsSize());
+  EXPECT_EQ(gfx::Rect(25, 10, 400, 400), scroll->ContentsRect());
   EXPECT_EQ(PhysicalOffset(), scroller->FirstFragment().PaintOffset());
   EXPECT_EQ(IntPoint(315, 0), scroller->ScrollOrigin());
   EXPECT_EQ(PhysicalOffset(25, 10), content->FirstFragment().PaintOffset());
@@ -3984,7 +3984,7 @@
   EXPECT_EQ(gfx::Rect(0, 0, 5, 3), overflow_a_scroll_node->ContainerRect());
   // 107 is the forceScroll element plus the height of the overflow scroll child
   // (overflowB).
-  EXPECT_EQ(gfx::Size(9, 107), overflow_a_scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 9, 107), overflow_a_scroll_node->ContentsRect());
   EXPECT_TRUE(overflow_a_scroll_node->UserScrollableHorizontal());
   EXPECT_TRUE(overflow_a_scroll_node->UserScrollableVertical());
 
@@ -3998,7 +3998,7 @@
   EXPECT_EQ(overflow_a_scroll_node, overflow_b_scroll_node->Parent());
   EXPECT_EQ(gfx::Vector2dF(0, -41), scroll_b_translation->Translation2D());
   EXPECT_EQ(gfx::Rect(0, 0, 9, 7), overflow_b_scroll_node->ContainerRect());
-  EXPECT_EQ(gfx::Size(9, 100), overflow_b_scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 9, 100), overflow_b_scroll_node->ContentsRect());
   EXPECT_TRUE(overflow_b_scroll_node->UserScrollableHorizontal());
   EXPECT_TRUE(overflow_b_scroll_node->UserScrollableVertical());
 }
@@ -4070,7 +4070,7 @@
   // The height should be 4000px because the (dom-order) overflow children are
   // positioned and do not contribute to the height. Only the 4000px
   // "forceScroll" height is present.
-  EXPECT_EQ(gfx::Size(5, 4000), overflow_scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 5, 4000), overflow_scroll_node->ContentsRect());
 
   const ObjectPaintProperties* abspos_overflow_scroll_properties =
       abspos_overflow->GetLayoutObject()->FirstFragment().PaintProperties();
@@ -4083,7 +4083,8 @@
   EXPECT_EQ(gfx::Vector2dF(0, -41), abspos_scroll_translation->Translation2D());
   EXPECT_EQ(gfx::Rect(0, 0, 9, 7),
             abspos_overflow_scroll_node->ContainerRect());
-  EXPECT_EQ(gfx::Size(9, 4000), abspos_overflow_scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 9, 4000),
+            abspos_overflow_scroll_node->ContentsRect());
 
   const ObjectPaintProperties* fixed_overflow_scroll_properties =
       fixed_overflow->GetLayoutObject()->FirstFragment().PaintProperties();
@@ -4096,7 +4097,8 @@
   EXPECT_EQ(gfx::Vector2dF(0, -43), fixed_scroll_translation->Translation2D());
   EXPECT_EQ(gfx::Rect(0, 0, 13, 11),
             fixed_overflow_scroll_node->ContainerRect());
-  EXPECT_EQ(gfx::Size(13, 4000), fixed_overflow_scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 13, 4000),
+            fixed_overflow_scroll_node->ContentsRect());
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, NestedPositionedScrollProperties) {
@@ -4152,7 +4154,7 @@
   EXPECT_EQ(gfx::Rect(0, 0, 20, 20), overflow_a_scroll_node->ContainerRect());
   // 100 is the forceScroll element's height because the overflow child does not
   // contribute to the height.
-  EXPECT_EQ(gfx::Size(20, 100), overflow_a_scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 20, 100), overflow_a_scroll_node->ContentsRect());
   EXPECT_TRUE(overflow_a_scroll_node->UserScrollableHorizontal());
   EXPECT_TRUE(overflow_a_scroll_node->UserScrollableVertical());
 
@@ -4166,7 +4168,7 @@
   EXPECT_EQ(overflow_a_scroll_node, overflow_b_scroll_node->Parent());
   EXPECT_EQ(gfx::Vector2dF(0, -41), scroll_b_translation->Translation2D());
   EXPECT_EQ(gfx::Rect(0, 0, 5, 3), overflow_b_scroll_node->ContainerRect());
-  EXPECT_EQ(gfx::Size(5, 100), overflow_b_scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 5, 100), overflow_b_scroll_node->ContentsRect());
   EXPECT_TRUE(overflow_b_scroll_node->UserScrollableHorizontal());
   EXPECT_TRUE(overflow_b_scroll_node->UserScrollableVertical());
 }
@@ -6147,7 +6149,7 @@
   const auto* properties = PaintPropertiesForElement("scroller");
   const auto* scroll_node = properties->ScrollTranslation()->ScrollNode();
   EXPECT_EQ(gfx::Rect(0, 0, 200, 200), scroll_node->ContainerRect());
-  EXPECT_EQ(gfx::Size(1000, 200), scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 1000, 200), scroll_node->ContentsRect());
 }
 
 TEST_P(PaintPropertyTreeBuilderTest,
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
index ce64aa5..138917c 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -793,7 +793,7 @@
                           ->ScrollTranslation()
                           ->ScrollNode();
   EXPECT_EQ(gfx::Rect(0, 0, 100, 100), scroll_node->ContainerRect());
-  EXPECT_EQ(gfx::Size(200, 200), scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 200, 200), scroll_node->ContentsRect());
 
   GetDocument().getElementById("content")->setAttribute(
       html_names::kStyleAttr, "width: 200px; height: 300px");
@@ -803,7 +803,7 @@
                              ->ScrollTranslation()
                              ->ScrollNode());
   EXPECT_EQ(gfx::Rect(0, 0, 100, 100), scroll_node->ContainerRect());
-  EXPECT_EQ(gfx::Size(200, 300), scroll_node->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 200, 300), scroll_node->ContentsRect());
 }
 
 // The scrollbars are attached to the visual viewport but created by (and have
@@ -825,8 +825,8 @@
 
   EXPECT_EQ(gfx::Rect(0, 0, 800, 600),
             visual_viewport.GetScrollNode()->ContainerRect());
-  EXPECT_EQ(gfx::Size(800, 600),
-            visual_viewport.GetScrollNode()->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 800, 600),
+            visual_viewport.GetScrollNode()->ContentsRect());
 }
 
 TEST_P(PaintPropertyTreeUpdateTest, ViewportAddRemoveDeviceEmulationNode) {
diff --git a/third_party/blink/renderer/core/paint/view_painter_test.cc b/third_party/blink/renderer/core/paint/view_painter_test.cc
index 8c624b0..28675d1 100644
--- a/third_party/blink/renderer/core/paint/view_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/view_painter_test.cc
@@ -191,7 +191,7 @@
   const auto& contents_transform =
       ToUnaliased(contents_chunk.properties.Transform());
   const auto* contents_scroll = contents_transform.ScrollNode();
-  EXPECT_EQ(gfx::Size(800, 2000), contents_scroll->ContentsSize());
+  EXPECT_EQ(gfx::Rect(0, 0, 800, 2000), contents_scroll->ContentsRect());
   EXPECT_EQ(gfx::Rect(0, 0, 800, 600), contents_scroll->ContainerRect());
   const auto& contents_clip = ToUnaliased(contents_chunk.properties.Clip());
   EXPECT_EQ(FloatRect(0, 0, 800, 600), contents_clip.PaintClipRect().Rect());
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index bc24db3..9741678 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -1052,7 +1052,7 @@
                               const cc::ScrollNode& cc_scroll) {
   EXPECT_TRUE(cc_scroll.scrollable);
   EXPECT_EQ(blink_scroll.ContainerRect().size(), cc_scroll.container_bounds);
-  EXPECT_EQ(blink_scroll.ContentsSize(), cc_scroll.bounds);
+  EXPECT_EQ(blink_scroll.ContentsRect().size(), cc_scroll.bounds);
   EXPECT_EQ(blink_scroll.UserScrollableHorizontal(),
             cc_scroll.user_scrollable_horizontal);
   EXPECT_EQ(blink_scroll.UserScrollableVertical(),
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
index e10cef3..e9c878f1 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
@@ -861,7 +861,7 @@
        scroll_node; scroll_node = scroll_node->Parent()) {
     if (scroll_node->UserScrollableHorizontal() &&
         scroll_node->ContainerRect().width() <
-            scroll_node->ContentsSize().width()) {
+            scroll_node->ContentsRect().width()) {
       disable_cursor_control = TouchAction::kInternalPanXScrolls;
       break;
     }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
index 738e503..da48887 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
@@ -32,10 +32,8 @@
 absl::optional<gfx::RectF> VisibilityLimit(const PropertyTreeState& state) {
   if (&state.Clip().LocalTransformSpace() == &state.Transform())
     return ToGfxRectF(state.Clip().PaintClipRect().Rect());
-  if (const auto* scroll = state.Transform().ScrollNode()) {
-    return gfx::RectF(
-        gfx::Rect(scroll->ContainerRect().origin(), scroll->ContentsSize()));
-  }
+  if (const auto* scroll = state.Transform().ScrollNode())
+    return gfx::RectF(scroll->ContentsRect());
   return absl::nullopt;
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 14fba30..0616c41 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -588,7 +588,7 @@
   compositor_node.scrollable = true;
 
   compositor_node.container_bounds = scroll_node.ContainerRect().size();
-  compositor_node.bounds = scroll_node.ContentsSize();
+  compositor_node.bounds = scroll_node.ContentsRect().size();
   compositor_node.user_scrollable_horizontal =
       scroll_node.UserScrollableHorizontal();
   compositor_node.user_scrollable_vertical =
diff --git a/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc b/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
index 2b24010f..6aa820c 100644
--- a/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
@@ -96,14 +96,13 @@
 
   // We create scroll node for the root scroller even it's not scrollable.
   // Don't expand in the case.
-  if (scroll->ContainerRect().width() >= scroll->ContentsSize().width() &&
-      scroll->ContainerRect().height() >= scroll->ContentsSize().height())
+  gfx::Rect contents_rect = scroll->ContentsRect();
+  if (scroll->ContainerRect().width() >= contents_rect.width() &&
+      scroll->ContainerRect().height() >= contents_rect.height())
     return kNotExpanded;
 
   // Expand the cull rect for scrolling contents for composited scrolling.
   rect_.Outset(LocalPixelDistanceToExpand(root_transform, scroll_translation));
-  gfx::Rect contents_rect(scroll->ContainerRect().origin(),
-                          scroll->ContentsSize());
   rect_.Intersect(contents_rect);
   return rect_ == contents_rect ? kExpandedForWholeScrollingContents
                                 : kExpandedForPartialScrollingContents;
@@ -247,9 +246,7 @@
   bool expanded = false;
   if (last_scroll_translation_result == kExpandedForPartialScrollingContents) {
     DCHECK(last_transform->ScrollNode());
-    expansion_bounds.emplace(
-        last_transform->ScrollNode()->ContainerRect().origin(),
-        last_transform->ScrollNode()->ContentsSize());
+    expansion_bounds = last_transform->ScrollNode()->ContentsRect();
     if (last_transform != &destination.Transform() ||
         last_clip != &destination.Clip()) {
       // Map expansion_bounds in the same way as we did for rect_ in the last
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
index 4eed379..40b49c1 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
@@ -37,7 +37,7 @@
   // Calculate the max scroll offset and expand by that amount. The max scroll
   // offset is the contents size minus one viewport's worth of space (i.e. the
   // container rect size).
-  gfx::Size contents_size = node->ScrollNode()->ContentsSize();
+  gfx::Size contents_size = node->ScrollNode()->ContentsRect().size();
   gfx::Size container_size = node->ScrollNode()->ContainerRect().size();
   rect_to_map.Rect().Expand(FloatSize(contents_size - container_size));
 }
diff --git a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
index 3db1585..5fc58f2 100644
--- a/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h
@@ -132,13 +132,17 @@
   }
 
   // Rect of the container area that the contents scrolls in, in the space of
-  // the parent of the associated transform node (ScrollTranslation).
-  // It doesn't include non-overlay scrollbars. Overlay scrollbars do not affect
-  // the rect.
+  // the parent of the associated transform node, i.e. PaintOffsetTranslation
+  // which is the parent of ScrollTranslation. It doesn't include non-overlay
+  // scrollbars. Overlay scrollbars do not affect the rect.
   const gfx::Rect& ContainerRect() const { return state_.container_rect; }
 
-  // Size of the contents that is scrolled within the container rect.
-  const gfx::Size& ContentsSize() const { return state_.contents_size; }
+  // Rect of the contents that is scrolled within the container rect, in the
+  // space of the associated transform node (ScrollTranslation). It has the
+  // same origin as ContainerRect().
+  gfx::Rect ContentsRect() const {
+    return gfx::Rect(state_.container_rect.origin(), state_.contents_size);
+  }
 
   bool UserScrollableHorizontal() const {
     return state_.user_scrollable_horizontal;
diff --git a/third_party/blink/web_tests/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt b/third_party/blink/web_tests/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt
index a39a6a3..1462245 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/HTMLMeterElement/meter-element-markup-expected.txt
@@ -20,7 +20,6 @@
 |       pseudo="-internal-fallback"
 |       shadow:pseudoId="-internal-fallback"
 |       <slot>
-|         name="user-agent-default-slot"
 | "
     "
 | <meter>
@@ -45,7 +44,6 @@
 |       pseudo="-internal-fallback"
 |       shadow:pseudoId="-internal-fallback"
 |       <slot>
-|         name="user-agent-default-slot"
 | "
     "
 | <meter>
@@ -70,6 +68,5 @@
 |       pseudo="-internal-fallback"
 |       shadow:pseudoId="-internal-fallback"
 |       <slot>
-|         name="user-agent-default-slot"
 | "
   "
diff --git a/third_party/blink/web_tests/fast/forms/file/file-input-change-event-on-back-forward.html b/third_party/blink/web_tests/fast/forms/file/file-input-change-event-on-back-forward.html
new file mode 100644
index 0000000..d9a1f821
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/file/file-input-change-event-on-back-forward.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>Input and change events for file inputs</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://github.com/whatwg/html/issues/6853">
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="resources/file-drag-common.js"></script>
+
+<body onload="setTimeout(runTest, 0)">
+  <input type=file oninput=onInputHandler() onchange=onChangeHandler()>
+  <input id="been-here" value="start">
+  <form action="../../../resources/back.html" method="POST"></form>
+</body>
+
+<script>
+var seenOnInput = false;
+var seenOnChange = false;
+var restorationEventTest = async_test('File input restoration: should not fire input and change events');
+
+function onInputHandler() {
+  var beenHere = document.getElementById("been-here");
+  if (beenHere.value)
+    seenOnInput = true;
+}
+
+function onChangeHandler() {
+  var beenHere = document.getElementById("been-here");
+  if (beenHere.value)
+    seenOnChange = true;
+}
+
+function runTest() {
+  var beenHere = document.getElementById('been-here');
+  var fileInput = document.querySelector('input[type=file]');
+
+  if (beenHere.value == 'start') {
+    if (window.eventSender)
+      dragFilesOntoElement(fileInput, ['foo.txt']);
+    assert_equals(fileInput.value, 'C:\\fakepath\\foo.txt');
+    beenHere.value = 'visited';
+    var form = document.querySelector('form');
+    // Submit form in a timeout to make sure that we create a new back/forward list item.
+    setTimeout(function() {form.submit();}, 0);
+  } else {
+    restorationEventTest.step(function() {
+      assert_false(seenOnInput);
+      assert_false(seenOnChange);
+    });
+    restorationEventTest.done();
+  }
+}
+</script>
diff --git a/third_party/blink/web_tests/fast/forms/file/file-truncation-crash.html b/third_party/blink/web_tests/fast/forms/file/file-truncation-crash.html
index f14b6fc..b568bf4 100644
--- a/third_party/blink/web_tests/fast/forms/file/file-truncation-crash.html
+++ b/third_party/blink/web_tests/fast/forms/file/file-truncation-crash.html
@@ -2,6 +2,7 @@
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <input style="letter-spacing:-89920240; word-spacing:639549764px;" type="file">
+<input style="direction: rtl; letter-spacing:-89920240; word-spacing:639549764px;" type=file>
 <script>
 test(() => {}, 'Crash test for a negative inline_size item');
 </script>
diff --git a/third_party/blink/web_tests/shadow-dom/ua/meter-fallback-expected.html b/third_party/blink/web_tests/shadow-dom/ua/meter-fallback-expected.html
index e73ddb0..69e09ea 100644
--- a/third_party/blink/web_tests/shadow-dom/ua/meter-fallback-expected.html
+++ b/third_party/blink/web_tests/shadow-dom/ua/meter-fallback-expected.html
@@ -1,4 +1,4 @@
 <!DOCTYPE html>
-<meter style="-webkit-appearance: none">A<span>B</span><span>C</span></meter>
+<meter style="-webkit-appearance: none">A<span>C</span></meter>
 
 <div><meter style="-webkit-appearance: none"><span>X</span></meter>
diff --git a/third_party/closure_compiler/externs/terminal_private.js b/third_party/closure_compiler/externs/terminal_private.js
index d57249b6..57ac93c 100644
--- a/third_party/closure_compiler/externs/terminal_private.js
+++ b/third_party/closure_compiler/externs/terminal_private.js
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -7,7 +7,7 @@
 // NOTE: The format of types has changed. 'FooType' is now
 //   'chrome.terminalPrivate.FooType'.
 // Please run the closure compiler before committing changes.
-// See https://chromium.googlesource.com/chromium/src/+/master/docs/closure_compilation.md
+// See https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md
 
 /** @fileoverview Externs generated from namespace: terminalPrivate */
 
@@ -84,10 +84,11 @@
 
 /**
  * Open the Terminal tabbed window.
- * @param {function(): void} callback Callback that will be called when
- *     complete.
+ * @param {{
+ *   url: (string|undefined)
+ * }=} data
  */
-chrome.terminalPrivate.openWindow = function(callback) {};
+chrome.terminalPrivate.openWindow = function(data) {};
 
 /**
  * Open the Terminal Settings page.
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index 01cd541..cb186cf 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 413dedd90fbefd377417d7c5947a1d11c09220ae
+Revision: dd539703805b9c289f4990f42f710bd3a68c41f1
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/snapshot/linux/debug_rendezvous_test.cc b/third_party/crashpad/crashpad/snapshot/linux/debug_rendezvous_test.cc
index c77e862..4754528e 100644
--- a/third_party/crashpad/crashpad/snapshot/linux/debug_rendezvous_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/linux/debug_rendezvous_test.cc
@@ -162,7 +162,9 @@
     ASSERT_GE(possible_mappings->Count(), 1u);
 
     std::unique_ptr<ElfImageReader> module_reader;
+#if !defined(OS_ANDROID)
     const MemoryMap::Mapping* module_mapping = nullptr;
+#endif
     const MemoryMap::Mapping* mapping = nullptr;
     while ((mapping = possible_mappings->Next())) {
       auto parsed_module = std::make_unique<ElfImageReader>();
@@ -172,7 +174,9 @@
           parsed_module->GetDynamicArrayAddress(&dynamic_address) &&
           dynamic_address == module.dynamic_array) {
         module_reader = std::move(parsed_module);
+#if !defined(OS_ANDROID)
         module_mapping = mapping;
+#endif
         break;
       }
     }
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index 31867eb..da1b067 100644
--- a/third_party/nearby/README.chromium
+++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@
 Name: Nearby Connections Library
 Short Name: Nearby
 URL: https://github.com/google/nearby-connections
-Version: dceb1e216bdd22daf37f71026db3d93124841cd4
+Version: 51a19d24c1644cccdf33d56a48db4dad3e418447
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v2.xml b/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v2.xml
index 622ebea..bcf1afb5 100644
--- a/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v2.xml
+++ b/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v2.xml
@@ -38,7 +38,7 @@
     reset.
   </description>
 
-  <interface name="zcr_remote_shell_v2" version="1">
+  <interface name="zcr_remote_shell_v2" version="2">
     <description summary="remote_shell">
       The global interface that allows clients to turn a wl_surface into a
       "real window" which is remotely managed but can be stacked, activated
@@ -162,7 +162,7 @@
 
   </interface>
 
-  <interface name="zcr_remote_surface_v2" version="1">
+  <interface name="zcr_remote_surface_v2" version="2">
     <description summary="A desktop window">
       An interface that may be implemented by a wl_surface, for
       implementations that provide a desktop-style user interface
@@ -736,7 +736,7 @@
 
     <request name="set_resize_lock" since="1">
       <description summary="set resize lock state">
-          Enable the resize lock and put restrictions related to resizing on
+          [Deprecated] Enable the resize lock and put restrictions related to resizing on
           the shell surface.
 
           The resize lock state is double buffered, and will be applied at the
@@ -746,7 +746,7 @@
 
     <request name="unset_resize_lock" since="1">
       <description summary="unset resize lock state">
-          Disable the resize lock and allow the shell surface to be resized
+          [Deprecated] Disable the resize lock and allow the shell surface to be resized
           freely.
 
           The resize lock state is double buffered, and will be applied at the
@@ -806,6 +806,28 @@
       <arg name="width" type="int"/>
       <arg name="height" type="int"/>
     </request>
+
+    <!-- Version 2 additions -->
+
+    <enum name="resize_lock_type">
+      <description summary="resize lock type">
+  Resize lock type that can be used to put restrictions related to resizing.
+      </description>
+      <entry name="none" value="0" summary="follows normal resizeable policies"/>
+      <entry name="resize_enabled_togglable" value="1" summary="resizing is enabled and resize lock type is togglable" />
+      <entry name="resize_disabled_togglable" value="2" summary="resizing is disabled and resize lock type is togglable" />
+      <entry name="resize_disalbed_nontoggleable" value="3" summary="resizing is disabled and resize lock type is not togglable"/>
+    </enum>
+
+    <request name="set_resize_lock_type" since="2">
+      <description summary="set resize lock type">
+  Set resize lock type and put restrictions related to resizing on the shell surface.
+
+  The resize lock type is double buffered, and will be applied at the
+  time wl_surface.commit of the corresponding wl_surface is called.
+      </description>
+      <arg name="type" type="uint" enum="resize_lock_type" summary="resize lock type"/>
+    </request>
   </interface>
 
   <interface name="zcr_notification_surface_v2" version="1">
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 359dc35..2d9ab11 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -450,8 +450,8 @@
       'Chromium Android ARM 32-bit Goma RBE ToT (ATS)': 'android_release_bot_minimal_symbols',
       'Chromium Android ARM 32-bit Goma RBE Staging': 'android_release_bot_minimal_symbols',
 
-      'chromeos-amd64-generic-rel-goma-rbe-tot': 'chromeos_amd64-generic',
-      'chromeos-amd64-generic-rel-goma-rbe-staging': 'chromeos_amd64-generic',
+      'chromeos-amd64-generic-rel-goma-rbe-tot': 'chromeos_amd64-generic_use_fake_dbus_clients_vm_optimized',
+      'chromeos-amd64-generic-rel-goma-rbe-staging': 'chromeos_amd64-generic_use_fake_dbus_clients_vm_optimized',
     },
 
     'chromium.gpu': {
@@ -945,7 +945,7 @@
       # and two kevin bots when the PFQ has it enabled.
       'chromeos-amd64-generic-cfi-thin-lto-rel': 'chromeos_amd64-generic_cfi_thin_lto',
       'chromeos-amd64-generic-dbg': 'chromeos_amd64-generic_dbg',
-      'chromeos-amd64-generic-rel': 'chromeos_amd64-generic_use_fake_dbus_clients_vm_optimized',
+      'chromeos-amd64-generic-rel': 'chromeos_amd64-generic_use_fake_dbus_clients_vm_optimized_dchecks',
       'chromeos-amd64-generic-rel-dchecks': 'chromeos_amd64-generic_use_fake_dbus_clients_vm_optimized_dchecks',
       'chromeos-amd64-generic-rel-rts': 'chromeos_amd64-generic_use_fake_dbus_clients',
       'chromeos-arm-generic-dbg': 'chromeos_arm-generic_dbg',
@@ -1817,10 +1817,6 @@
       'cfi_full', 'cfi_icall', 'cfi_diag', 'thin_lto', 'release', 'static', 'dcheck_always_on', 'goma',
     ],
 
-    'chromeos_amd64-generic': [
-      'chromeos_amd64-generic',
-    ],
-
     'chromeos_amd64-generic_asan': [
       'chromeos_amd64-generic', 'asan',
     ],
diff --git a/tools/mb/mb_config_expectations/chromium.goma.json b/tools/mb/mb_config_expectations/chromium.goma.json
index 988acdc..28418f2 100644
--- a/tools/mb/mb_config_expectations/chromium.goma.json
+++ b/tools/mb/mb_config_expectations/chromium.goma.json
@@ -194,21 +194,23 @@
     }
   },
   "chromeos-amd64-generic-rel-goma-rbe-staging": {
-    "args_file": "//build/args/chromeos/amd64-generic.gni",
+    "args_file": "//build/args/chromeos/amd64-generic-vm.gni",
     "gn_args": {
       "dcheck_always_on": false,
       "is_chromeos_device": true,
       "ozone_platform_headless": true,
-      "use_goma": true
+      "use_goma": true,
+      "use_real_dbus_clients": false
     }
   },
   "chromeos-amd64-generic-rel-goma-rbe-tot": {
-    "args_file": "//build/args/chromeos/amd64-generic.gni",
+    "args_file": "//build/args/chromeos/amd64-generic-vm.gni",
     "gn_args": {
       "dcheck_always_on": false,
       "is_chromeos_device": true,
       "ozone_platform_headless": true,
-      "use_goma": true
+      "use_goma": true,
+      "use_real_dbus_clients": false
     }
   }
 }
\ No newline at end of file
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json b/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json
index 32cd214..e8b3b8c 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.chromiumos.json
@@ -24,7 +24,7 @@
   "chromeos-amd64-generic-rel": {
     "args_file": "//build/args/chromeos/amd64-generic-vm.gni",
     "gn_args": {
-      "dcheck_always_on": false,
+      "dcheck_always_on": true,
       "is_chromeos_device": true,
       "ozone_platform_headless": true,
       "use_goma": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5b351aa..0ca17d5f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3846,6 +3846,17 @@
   </int>
 </enum>
 
+<enum name="ArcImageCopyPasteCompatOperationType">
+  <summary>
+    Defines the combinations of operation types (copy-paste and drag-drop) and
+    sources (from browser and Files app) for ARC image copy-paste app compat.
+  </summary>
+  <int value="0" label="An image pasted from FilesApp"/>
+  <int value="1" label="An image dragged from FilesApp"/>
+  <int value="2" label="An image pasted from browser"/>
+  <int value="3" label="An image dragged from browser"/>
+</enum>
+
 <enum name="ArcIntentHandlerAction">
   <summary>Defines Arc intent handler actions</summary>
   <int value="0" label="Error after showing picker"/>
@@ -6953,6 +6964,12 @@
   <int value="3" label="[Deprecated] AUTO_LAUNCH_FOREGROUND_USELESS"/>
 </enum>
 
+<enum name="AutomaticLazyLoadFrameState">
+  <int value="0" label="Feature is not enabled"/>
+  <int value="1" label="Target frames not found"/>
+  <int value="2" label="Target frames found"/>
+</enum>
+
 <enum name="AutoplayBlockedReason">
   <int value="0" label="[Deprecated] Data Saver enabled"/>
   <int value="1" label="Disabled by setting"/>
@@ -11398,6 +11415,7 @@
   <int value="3" label="User selected the Tab Scrolling lab"/>
   <int value="4" label="User selected the Side Panel lab"/>
   <int value="5" label="User selected the Lens Region Search lab"/>
+  <int value="6" label="User selected the WebUI Tab Strip lab"/>
 </enum>
 
 <enum name="ChromeMLServiceDecisionTreePredictionResult">
@@ -19291,6 +19309,7 @@
   <int value="7" label="kDebugSubframeProxyCreationWithNoRVH"/>
   <int value="8" label="kDebugBackForwardCacheEntryExistsOnSubframeHistoryNav"/>
   <int value="9" label="kDebugNoRenderFrameProxyHostOnSetFocusedFrame"/>
+  <int value="10" label="kDebugNoRestoredRFHOnNonRestartedNavigation"/>
 </enum>
 
 <enum name="DeclarativeAPIFunctionType">
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index 201ccfb4..0e29700 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -913,6 +913,24 @@
   </summary>
 </histogram>
 
+<histogram name="Arc.ImageCopyPasteCompatOperationType"
+    enum="ArcImageCopyPasteCompatOperationType" expires_after="2022-04-01">
+  <owner>tetsui@chromium.org</owner>
+  <owner>yhanada@chromium.org</owner>
+  <summary>
+    The operation type (copy-paste or drag-drop) and the source of the image
+    (from browser or Files app), counted when image copy-paste app compat is
+    triggered. Image copy-paste app compat is a feature that allows insertion of
+    images to Android apps through commitContent IME API or Android intent, when
+    the app don't support images in the clipboard. For example, when a user
+    pastes an image from Files app to an Android app and the app compat logic
+    succeeds to insert the image, then that is one histogram count. If it fails
+    (for example the app does not implement commitContent API) or the app
+    already supports image pasting from the clipboard and the app compat logic
+    is not used, that will not be counted.
+  </summary>
+</histogram>
+
 <histogram name="Arc.ImeCount" units="units" expires_after="2022-04-01">
   <owner>yhanada@chromium.org</owner>
   <owner>tetsui@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 237cc34..e59bbb14 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -99,6 +99,22 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.AutomaticLazyLoadFrame"
+    enum="AutomaticLazyLoadFrameState" expires_after="2022-04-09">
+  <owner>sisidovski@google.com</owner>
+  <owner>kouhei@chromium.org</owner>
+  <summary>
+    Records if the automatic lazy-load for selected performance problematic
+    iframes is applied or not. This is recorded when each frame having a url in
+    the page is created. See HTMLFrameOwnerElement::LazyLoadIfPossible() for
+    more details.
+
+    Note: There are two groups that lazy-load isn't applied. One group is simply
+    the feature itself is disabled, another groupd is that feature is enabled,
+    but there are no targeted frames on the page.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Canvas.2DPrintingAsVector" enum="BooleanSuccess"
     expires_after="2022-01-31">
   <owner>fserb@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 563ac4c..7cb7a7cc 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,11 +6,11 @@
         },
         "win": {
             "hash": "52f421824816597171d49ad99769576b6eb8c1e8",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/a24cded09fce516821605181a032190110db2fd7/trace_processor_shell.exe"
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/a380238d9c232c8be236d141d0c81ca358a2597e/trace_processor_shell.exe"
         },
         "mac": {
             "hash": "c23e575ca7971342be4b254789513a3d9e75e19f",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/a24cded09fce516821605181a032190110db2fd7/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/a380238d9c232c8be236d141d0c81ca358a2597e/trace_processor_shell"
         },
         "linux_arm64": {
             "hash": "5074025a2898ec41a872e70a5719e417acb0a380",
diff --git a/ui/base/models/dialog_model.cc b/ui/base/models/dialog_model.cc
index 762a113..0404ac5 100644
--- a/ui/base/models/dialog_model.cc
+++ b/ui/base/models/dialog_model.cc
@@ -28,8 +28,8 @@
     base::OnceClosure callback,
     std::u16string label,
     const DialogModelButton::Params& params) {
-  DCHECK(!model_->accept_callback_);
-  model_->accept_callback_ = std::move(callback);
+  DCHECK(!model_->accept_action_callback_);
+  model_->accept_action_callback_ = std::move(callback);
   // NOTREACHED() is used below to make sure this callback isn't used.
   // DialogModelHost should be using OnDialogAccepted() instead.
   model_->ok_button_.emplace(
@@ -44,8 +44,8 @@
     base::OnceClosure callback,
     std::u16string label,
     const DialogModelButton::Params& params) {
-  DCHECK(!model_->cancel_callback_);
-  model_->cancel_callback_ = std::move(callback);
+  DCHECK(!model_->cancel_action_callback_);
+  model_->cancel_action_callback_ = std::move(callback);
   // NOTREACHED() is used below to make sure this callback isn't used.
   // DialogModelHost should be using OnDialogCanceled() instead.
   model_->cancel_button_.emplace(
@@ -136,24 +136,24 @@
   return GetFieldByUniqueId(unique_id)->AsTextfield();
 }
 
-void DialogModel::OnDialogAccepted(base::PassKey<DialogModelHost>) {
-  if (accept_callback_)
-    std::move(accept_callback_).Run();
+void DialogModel::OnDialogAcceptAction(base::PassKey<DialogModelHost>) {
+  if (accept_action_callback_)
+    std::move(accept_action_callback_).Run();
 }
 
-void DialogModel::OnDialogCancelled(base::PassKey<DialogModelHost>) {
-  if (cancel_callback_)
-    std::move(cancel_callback_).Run();
+void DialogModel::OnDialogCancelAction(base::PassKey<DialogModelHost>) {
+  if (cancel_action_callback_)
+    std::move(cancel_action_callback_).Run();
 }
 
-void DialogModel::OnDialogClosed(base::PassKey<DialogModelHost>) {
-  if (close_callback_)
-    std::move(close_callback_).Run();
+void DialogModel::OnDialogCloseAction(base::PassKey<DialogModelHost>) {
+  if (close_action_callback_)
+    std::move(close_action_callback_).Run();
 }
 
-void DialogModel::OnWindowClosing(base::PassKey<DialogModelHost>) {
-  if (window_closing_callback_)
-    std::move(window_closing_callback_).Run();
+void DialogModel::OnDialogDestroying(base::PassKey<DialogModelHost>) {
+  if (dialog_destroying_callback_)
+    std::move(dialog_destroying_callback_).Run();
 }
 
 void DialogModel::AddField(std::unique_ptr<DialogModelField> field) {
diff --git a/ui/base/models/dialog_model.h b/ui/base/models/dialog_model.h
index c2a6c90..8a8a7fd 100644
--- a/ui/base/models/dialog_model.h
+++ b/ui/base/models/dialog_model.h
@@ -151,17 +151,20 @@
 
     // Called when the dialog is explicitly closed (Esc, close-x). Not called
     // during accept/cancel.
-    Builder& SetCloseCallback(base::OnceClosure callback) {
-      model_->close_callback_ = std::move(callback);
+    Builder& SetCloseActionCallback(base::OnceClosure callback) {
+      model_->close_action_callback_ = std::move(callback);
       return *this;
     }
 
     // TODO(pbos): Clarify and enforce (through tests) that this is called after
     // {accept,cancel,close} callbacks.
-    // Unconditionally called when the dialog closes. Called on top of
-    // {accept,cancel,close} callbacks.
-    Builder& SetWindowClosingCallback(base::OnceClosure callback) {
-      model_->window_closing_callback_ = std::move(callback);
+    // Unconditionally called when the dialog destroys. Happens after
+    // user-action callbacks (accept, cancel, close), or as a result of dialog
+    // destruction. The latter can happen without a user action, for instance as
+    // a result of the OS destroying a native Widget in which this dialog is
+    // hosted.
+    Builder& SetDialogDestroyingCallback(base::OnceClosure callback) {
+      model_->dialog_destroying_callback_ = std::move(callback);
       return *this;
     }
 
@@ -278,10 +281,11 @@
 
   // Methods with base::PassKey<DialogModelHost> are only intended to be called
   // by the DialogModelHost implementation.
-  void OnDialogAccepted(base::PassKey<DialogModelHost>);
-  void OnDialogCancelled(base::PassKey<DialogModelHost>);
-  void OnDialogClosed(base::PassKey<DialogModelHost>);
-  void OnWindowClosing(base::PassKey<DialogModelHost>);
+  void OnDialogAcceptAction(base::PassKey<DialogModelHost>);
+  void OnDialogCancelAction(base::PassKey<DialogModelHost>);
+  void OnDialogCloseAction(base::PassKey<DialogModelHost>);
+
+  void OnDialogDestroying(base::PassKey<DialogModelHost>);
 
   // Called when added to a DialogModelHost.
   void set_host(base::PassKey<DialogModelHost>, DialogModelHost* host) {
@@ -360,11 +364,11 @@
   absl::optional<DialogModelButton> cancel_button_;
   absl::optional<DialogModelButton> extra_button_;
 
-  base::OnceClosure accept_callback_;
-  base::OnceClosure cancel_callback_;
-  base::OnceClosure close_callback_;
+  base::OnceClosure accept_action_callback_;
+  base::OnceClosure cancel_action_callback_;
+  base::OnceClosure close_action_callback_;
 
-  base::OnceClosure window_closing_callback_;
+  base::OnceClosure dialog_destroying_callback_;
 };
 
 }  // namespace ui
diff --git a/ui/base/models/dialog_model_unittest.cc b/ui/base/models/dialog_model_unittest.cc
index 4db911f..95625e48 100644
--- a/ui/base/models/dialog_model_unittest.cc
+++ b/ui/base/models/dialog_model_unittest.cc
@@ -126,7 +126,7 @@
     switch (button_id) {
       case kCancelButton:
         button = model->cancel_button(TestDialogModelHost::GetPassKey());
-        model->OnDialogCancelled(TestDialogModelHost::GetPassKey());
+        model->OnDialogCancelAction(TestDialogModelHost::GetPassKey());
         break;
       case kExtraButton:
         button = model->extra_button(TestDialogModelHost::GetPassKey());
@@ -135,7 +135,7 @@
         break;
       case kOkButton:
         button = model->ok_button(TestDialogModelHost::GetPassKey());
-        model->OnDialogAccepted(TestDialogModelHost::GetPassKey());
+        model->OnDialogAcceptAction(TestDialogModelHost::GetPassKey());
         break;
     }
     ASSERT_TRUE(button);
diff --git a/ui/ozone/common/features.cc b/ui/ozone/common/features.cc
index c2279f9..c4bd2ec 100644
--- a/ui/ozone/common/features.cc
+++ b/ui/ozone/common/features.cc
@@ -17,12 +17,25 @@
 #endif
 };
 
+// This feature flag enables a mode where the wayland client would submit
+// buffers at a scale of 1 and the server applies the respective scale transform
+// to properly composite the buffers. This mode is used to support fractional
+// scale factor.
+const base::Feature kWaylandSurfaceSubmissionInPixelCoordinates{
+    "WaylandSurfaceSubmissionInPixelCoordinates",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // This feature flag is used for fractional display scale factor development for
 // LaCros. When enabled, the wayland client would use the xdg output protocol to
 // receive extra output metrics (logical size) to calculate the scale factor.
 const base::Feature kXdgOutputProtocolSupport{
     "XdgOutputProtocolSupport", base::FEATURE_DISABLED_BY_DEFAULT};
 
+bool IsWaylandSurfaceSubmissionInPixelCoordinatesEnabled() {
+  return base::FeatureList::IsEnabled(
+      kWaylandSurfaceSubmissionInPixelCoordinates);
+}
+
 bool IsWaylandOverlayDelegationEnabled() {
   return base::FeatureList::IsEnabled(kWaylandOverlayDelegation);
 }
diff --git a/ui/ozone/common/features.h b/ui/ozone/common/features.h
index 2c1034d08..fe969af 100644
--- a/ui/ozone/common/features.h
+++ b/ui/ozone/common/features.h
@@ -9,9 +9,11 @@
 
 namespace ui {
 
+extern const base::Feature kWaylandSurfaceSubmissionInPixelCoordinates;
 extern const base::Feature kWaylandOverlayDelegation;
 extern const base::Feature kXdgOutputProtocolSupport;
 
+bool IsWaylandSurfaceSubmissionInPixelCoordinatesEnabled();
 bool IsWaylandOverlayDelegationEnabled();
 bool IsXdgOutputProtocolSupportEnabled();
 
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index cd00153e..b1e946e 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -156,6 +156,9 @@
   }
 #endif
 
+  surface_submission_in_pixel_coordinates_ =
+      IsWaylandSurfaceSubmissionInPixelCoordinatesEnabled();
+
   // Register factories for classes that implement wl::GlobalObjectRegistrar<T>.
   // Keep alphabetical order for convenience.
   RegisterGlobalObjectFactory(GtkPrimarySelectionDeviceManager::kInterfaceName,
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h
index 32d3aa9..f619fb1 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.h
+++ b/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -270,6 +270,9 @@
     return available_globals_;
   }
 
+  bool surface_submission_in_pixel_coordinates() const {
+    return surface_submission_in_pixel_coordinates_;
+  }
   wl::SerialTracker& serial_tracker() { return serial_tracker_; }
 
  private:
@@ -417,6 +420,12 @@
 
   bool scheduled_flush_ = false;
 
+  // Surfaces are submitted in pixel coordinates. Their buffer scales are always
+  // advertised to server as 1, and the scale via vp_viewporter won't be
+  // applied. The server will be responsible to scale the buffers to the right
+  // sizes.
+  bool surface_submission_in_pixel_coordinates_ = false;
+
   wl::SerialTracker serial_tracker_;
 
   // Global Wayland interfaces available in the current session, with their
diff --git a/ui/ozone/platform/wayland/host/wayland_event_source.cc b/ui/ozone/platform/wayland/host/wayland_event_source.cc
index 476c1ca0..4a24049 100644
--- a/ui/ozone/platform/wayland/host/wayland_event_source.cc
+++ b/ui/ozone/platform/wayland/host/wayland_event_source.cc
@@ -162,12 +162,15 @@
   pointer_location_ = location;
 
   bool focused = !!window;
-  if (focused)
+  if (focused) {
+    if (SurfaceSubmissionInPixelCoordinates())
+      pointer_location_.Scale(1.0f / window->window_scale());
     window_manager_->SetPointerFocusedWindow(window);
+  }
 
   EventType type = focused ? ET_MOUSE_ENTERED : ET_MOUSE_EXITED;
-  MouseEvent event(type, location, location, EventTimeForNow(), pointer_flags_,
-                   0);
+  MouseEvent event(type, pointer_location_, pointer_location_,
+                   EventTimeForNow(), pointer_flags_, 0);
   DispatchEvent(&event);
 
   if (!focused)
@@ -201,6 +204,13 @@
 
 void WaylandEventSource::OnPointerMotionEvent(const gfx::PointF& location) {
   pointer_location_ = location;
+
+  if (SurfaceSubmissionInPixelCoordinates()) {
+    if (WaylandWindow* window =
+            window_manager_->GetCurrentPointerFocusedWindow())
+      pointer_location_.Scale(1.0f / window->window_scale());
+  }
+
   int flags = pointer_flags_ | keyboard_modifiers_;
   MouseEvent event(ET_MOUSE_MOVED, pointer_location_, pointer_location_,
                    EventTimeForNow(), flags, 0);
@@ -299,16 +309,20 @@
   DCHECK(window);
   HandleTouchFocusChange(window, true);
 
+  gfx::PointF loc =
+      SurfaceSubmissionInPixelCoordinates()
+          ? gfx::ScalePoint(location, 1.f / window->window_scale())
+          : location;
   // Make sure this touch point wasn't present before.
-  auto success = touch_points_.try_emplace(
-      id, std::make_unique<TouchPoint>(location, window));
+  auto success =
+      touch_points_.try_emplace(id, std::make_unique<TouchPoint>(loc, window));
   if (!success.second) {
     LOG(WARNING) << "Touch down fired with wrong id";
     return;
   }
 
   PointerDetails details(EventPointerType::kTouch, id);
-  TouchEvent event(ET_TOUCH_PRESSED, location, location, timestamp, details);
+  TouchEvent event(ET_TOUCH_PRESSED, loc, loc, timestamp, details);
   DispatchEvent(&event);
 }
 
@@ -341,9 +355,14 @@
     LOG(WARNING) << "Touch event fired with wrong id";
     return;
   }
-  it->second->last_known_location = location;
+
+  gfx::PointF loc =
+      SurfaceSubmissionInPixelCoordinates()
+          ? gfx::ScalePoint(location, 1.f / it->second->window->window_scale())
+          : location;
+  it->second->last_known_location = loc;
   PointerDetails details(EventPointerType::kTouch, id);
-  TouchEvent event(ET_TOUCH_MOVED, location, location, timestamp, details);
+  TouchEvent event(ET_TOUCH_MOVED, loc, loc, timestamp, details);
   DispatchEvent(&event);
 }
 
@@ -502,4 +521,8 @@
                       : gfx::Vector2dF(dx * dt_inv, dy * dt_inv);
 }
 
+bool WaylandEventSource::SurfaceSubmissionInPixelCoordinates() const {
+  return connection_->surface_submission_in_pixel_coordinates();
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_event_source.h b/ui/ozone/platform/wayland/host/wayland_event_source.h
index da7fade7..32e85251 100644
--- a/ui/ozone/platform/wayland/host/wayland_event_source.h
+++ b/ui/ozone/platform/wayland/host/wayland_event_source.h
@@ -170,6 +170,7 @@
   // Computes initial velocity of fling scroll based on recent frames.
   gfx::Vector2dF ComputeFlingVelocity();
 
+  bool SurfaceSubmissionInPixelCoordinates() const;
   WaylandWindowManager* const window_manager_;
 
   WaylandConnection* const connection_;
diff --git a/ui/ozone/platform/wayland/host/wayland_surface.cc b/ui/ozone/platform/wayland/host/wayland_surface.cc
index d6928fc..ad11597 100644
--- a/ui/ozone/platform/wayland/host/wayland_surface.cc
+++ b/ui/ozone/platform/wayland/host/wayland_surface.cc
@@ -273,8 +273,10 @@
 
 void WaylandSurface::SetSurfaceBufferScale(int32_t scale) {
   DCHECK_GE(scale, 1);
-  wl_surface_set_buffer_scale(surface_.get(), scale);
-  buffer_scale_ = scale;
+  if (!SurfaceSubmissionInPixelCoordinates()) {
+    wl_surface_set_buffer_scale(surface_.get(), scale);
+    buffer_scale_ = scale;
+  }
   connection_->ScheduleFlush();
 }
 
@@ -315,6 +317,8 @@
 
   auto window_shape_in_dips = root_window_->GetWindowShape();
 
+  bool surface_submission_in_pixel_coordinates =
+      SurfaceSubmissionInPixelCoordinates();
   // Only root_surface and primary_subsurface should use |window_shape_in_dips|.
   // Do not use non empty |window_shape_in_dips| if |region_px| is empty, i.e.
   // this surface is transluscent.
@@ -326,15 +330,23 @@
       std::all_of(region_px.begin(), region_px.end(),
                   [](const gfx::Rect& rect) { return rect.IsEmpty(); });
   if (window_shape_in_dips.has_value() && !is_empty && is_primary_or_root) {
-    for (const auto& rect : window_shape_in_dips.value())
+    for (auto& rect : window_shape_in_dips.value()) {
+      if (surface_submission_in_pixel_coordinates)
+        rect = gfx::ScaleToEnclosingRect(rect, root_window_->window_scale());
       wl_region_add(region.get(), rect.x(), rect.y(), rect.width(),
                     rect.height());
+    }
   } else {
-    for (const auto& rect : region_px) {
-      gfx::Rect region_dip =
-          gfx::ScaleToEnclosingRect(rect, 1.f / root_window_->window_scale());
-      wl_region_add(region.get(), region_dip.x(), region_dip.y(),
-                    region_dip.width(), region_dip.height());
+    for (const auto& rect_px : region_px) {
+      if (surface_submission_in_pixel_coordinates) {
+        wl_region_add(region.get(), rect_px.x(), rect_px.y(), rect_px.width(),
+                      rect_px.height());
+      } else {
+        gfx::Rect rect = gfx::ScaleToEnclosingRect(
+            rect_px, 1.f / root_window_->window_scale());
+        wl_region_add(region.get(), rect.x(), rect.y(), rect.width(),
+                      rect.height());
+      }
     }
   }
   return region;
@@ -486,6 +498,10 @@
   }
 }
 
+bool WaylandSurface::SurfaceSubmissionInPixelCoordinates() const {
+  return connection_->surface_submission_in_pixel_coordinates();
+}
+
 // static
 void WaylandSurface::FencedRelease(
     void* data,
diff --git a/ui/ozone/platform/wayland/host/wayland_surface.h b/ui/ozone/platform/wayland/host/wayland_surface.h
index 5da93a8..f0bb6242 100644
--- a/ui/ozone/platform/wayland/host/wayland_surface.h
+++ b/ui/ozone/platform/wayland/host/wayland_surface.h
@@ -170,6 +170,7 @@
   wl::Object<wl_region> CreateAndAddRegion(
       const std::vector<gfx::Rect>& region_px);
 
+  bool SurfaceSubmissionInPixelCoordinates() const;
   // Creates (if not created) the synchronization surface and returns a pointer
   // to it.
   zwp_linux_surface_synchronization_v1* GetSurfaceSync();
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
index a9e72376..778b1f5 100644
--- a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
+++ b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
@@ -18,7 +18,7 @@
 namespace ui {
 
 namespace {
-constexpr uint32_t kMaxAuraShellVersion = 24;
+constexpr uint32_t kMaxAuraShellVersion = 26;
 }
 
 // static
@@ -46,11 +46,6 @@
   ReportShellUMA(UMALinuxWaylandShell::kZauraShell);
 }
 
-void OnActivated(void* data,
-                 struct zaura_shell* zaura_shell,
-                 wl_surface* x,
-                 wl_surface* y) {}
-
 WaylandZAuraShell::WaylandZAuraShell(zaura_shell* aura_shell,
                                      WaylandConnection* connection)
     : obj_(aura_shell), connection_(connection) {
@@ -62,6 +57,8 @@
       &OnActivated,
   };
   zaura_shell_add_listener(obj_.get(), &zaura_shell_listener, this);
+  if (connection->surface_submission_in_pixel_coordinates())
+    zaura_shell_surface_submission_in_pixel_coordinates(obj_.get());
 }
 
 WaylandZAuraShell::~WaylandZAuraShell() = default;
@@ -134,4 +131,9 @@
   self->active_desk_index_ = active_desk_index;
 }
 
+// static
+void WaylandZAuraShell::OnActivated(void* data,
+                                    struct zaura_shell* zaura_shell,
+                                    wl_surface* gained_active,
+                                    wl_surface* lost_active) {}
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_shell.h b/ui/ozone/platform/wayland/host/wayland_zaura_shell.h
index 19144508..56b7e9c4 100644
--- a/ui/ozone/platform/wayland/host/wayland_zaura_shell.h
+++ b/ui/ozone/platform/wayland/host/wayland_zaura_shell.h
@@ -52,6 +52,10 @@
   static void OnDeskActivationChanged(void* data,
                                       struct zaura_shell* zaura_shell,
                                       int active_desk_index);
+  static void OnActivated(void* data,
+                          struct zaura_shell* zaura_shell,
+                          struct wl_surface* gained_active,
+                          struct wl_surface* lost_active);
 
   wl::Object<zaura_shell> obj_;
   WaylandConnection* const connection_;
diff --git a/ui/views/bubble/bubble_dialog_model_host.cc b/ui/views/bubble/bubble_dialog_model_host.cc
index 5a245987..8b45d0a 100644
--- a/ui/views/bubble/bubble_dialog_model_host.cc
+++ b/ui/views/bubble/bubble_dialog_model_host.cc
@@ -199,13 +199,13 @@
   // Dialog callbacks can safely refer to |model_|, they can't be called after
   // Widget::Close() calls WidgetWillClose() synchronously so there shouldn't
   // be any dangling references after model removal.
-  SetAcceptCallback(base::BindOnce(&ui::DialogModel::OnDialogAccepted,
+  SetAcceptCallback(base::BindOnce(&ui::DialogModel::OnDialogAcceptAction,
                                    base::Unretained(model_.get()),
                                    GetPassKey()));
-  SetCancelCallback(base::BindOnce(&ui::DialogModel::OnDialogCancelled,
+  SetCancelCallback(base::BindOnce(&ui::DialogModel::OnDialogCancelAction,
                                    base::Unretained(model_.get()),
                                    GetPassKey()));
-  SetCloseCallback(base::BindOnce(&ui::DialogModel::OnDialogClosed,
+  SetCloseCallback(base::BindOnce(&ui::DialogModel::OnDialogCloseAction,
                                   base::Unretained(model_.get()),
                                   GetPassKey()));
 
@@ -335,7 +335,7 @@
 
   // Notify the model of window closing before destroying it (as if
   // Widget::Close)
-  model_->OnWindowClosing(GetPassKey());
+  model_->OnDialogDestroying(GetPassKey());
 
   // TODO(pbos): Consider turning this into for-each-field remove field.
   // TODO(pbos): Move this into a better-named call inside contents_view_ to
@@ -428,7 +428,8 @@
   // ::Close() stack.
   if (!model_)
     return;
-  model_->OnWindowClosing(GetPassKey());
+  model_->OnDialogDestroying(GetPassKey());
+  // TODO(pbos): Do we need to reset `model_` and destroy contents? See Close().
 }
 
 void BubbleDialogModelHost::AddOrUpdateBodyText(
diff --git a/ui/views/bubble/bubble_dialog_model_host_unittest.cc b/ui/views/bubble/bubble_dialog_model_host_unittest.cc
index 419242e..e7b1173 100644
--- a/ui/views/bubble/bubble_dialog_model_host_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_model_host_unittest.cc
@@ -42,7 +42,7 @@
   int window_closing_count = 0;
   auto host = std::make_unique<BubbleDialogModelHost>(
       ui::DialogModel::Builder(std::move(delegate))
-          .SetWindowClosingCallback(base::BindOnce(base::BindOnce(
+          .SetDialogDestroyingCallback(base::BindOnce(base::BindOnce(
               [](int* window_closing_count) { ++(*window_closing_count); },
               &window_closing_count)))
           .Build(),