diff --git a/DEPS b/DEPS
index 5e74181..78386ce7b 100644
--- a/DEPS
+++ b/DEPS
@@ -188,11 +188,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '33603fd54117d83e76b67fc638ba79ad26f1ea60',
+  'skia_revision': '10b7541a1edc4c856937346e162086b368107752',
   # 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': 'f6008bf3e5ae35cfaa6e4bfbfe91438dfa5a1cc0',
+  'v8_revision': '76c4224b7165358334e34caa9999202e2b8d22af',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -200,7 +200,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'a935c65a0488f0ba42e72c6f744f837bef153a37',
+  'angle_revision': 'e2de2c1c54736b994a071801457e7916293d7ee5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -251,7 +251,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': 'de622ae397a72fc31ef7fbd64335a20e0fec75e1',
+  'catapult_revision': '9e96ce18033151f6695012f97d7fd0de23278753',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -259,7 +259,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'c1e0cf3933c592dd90f176998cbe6b39ae101fd6',
+  'devtools_frontend_revision': '82ed9d9bb1f0ca8c39ee03e2b6420803216813c7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -311,11 +311,11 @@
   # 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': '1c9e0450240d6ddce49d373f3ab38f33796708d7',
+  'dawn_revision': 'b231c7fb717d13dc5b15f4ab34527cff198fd17b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '54a38e35843d7a57cc589e4db5a7b7b2a810d358',
+  'quiche_revision': '1bb8308215d886eff707802a882302d5a5c525a9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -861,7 +861,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'dbc68791f80ca24f61679662632d8aa189089032',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'bc4525604b66de9453fc18edec700e4aa0cd48d8',
       'condition': 'checkout_linux',
   },
 
@@ -881,7 +881,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c7eed83f9616452f94af0affe2fad6fe571fbfe3',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd9391fdb176da469e56f3a9d490cff8c89833262',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1458,7 +1458,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '096c0b0921ee1e753d35a3aa9dab321b0b113351',
+    Var('webrtc_git') + '/src.git' + '@' + 'a1163749fd80709f30f54a95a475b5958b98cab7',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1530,7 +1530,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9ca21df4f8339b260741c0b22210a2a3adbd9212',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0f437404b0ed414cea17d177b923a714749497ec',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/assistant/assistant_alarm_timer_controller_impl.cc b/ash/assistant/assistant_alarm_timer_controller_impl.cc
index 7ebb6f90..c877b70 100644
--- a/ash/assistant/assistant_alarm_timer_controller_impl.cc
+++ b/ash/assistant/assistant_alarm_timer_controller_impl.cc
@@ -4,8 +4,7 @@
 
 #include "ash/assistant/assistant_alarm_timer_controller_impl.h"
 
-#include <map>
-#include <string>
+#include <cmath>
 #include <utility>
 
 #include "ash/assistant/assistant_controller_impl.h"
@@ -14,6 +13,7 @@
 #include "ash/public/mojom/assistant_controller.mojom.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/i18n/message_formatter.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -43,9 +43,6 @@
 constexpr char kTimerNotificationGroupingKey[] = "assistant/timer";
 constexpr char kTimerNotificationIdPrefix[] = "assistant/timer";
 
-// Interval at which alarms/timers are ticked.
-constexpr base::TimeDelta kTickInterval = base::TimeDelta::FromSeconds(1);
-
 // Helpers ---------------------------------------------------------------------
 
 std::string ToFormattedTimeString(base::TimeDelta time,
@@ -374,11 +371,8 @@
 
 void AssistantAlarmTimerControllerImpl::OnTimerAdded(
     const AssistantTimer& timer) {
-  // Schedule a repeating timer to tick the tracked timers.
-  if (!ticker_.IsRunning()) {
-    ticker_.Start(FROM_HERE, kTickInterval, &model_,
-                  &AssistantAlarmTimerModel::Tick);
-  }
+  // Schedule the next tick of |timer|.
+  ScheduleNextTick(timer);
 
   // Create a notification for the added alarm/timer.
   assistant_controller_->notification_controller()->AddOrUpdateNotification(
@@ -387,6 +381,9 @@
 
 void AssistantAlarmTimerControllerImpl::OnTimerUpdated(
     const AssistantTimer& timer) {
+  // Schedule the next tick of |timer|.
+  ScheduleNextTick(timer);
+
   // When a |timer| is updated we need to update the corresponding notification
   // unless it has already been dismissed by the user.
   auto* notification_controller =
@@ -400,9 +397,8 @@
 
 void AssistantAlarmTimerControllerImpl::OnTimerRemoved(
     const AssistantTimer& timer) {
-  // If our model is empty, we no longer need tick updates.
-  if (model_.empty())
-    ticker_.Stop();
+  // Clean up the ticker for |timer|, if one exists.
+  tickers_.erase(timer.id);
 
   // Remove any notification associated w/ |timer|.
   assistant_controller_->notification_controller()->RemoveNotificationById(
@@ -438,4 +434,45 @@
   }
 }
 
+void AssistantAlarmTimerControllerImpl::ScheduleNextTick(
+    const AssistantTimer& timer) {
+  auto& ticker = tickers_[timer.id];
+  if (ticker.IsRunning())
+    return;
+
+  // The next tick of |timer| should occur at its next full second of remaining
+  // time. Here we are calculating the number of milliseconds to that next full
+  // second.
+  int millis_to_next_full_sec = timer.remaining_time.InMilliseconds() % 1000;
+
+  // If |timer| has already fired, |millis_to_next_full_sec| will be negative.
+  // In this case, we take the inverse of the value to get the correct number of
+  // milliseconds to the next full second of remaining time.
+  if (millis_to_next_full_sec < 0)
+    millis_to_next_full_sec = 1000 + millis_to_next_full_sec;
+
+  // NOTE: We pass a copy of |timer.id| here as |timer| may no longer exist
+  // when Tick() is called due to the possibility of the |model_| being updated
+  // via a call to OnTimerStateChanged(), such as might happen if a timer is
+  // created, paused, resumed, or removed by LibAssistant.
+  ticker.Start(FROM_HERE,
+               base::TimeDelta::FromMilliseconds(millis_to_next_full_sec),
+               base::BindOnce(&AssistantAlarmTimerControllerImpl::Tick,
+                              base::Unretained(this), timer.id));
+}
+
+void AssistantAlarmTimerControllerImpl::Tick(const std::string& timer_id) {
+  const auto* timer = model_.GetTimerById(timer_id);
+  DCHECK(timer);
+
+  // We don't tick paused timers. Once the |timer| resumes, ticking will resume.
+  if (timer->state == AssistantTimerState::kPaused)
+    return;
+
+  // Update |timer| to reflect the new amount of |remaining_time|.
+  AssistantTimerPtr updated_timer = std::make_unique<AssistantTimer>(*timer);
+  updated_timer->remaining_time = updated_timer->fire_time - base::Time::Now();
+  model_.AddOrUpdateTimer(std::move(updated_timer));
+}
+
 }  // namespace ash
diff --git a/ash/assistant/assistant_alarm_timer_controller_impl.h b/ash/assistant/assistant_alarm_timer_controller_impl.h
index 7e4bca0..bbcfb561 100644
--- a/ash/assistant/assistant_alarm_timer_controller_impl.h
+++ b/ash/assistant/assistant_alarm_timer_controller_impl.h
@@ -72,11 +72,16 @@
                                const std::string& alarm_timer_id,
                                const base::Optional<base::TimeDelta>& duration);
 
+  void ScheduleNextTick(const AssistantTimer& timer);
+  void Tick(const std::string& timer_id);
+
   AssistantControllerImpl* const assistant_controller_;  // Owned by Shell.
 
   AssistantAlarmTimerModel model_;
 
-  base::RepeatingTimer ticker_;
+  // We independently tick timers in our |model_| to update their respective
+  // remaining times. This map contains these tickers, keyed by timer id.
+  std::map<std::string, base::OneShotTimer> tickers_;
 
   // Owned by AssistantService.
   chromeos::assistant::Assistant* assistant_;
diff --git a/ash/assistant/assistant_alarm_timer_controller_unittest.cc b/ash/assistant/assistant_alarm_timer_controller_unittest.cc
index 4b8284c..0e6f92aa 100644
--- a/ash/assistant/assistant_alarm_timer_controller_unittest.cc
+++ b/ash/assistant/assistant_alarm_timer_controller_unittest.cc
@@ -14,11 +14,11 @@
 #include "ash/assistant/model/assistant_alarm_timer_model_observer.h"
 #include "ash/assistant/model/assistant_notification_model.h"
 #include "ash/assistant/model/assistant_notification_model_observer.h"
+#include "ash/assistant/test/assistant_ash_test_base.h"
 #include "ash/assistant/util/deep_link_util.h"
 #include "ash/public/mojom/assistant_controller.mojom.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/test/ash_test_base.h"
 #include "base/macros.h"
 #include "base/test/icu_test_util.h"
 #include "base/test/scoped_feature_list.h"
@@ -40,8 +40,8 @@
 using chromeos::assistant::mojom::AssistantNotificationPtr;
 
 // Constants.
-constexpr char kTimerId[] = "1";
-constexpr char kClientId[] = "assistant/timer1";
+constexpr char kClientId[] = "assistant/timer<timer-id>";
+constexpr char kTimerId[] = "<timer-id>";
 
 // Mocks -----------------------------------------------------------------------
 
@@ -154,6 +154,8 @@
       os << "\npriority: '" << notif.priority_.value() << "'";
     if (notif.remove_on_click_.has_value())
       os << "\nremove_on_click: '" << notif.remove_on_click_.value() << "'";
+    if (notif.title_.has_value())
+      os << "\ntitle: '" << notif.title_.value() << "'";
     return os;
   }
 
@@ -311,28 +313,45 @@
 
 // AssistantAlarmTimerControllerTest -------------------------------------------
 
-class AssistantAlarmTimerControllerTest : public AshTestBase {
+class AssistantAlarmTimerControllerTest : public AssistantAshTestBase {
  protected:
   AssistantAlarmTimerControllerTest()
-      : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+      : AssistantAshTestBase(
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
 
   ~AssistantAlarmTimerControllerTest() override = default;
 
   // AshTestBase:
   void SetUp() override {
-    AshTestBase::SetUp();
-
+    AssistantAshTestBase::SetUp();
     feature_list_.InitAndDisableFeature(
         chromeos::assistant::features::kAssistantTimersV2);
   }
 
-  // Advances the clock by |time_delta|, running any sequenced tasks in the
-  // queue. Note that we don't use |TaskEnvironment::FastForwardBy| because that
-  // API will hang when |time_delta| is sufficiently large, ultimately resulting
-  // in unittest timeout.
-  void AdvanceClock(base::TimeDelta time_delta) {
+  void TearDown() override {
+    controller()->OnTimerStateChanged({});
+    AssistantAshTestBase::TearDown();
+  }
+
+  // Advances the clock by |time_delta| and waits for the next timer update.
+  // NOTE: If |time_delta| is zero, this method is a no-op.
+  void AdvanceClockAndWaitForTimerUpdate(base::TimeDelta time_delta) {
+    if (time_delta.is_zero())
+      return;
+
     task_environment()->AdvanceClock(time_delta);
-    task_environment()->RunUntilIdle();
+
+    MockAssistantAlarmTimerModelObserver observer;
+    controller()->GetModel()->AddObserver(&observer);
+
+    base::RunLoop run_loop;
+    EXPECT_CALL(observer, OnTimerUpdated)
+        .WillOnce(testing::Invoke([&](const AssistantTimer& timer) {
+          run_loop.QuitClosure().Run();
+        }));
+    run_loop.Run();
+
+    controller()->GetModel()->RemoveObserver(&observer);
   }
 
   AssistantAlarmTimerController* controller() {
@@ -391,7 +410,7 @@
   ScheduleTimer(kTimerId).WithCreationTime(creation_time);
 
   // Advance clock.
-  AdvanceClock(base::TimeDelta::FromMinutes(1));
+  AdvanceClockAndWaitForTimerUpdate(base::TimeDelta::FromMinutes(1));
 
   // If unspecified, |creation_time| should carry forward on update.
   EXPECT_CALL(mock, OnTimerUpdated)
@@ -494,7 +513,7 @@
     // Run each tick of the clock in the test.
     for (auto& tick : i18n_test_case.ticks) {
       // Advance clock to next tick.
-      AdvanceClock(tick.advance_clock);
+      AdvanceClockAndWaitForTimerUpdate(tick.advance_clock);
 
       // Make assertions about the notification.
       EXPECT_EQ(ExpectedNotification().WithClientId(kClientId).WithTitle(
@@ -550,7 +569,7 @@
     // Run each tick of the clock in the test.
     for (auto& tick : i18n_test_case.ticks) {
       // Advance clock to next tick.
-      AdvanceClock(tick.advance_clock);
+      AdvanceClockAndWaitForTimerUpdate(tick.advance_clock);
 
       // Make assertions about the notification.
       EXPECT_EQ(ExpectedNotification().WithClientId(kClientId).WithMessage(
@@ -909,7 +928,7 @@
 
   // Advance the clock.
   // NOTE: Six seconds is the threshold for popping up our notification.
-  AdvanceClock(base::TimeDelta::FromSeconds(6));
+  AdvanceClockAndWaitForTimerUpdate(base::TimeDelta::FromSeconds(6));
 
   // Make assertions about the notification.
   EXPECT_EQ(ExpectedNotification().WithClientId(kClientId).WithPriority(
diff --git a/ash/assistant/model/assistant_alarm_timer_model.cc b/ash/assistant/model/assistant_alarm_timer_model.cc
index 4f97135..69a3f2b4 100644
--- a/ash/assistant/model/assistant_alarm_timer_model.cc
+++ b/ash/assistant/model/assistant_alarm_timer_model.cc
@@ -76,20 +76,6 @@
   return it != timers_.end() ? it->second.get() : nullptr;
 }
 
-void AssistantAlarmTimerModel::Tick() {
-  if (timers_.empty())
-    return;
-
-  for (auto& pair : timers_) {
-    AssistantTimer* timer = pair.second.get();
-    if (timer->state == AssistantTimerState::kPaused)
-      continue;
-
-    timer->remaining_time = timer->fire_time - base::Time::Now();
-    NotifyTimerUpdated(*timer);
-  }
-}
-
 void AssistantAlarmTimerModel::NotifyTimerAdded(const AssistantTimer& timer) {
   for (auto& observer : observers_)
     observer.OnTimerAdded(timer);
diff --git a/ash/assistant/model/assistant_alarm_timer_model.h b/ash/assistant/model/assistant_alarm_timer_model.h
index f18cf578..2c316bf0 100644
--- a/ash/assistant/model/assistant_alarm_timer_model.h
+++ b/ash/assistant/model/assistant_alarm_timer_model.h
@@ -44,10 +44,6 @@
   // Returns the timer uniquely identified by |id|.
   const AssistantTimer* GetTimerById(const std::string& id) const;
 
-  // Invoke to tick any timers. Note that this will update the |remaining_time|
-  // for all timers in the model and trigger an OnTimerUpdated() event.
-  void Tick();
-
   // Returns |true| if the model contains no timers, |false| otherwise.
   bool empty() const { return timers_.empty(); }
 
diff --git a/ash/lock_screen_action/lock_screen_note_display_state_handler.cc b/ash/lock_screen_action/lock_screen_note_display_state_handler.cc
index b9ef03a..7196979 100644
--- a/ash/lock_screen_action/lock_screen_note_display_state_handler.cc
+++ b/ash/lock_screen_action/lock_screen_note_display_state_handler.cc
@@ -76,7 +76,7 @@
   // been turned off yet - the note should be launched when the pending
   // backlights state is finished (i.e. the screen is turned off).
   if (backlights_forced_off_setter_->backlights_forced_off() &&
-      backlights_forced_off_setter_->screen_state() == ScreenState::ON) {
+      backlights_forced_off_setter_->GetScreenState() == ScreenState::ON) {
     note_launch_delayed_until_screen_off_ = true;
     return;
   }
@@ -124,7 +124,8 @@
   // delay between request to force backlights off and screen state getting
   // updated due to that request.
   return backlights_forced_off_setter_->backlights_forced_off() ||
-         backlights_forced_off_setter_->screen_state() == ScreenState::OFF_AUTO;
+         backlights_forced_off_setter_->GetScreenState() ==
+             ScreenState::OFF_AUTO;
 }
 
 bool LockScreenNoteDisplayStateHandler::NoteLaunchInProgressOrDelayed() const {
diff --git a/ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.cc b/ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.cc
index bff3996..9ee7e282 100644
--- a/ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.cc
+++ b/ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.cc
@@ -17,6 +17,8 @@
 // AssistantTimer --------------------------------------------------------------
 
 AssistantTimer::AssistantTimer() = default;
+AssistantTimer::AssistantTimer(const AssistantTimer&) = default;
+AssistantTimer& AssistantTimer::operator=(const AssistantTimer&) = default;
 AssistantTimer::~AssistantTimer() = default;
 
 // AssistantAlarmTimerController -----------------------------------------------
diff --git a/ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.h b/ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.h
index bf5f08bf..3853aba1 100644
--- a/ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.h
+++ b/ash/public/cpp/assistant/controller/assistant_alarm_timer_controller.h
@@ -36,8 +36,8 @@
 // Models an Assistant timer.
 struct ASH_PUBLIC_EXPORT AssistantTimer {
   AssistantTimer();
-  AssistantTimer(const AssistantTimer&) = delete;
-  AssistantTimer& operator=(const AssistantTimer&) = delete;
+  AssistantTimer(const AssistantTimer&);
+  AssistantTimer& operator=(const AssistantTimer&);
   ~AssistantTimer();
 
   std::string id;
diff --git a/ash/public/cpp/screen_backlight.h b/ash/public/cpp/screen_backlight.h
index 1ad83dc2..f803afd8 100644
--- a/ash/public/cpp/screen_backlight.h
+++ b/ash/public/cpp/screen_backlight.h
@@ -24,6 +24,9 @@
   virtual void AddObserver(ScreenBacklightObserver* observer) = 0;
   virtual void RemoveObserver(ScreenBacklightObserver* observer) = 0;
 
+  // Returns current system screen state.
+  virtual ScreenState GetScreenState() const = 0;
+
  protected:
   ScreenBacklight();
   virtual ~ScreenBacklight();
diff --git a/ash/quick_answers/ui/quick_answers_pre_target_handler.cc b/ash/quick_answers/ui/quick_answers_pre_target_handler.cc
index 64723149..39f0dda 100644
--- a/ash/quick_answers/ui/quick_answers_pre_target_handler.cc
+++ b/ash/quick_answers/ui/quick_answers_pre_target_handler.cc
@@ -172,7 +172,7 @@
       if (!selected_item)
         return;
 
-      const auto* const parent = selected_item->GetParentMenuItem();
+      auto* const parent = selected_item->GetParentMenuItem();
       bool view_should_gain_focus = false;
       if (parent) {
         // Check if the item is within the outer-most menu, since we do not want
@@ -205,8 +205,10 @@
         view_->RequestFocus();
         key_event->StopPropagation();
 
-        // Deselect the selected boundary menu-item.
-        selected_item->SetSelected(false);
+        // Reopen the sub-menu owned by |parent| to clear the currently selected
+        // boundary menu-item.
+        if (parent)
+          active_menu->SelectItemAndOpenSubmenu(parent);
       }
 
       return;
diff --git a/ash/quick_answers/ui/quick_answers_view.cc b/ash/quick_answers/ui/quick_answers_view.cc
index 8ca5d52..a8a3443 100644
--- a/ash/quick_answers/ui/quick_answers_view.cc
+++ b/ash/quick_answers/ui/quick_answers_view.cc
@@ -72,7 +72,16 @@
 constexpr SkColor kDogfoodButtonColor = gfx::kGoogleGrey500;
 
 // Accessibility.
+// TODO(siabhijeet): Move to grd (tracked in b/149758492).
+constexpr char kA11yAlertAnnouncement[] =
+    "Quick Answer available for text-selection. Use Up or Down arrow keys to "
+    "navigate to the feature from within the menu.";
 constexpr char kA11yNameTemplate[] = "Quick Answer: %s";
+constexpr char kA11yDescTemplate[] =
+    "%s; Click the dialog to see the result in Assistant.";
+constexpr char kA11yRetryLabelName[] = "Quick Answers: Retry";
+constexpr char kA11yRetryLabelDesc[] =
+    "Cannot connect to the internet. Click to try again.";
 
 // Maximum height QuickAnswersView can expand to.
 int MaximumViewHeight() {
@@ -137,11 +146,6 @@
   InitLayout();
   InitWidget();
 
-  // Accessibility.
-  GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenuItem);
-  GetViewAccessibility().OverrideName(
-      base::StringPrintf(kA11yNameTemplate, title_.c_str()));
-
   // Focus.
   SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
   SetInstallFocusRingOnFocus(false);
@@ -184,6 +188,20 @@
   return focus_search_.get();
 }
 
+void QuickAnswersView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  // The view itself is not focused for retry-mode, so should not be announced
+  // by the screen reader.
+  if (retry_label_) {
+    node_data->role = ax::mojom::Role::kNone;
+    node_data->SetName(std::string());
+    node_data->SetDescription(std::string());
+    return;
+  }
+
+  node_data->role = ax::mojom::Role::kDialog;
+  node_data->SetName(base::StringPrintf(kA11yNameTemplate, title_.c_str()));
+}
+
 std::vector<views::View*> QuickAnswersView::GetFocusableViews() {
   std::vector<views::View*> focusable_views;
   // The view itself does not gain focus for retry-view and transfers it to the
@@ -279,6 +297,8 @@
   retry_label_->SetFocusForPlatform();
   retry_label_->set_request_focus_on_press(true);
   SetButtonNotifyActionToOnPress(retry_label_);
+  retry_label_->SetAccessibleName(base::UTF8ToUTF16(kA11yRetryLabelName));
+  retry_label_->GetViewAccessibility().OverrideDescription(kA11yRetryLabelDesc);
 }
 
 void QuickAnswersView::AddAssistantIcon() {
@@ -412,7 +432,8 @@
     // Update answer announcement.
     auto* answer_label =
         static_cast<Label*>(first_answer_view->children().front());
-    GetViewAccessibility().OverrideDescription(answer_label->GetText());
+    GetViewAccessibility().OverrideDescription(base::StringPrintf(
+        kA11yDescTemplate, base::UTF16ToUTF8(answer_label->GetText()).c_str()));
   }
 
   // Add second row answer.
@@ -431,8 +452,13 @@
   }
 
   // Restore focus if the view had one prior to updating the answer.
-  if (pane_already_had_focus)
+  if (pane_already_had_focus) {
     RequestFocus();
+  } else {
+    // Announce that a Quick Answer is available.
+    GetViewAccessibility().AnnounceText(
+        base::UTF8ToUTF16(kA11yAlertAnnouncement));
+  }
 }
 
 void QuickAnswersView::SetBackgroundState(bool highlight) {
diff --git a/ash/quick_answers/ui/quick_answers_view.h b/ash/quick_answers/ui/quick_answers_view.h
index 3a74f60a..74cd5b1e 100644
--- a/ash/quick_answers/ui/quick_answers_view.h
+++ b/ash/quick_answers/ui/quick_answers_view.h
@@ -47,6 +47,7 @@
   void OnFocus() override;
   void OnBlur() override;
   views::FocusTraversable* GetPaneFocusTraversable() override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
   // views::Button:
   void StateChanged(views::Button::ButtonState old_state) override;
diff --git a/ash/quick_answers/ui/user_consent_view.cc b/ash/quick_answers/ui/user_consent_view.cc
index bf0fa8d..bbf2e69 100644
--- a/ash/quick_answers/ui/user_consent_view.cc
+++ b/ash/quick_answers/ui/user_consent_view.cc
@@ -62,9 +62,13 @@
 
 // Manage-Settings button.
 constexpr SkColor kSettingsButtonTextColor = gfx::kGoogleBlue600;
+constexpr char kA11ySettingsButtonDescText[] =
+    "Click to open Google Assistant settings.";
 
 // Grant-Consent button.
 constexpr SkColor kConsentButtonTextColor = gfx::kGoogleGrey200;
+constexpr char kA11yConsentButtonDescTemplate[] =
+    "%s. Click to see the information for the current selection now.";
 
 // Dogfood button.
 constexpr int kDogfoodButtonMarginDip = 4;
@@ -73,7 +77,13 @@
 
 // Accessibility.
 // TODO(siabhijeet): Move to grd after finalizing with UX.
-constexpr char kA11yInfoNameTemplate[] = "New feature: %s";
+constexpr char kA11yInfoAlertText[] =
+    "New feature: %s. Use Up or Down arrow keys to navigate to the feature "
+    "from within the menu.";
+constexpr char kA11yInfoDescTemplate[] =
+    "%s To manage these settings or to start using the feature, use Left/Right "
+    "arrow keys to navigate to the buttons that say 'Manage Settings' or 'Got "
+    "it'.";
 
 // Create and return a simple label with provided specs.
 std::unique_ptr<views::Label> CreateLabel(const base::string16& text,
@@ -153,12 +163,9 @@
       reinterpret_cast<void*>(views::MenuConfig::kMenuControllerGroupingId));
 
   // Read out user-consent notice if screen-reader is active.
-  GetViewAccessibility().OverrideRole(ax::mojom::Role::kAlert);
-  GetViewAccessibility().OverrideName(base::StringPrintf(
-      kA11yInfoNameTemplate, base::UTF16ToUTF8(title_).c_str()));
-  GetViewAccessibility().OverrideDescription(l10n_util::GetStringUTF8(
-      IDS_ASH_QUICK_ANSWERS_USER_CONSENT_VIEW_DESC_TEXT));
-  NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
+  auto announcement =
+      base::StringPrintf(kA11yInfoAlertText, base::UTF16ToUTF8(title_).c_str());
+  GetViewAccessibility().AnnounceText(base::UTF8ToUTF16(announcement));
 }
 
 UserConsentView::~UserConsentView() = default;
@@ -184,6 +191,17 @@
   return focus_search_.get();
 }
 
+void UserConsentView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  node_data->role = ax::mojom::Role::kDialog;
+  node_data->SetName(title_);
+  auto desc =
+      base::StringPrintf(kA11yInfoDescTemplate,
+                         l10n_util::GetStringUTF8(
+                             IDS_ASH_QUICK_ANSWERS_USER_CONSENT_VIEW_DESC_TEXT)
+                             .c_str());
+  node_data->SetDescription(desc);
+}
+
 std::vector<views::View*> UserConsentView::GetFocusableViews() {
   std::vector<views::View*> focusable_views;
   // The view itself is not included in focus loop, unless screen-reader is on.
@@ -297,6 +315,8 @@
       l10n_util::GetStringUTF16(
           IDS_ASH_QUICK_ANSWERS_USER_CONSENT_VIEW_MANAGE_SETTINGS_BUTTON),
       kSettingsButtonTextColor);
+  settings_button->GetViewAccessibility().OverrideDescription(
+      kA11ySettingsButtonDescText);
   settings_button_ = button_bar->AddChildView(std::move(settings_button));
 
   // Grant-Consent button.
@@ -306,6 +326,11 @@
           IDS_ASH_QUICK_ANSWERS_USER_CONSENT_VIEW_GRANT_CONSENT_BUTTON),
       kConsentButtonTextColor);
   consent_button->SetProminent(true);
+  consent_button->GetViewAccessibility().OverrideDescription(
+      base::StringPrintf(kA11yConsentButtonDescTemplate,
+                         l10n_util::GetStringUTF8(
+                             IDS_ASH_QUICK_ANSWERS_USER_CONSENT_VIEW_TITLE_TEXT)
+                             .c_str()));
   consent_button_ = button_bar->AddChildView(std::move(consent_button));
 }
 
diff --git a/ash/quick_answers/ui/user_consent_view.h b/ash/quick_answers/ui/user_consent_view.h
index bab7a1f0..6717a47 100644
--- a/ash/quick_answers/ui/user_consent_view.h
+++ b/ash/quick_answers/ui/user_consent_view.h
@@ -46,6 +46,7 @@
   gfx::Size CalculatePreferredSize() const override;
   void OnFocus() override;
   views::FocusTraversable* GetPaneFocusTraversable() override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
   // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
diff --git a/ash/shelf/hotseat_widget.cc b/ash/shelf/hotseat_widget.cc
index f42a947a4..ec653f019 100644
--- a/ash/shelf/hotseat_widget.cc
+++ b/ash/shelf/hotseat_widget.cc
@@ -432,7 +432,7 @@
   if (translucent_background_.rounded_corner_radii() != rounded_corners)
     translucent_background_.SetRoundedCornerRadius(rounded_corners);
 
-  if (translucent_background_.bounds() != background_bounds)
+  if (translucent_background_.GetTargetBounds() != background_bounds)
     translucent_background_.SetBounds(background_bounds);
 }
 
diff --git a/ash/system/power/backlights_forced_off_setter.cc b/ash/system/power/backlights_forced_off_setter.cc
index b077834..0dbea97 100644
--- a/ash/system/power/backlights_forced_off_setter.cc
+++ b/ash/system/power/backlights_forced_off_setter.cc
@@ -39,6 +39,10 @@
   observers_.RemoveObserver(observer);
 }
 
+ScreenState BacklightsForcedOffSetter::GetScreenState() const {
+  return screen_state_;
+}
+
 std::unique_ptr<ScopedBacklightsForcedOff>
 BacklightsForcedOffSetter::ForceBacklightsOff() {
   auto scoped_backlights_forced_off =
diff --git a/ash/system/power/backlights_forced_off_setter.h b/ash/system/power/backlights_forced_off_setter.h
index 6e1d90e..22d59b4 100644
--- a/ash/system/power/backlights_forced_off_setter.h
+++ b/ash/system/power/backlights_forced_off_setter.h
@@ -35,11 +35,10 @@
     return backlights_forced_off_.value_or(false);
   }
 
-  ScreenState screen_state() const { return screen_state_; }
-
   // ScreenBacklight:
   void AddObserver(ScreenBacklightObserver* observer) override;
   void RemoveObserver(ScreenBacklightObserver* observer) override;
+  ScreenState GetScreenState() const override;
 
   // Forces the backlights off. The backlights will be kept in the forced-off
   // state until all requests have been destroyed.
diff --git a/ash/system/power/power_button_display_controller.cc b/ash/system/power/power_button_display_controller.cc
index f871bb15..0b739f3 100644
--- a/ash/system/power/power_button_display_controller.cc
+++ b/ash/system/power/power_button_display_controller.cc
@@ -48,7 +48,7 @@
 }
 
 bool PowerButtonDisplayController::IsScreenOn() const {
-  return backlights_forced_off_setter_->screen_state() == ScreenState::ON;
+  return backlights_forced_off_setter_->GetScreenState() == ScreenState::ON;
 }
 
 void PowerButtonDisplayController::SetBacklightsForcedOff(bool forced_off) {
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index 2f980ed..a663086 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -48,7 +48,6 @@
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/compositor_extra/shadow.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/transform_util.h"
 #include "ui/views/controls/button/image_button.h"
diff --git a/ash/wm/overview/scoped_overview_transform_window.cc b/ash/wm/overview/scoped_overview_transform_window.cc
index ae37682..122df248 100644
--- a/ash/wm/overview/scoped_overview_transform_window.cc
+++ b/ash/wm/overview/scoped_overview_transform_window.cc
@@ -33,7 +33,6 @@
 #include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_observer.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/transform_util.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/widget/widget.h"
diff --git a/base/OWNERS b/base/OWNERS
index 7895973..6949c23b 100644
--- a/base/OWNERS
+++ b/base/OWNERS
@@ -33,6 +33,8 @@
 per-file rand_util*=set noparent
 per-file rand_util*=file://ipc/SECURITY_OWNERS
 
+per-file safe_numerics_unittest.cc=file://base/numerics/OWNERS
+
 # For TCMalloc tests:
 per-file security_unittest.cc=jln@chromium.org
 
diff --git a/base/mac/call_with_eh_frame.cc b/base/mac/call_with_eh_frame.cc
index 31ca53e9..f26d1e5 100644
--- a/base/mac/call_with_eh_frame.cc
+++ b/base/mac/call_with_eh_frame.cc
@@ -12,6 +12,7 @@
 namespace base {
 namespace mac {
 
+#if defined(__x86_64__) || defined(__aarch64__)
 extern "C" _Unwind_Reason_Code __gxx_personality_v0(int,
                                                     _Unwind_Action,
                                                     uint64_t,
@@ -42,5 +43,11 @@
   return __gxx_personality_v0(version, actions, exception_class,
                               exception_object, context);
 }
+#else  // !defined(__x86_64__) && !defined(__aarch64__)
+// No implementation exists, so just call the block directly.
+void CallWithEHFrame(void (^block)(void)) {
+  block();
+}
+#endif  // defined(__x86_64__) || defined(__aarch64__)
 }  // namespace mac
 }  // namespace base
diff --git a/base/mac/call_with_eh_frame_asm.S b/base/mac/call_with_eh_frame_asm.S
index d720a339..c58d159 100644
--- a/base/mac/call_with_eh_frame_asm.S
+++ b/base/mac/call_with_eh_frame_asm.S
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#if defined(__x86_64__) || defined(__aarch64__)
+
 // base::mac::CallWithEHFrame(void () block_pointer)
 #define CALL_WITH_EH_FRAME __ZN4base3mac15CallWithEHFrameEU13block_pointerFvvE
 
@@ -127,3 +129,5 @@
 
 Ltypes_table_base:
   .p2align 2
+
+#endif  // defined(__x86_64__) || defined(__aarch64__)
diff --git a/base/numerics/README.md b/base/numerics/README.md
index 9d00d77..5142585 100644
--- a/base/numerics/README.md
+++ b/base/numerics/README.md
@@ -51,10 +51,26 @@
 corresponding maximum or minimum of the destination type:
 
 ```cpp
+// Cast to a smaller type, saturating as needed.
+int8_t eight_bit_value = saturated_cast<int8_t>(int_value);
+
 // Convert from float with saturation to INT_MAX, INT_MIN, or 0 for NaN.
 int int_value = saturated_cast<int>(floating_point_value);
 ```
 
+`Ceil`, `Floor`, and `Round` provide similar functionality to the versions in
+`std::`, but saturate and return an integral type.  An optional template
+parameter specifies the desired destination type (`int` if unspecified).  These
+should be used for most floating-to-integral conversions.
+
+```cpp
+// Basically saturated_cast<int>(std::round(floating_point_value)).
+int int_value = Round(floating_point_value);
+
+// A destination type can be explicitly specified.
+uint8_t byte_value = Floor<uint8_t>(floating_point_value);
+```
+
 ### Enforcing arithmetic type conversions at compile-time
 
 The `strict_cast` emits code that is identical to `static_cast`. However,
@@ -179,16 +195,25 @@
 
 ### Other helper and conversion functions
 
-*   `IsValueInRangeForNumericType<>()` - A convenience function that returns
-    true if the type supplied as the template parameter can represent the value
-    passed as an argument to the function.
+*   `Ceil<>()` - A convenience function that computes the ceil of its floating-
+    point arg, then saturates to the destination type (template parameter,
+    defaults to `int`).
+*   `Floor<>()` - A convenience function that computes the floor of its
+    floating-point arg, then saturates to the destination type (template
+    parameter, defaults to `int`).
 *   `IsTypeInRangeForNumericType<>()` - A convenience function that evaluates
     entirely at compile-time and returns true if the destination type (first
     template parameter) can represent the full range of the source type
     (second template parameter).
+*   `IsValueInRangeForNumericType<>()` - A convenience function that returns
+    true if the type supplied as the template parameter can represent the value
+    passed as an argument to the function.
 *   `IsValueNegative()` - A convenience function that will accept any
     arithmetic type as an argument and will return whether the value is less
     than zero. Unsigned types always return false.
+*   `Round<>()` - A convenience function that rounds its floating-point arg,
+    then saturates to the destination type (template parameter, defaults to
+    `int`).
 *   `SafeUnsignedAbs()` - Returns the absolute value of the supplied integer
     parameter as an unsigned result (thus avoiding an overflow if the value
     is the signed, two's complement minimum).
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h
index b9f81e8..cb9a717 100644
--- a/base/numerics/safe_conversions.h
+++ b/base/numerics/safe_conversions.h
@@ -7,6 +7,7 @@
 
 #include <stddef.h>
 
+#include <cmath>
 #include <limits>
 #include <type_traits>
 
@@ -353,6 +354,33 @@
 // Explicitly make a shorter size_t alias for convenience.
 using SizeT = StrictNumeric<size_t>;
 
+// floating -> integral conversions that saturate and thus can actually return
+// an integral type.  In most cases, these should be preferred over the std::
+// versions.
+template <typename Dst = int,
+          typename Src,
+          typename = std::enable_if_t<std::is_integral<Dst>::value &&
+                                      std::is_floating_point<Src>::value>>
+Dst Floor(Src value) {
+  return saturated_cast<Dst>(std::floor(value));
+}
+template <typename Dst = int,
+          typename Src,
+          typename = std::enable_if_t<std::is_integral<Dst>::value &&
+                                      std::is_floating_point<Src>::value>>
+Dst Ceil(Src value) {
+  return saturated_cast<Dst>(std::ceil(value));
+}
+template <typename Dst = int,
+          typename Src,
+          typename = std::enable_if_t<std::is_integral<Dst>::value &&
+                                      std::is_floating_point<Src>::value>>
+Dst Round(Src value) {
+  const Src rounded =
+      (value >= 0.0f) ? std::floor(value + 0.5f) : std::ceil(value - 0.5f);
+  return saturated_cast<Dst>(rounded);
+}
+
 }  // namespace base
 
 #endif  // BASE_NUMERICS_SAFE_CONVERSIONS_H_
diff --git a/base/profiler/module_cache.cc b/base/profiler/module_cache.cc
index 50e920d..ec08963 100644
--- a/base/profiler/module_cache.cc
+++ b/base/profiler/module_cache.cc
@@ -33,13 +33,8 @@
 ModuleCache::~ModuleCache() = default;
 
 const ModuleCache::Module* ModuleCache::GetModuleForAddress(uintptr_t address) {
-  const auto non_native_module_loc = non_native_modules_.find(address);
-  if (non_native_module_loc != non_native_modules_.end())
-    return non_native_module_loc->get();
-
-  const auto native_module_loc = native_modules_.find(address);
-  if (native_module_loc != native_modules_.end())
-    return native_module_loc->get();
+  if (const ModuleCache::Module* module = GetExistingModuleForAddress(address))
+    return module;
 
   std::unique_ptr<const Module> new_module = CreateModuleForAddress(address);
   if (!new_module)
@@ -97,6 +92,19 @@
   native_modules_.insert(std::move(module));
 }
 
+const ModuleCache::Module* ModuleCache::GetExistingModuleForAddress(
+    uintptr_t address) const {
+  const auto non_native_module_loc = non_native_modules_.find(address);
+  if (non_native_module_loc != non_native_modules_.end())
+    return non_native_module_loc->get();
+
+  const auto native_module_loc = native_modules_.find(address);
+  if (native_module_loc != native_modules_.end())
+    return native_module_loc->get();
+
+  return nullptr;
+}
+
 bool ModuleCache::ModuleAndAddressCompare::operator()(
     const std::unique_ptr<const Module>& m1,
     const std::unique_ptr<const Module>& m2) const {
diff --git a/base/profiler/module_cache.h b/base/profiler/module_cache.h
index f8725e40..9bc601e 100644
--- a/base/profiler/module_cache.h
+++ b/base/profiler/module_cache.h
@@ -98,6 +98,14 @@
   // will be found and added automatically when invoking GetModuleForAddress().
   void AddCustomNativeModule(std::unique_ptr<const Module> module);
 
+  // Gets the module containing |address| if one already exists, or nullptr
+  // otherwise. The returned module remains owned by and has the same lifetime
+  // as the ModuleCache object.
+  // NOTE: Only users that create their own modules and need control over native
+  // module creation should use this function. Everyone else should use
+  // GetModuleForAddress().
+  const Module* GetExistingModuleForAddress(uintptr_t address) const;
+
  private:
   // Heterogenously compares modules by base address, and modules and
   // addresses. The module/address comparison considers the address equivalent
diff --git a/base/profiler/native_unwinder_android.cc b/base/profiler/native_unwinder_android.cc
index 41bfaa5..efba835 100644
--- a/base/profiler/native_unwinder_android.cc
+++ b/base/profiler/native_unwinder_android.cc
@@ -202,7 +202,9 @@
       regs->set_dex_pc(0);
     }
 
-    // Add the frame to |stack|.
+    // Add the frame to |stack|. Must use GetModuleForAddress rather than
+    // GetExistingModuleForAddress because the unwound-to address may be in a
+    // module associated with a different unwinder.
     const ModuleCache::Module* module =
         module_cache->GetModuleForAddress(regs->pc());
     stack->emplace_back(regs->pc(), module);
@@ -246,12 +248,13 @@
 void NativeUnwinderAndroid::EmitDexFrame(uintptr_t dex_pc,
                                          ModuleCache* module_cache,
                                          std::vector<Frame>* stack) const {
-  const ModuleCache::Module* module = module_cache->GetModuleForAddress(dex_pc);
+  const ModuleCache::Module* module =
+      module_cache->GetExistingModuleForAddress(dex_pc);
   if (!module) {
     // The region containing |dex_pc| may not be in |module_cache| since it's
     // usually not executable (.dex file). Since non-executable regions
     // are used much less commonly, it's lazily added here instead of from
-    // AddInitialModules().
+    // AddInitialModulesFromMaps().
     unwindstack::MapInfo* map_info = memory_regions_map_->Find(dex_pc);
     if (map_info) {
       auto new_module = std::make_unique<NonElfModule>(map_info);
diff --git a/base/profiler/native_unwinder_android_unittest.cc b/base/profiler/native_unwinder_android_unittest.cc
index ed58492..2b82bae5 100644
--- a/base/profiler/native_unwinder_android_unittest.cc
+++ b/base/profiler/native_unwinder_android_unittest.cc
@@ -473,17 +473,10 @@
   const uintptr_t c_library_function_address =
       reinterpret_cast<uintptr_t>(&printf);
 
-  std::vector<const ModuleCache::Module*> unwinder_modules =
-      unwinder_module_cache.GetModules();
-  const auto unwinder_module_loc = std::find_if(
-      unwinder_modules.begin(), unwinder_modules.end(),
-      [c_library_function_address](const ModuleCache::Module* module) {
-        return c_library_function_address >= module->GetBaseAddress() &&
-               c_library_function_address <
-                   module->GetBaseAddress() + module->GetSize();
-      });
-  ASSERT_NE(unwinder_modules.end(), unwinder_module_loc);
-  const ModuleCache::Module* unwinder_module = *unwinder_module_loc;
+  const ModuleCache::Module* unwinder_module =
+      unwinder_module_cache.GetExistingModuleForAddress(
+          c_library_function_address);
+  ASSERT_NE(nullptr, unwinder_module);
 
   ModuleCache reference_module_cache;
   const ModuleCache::Module* reference_module =
@@ -516,17 +509,10 @@
   const uintptr_t chrome_function_address =
       reinterpret_cast<uintptr_t>(&CaptureScenario);
 
-  std::vector<const ModuleCache::Module*> unwinder_modules =
-      unwinder_module_cache.GetModules();
-  const auto unwinder_module_loc = std::find_if(
-      unwinder_modules.begin(), unwinder_modules.end(),
-      [chrome_function_address](const ModuleCache::Module* module) {
-        return chrome_function_address >= module->GetBaseAddress() &&
-               chrome_function_address <
-                   module->GetBaseAddress() + module->GetSize();
-      });
-  ASSERT_NE(unwinder_modules.end(), unwinder_module_loc);
-  const ModuleCache::Module* unwinder_module = *unwinder_module_loc;
+  const ModuleCache::Module* unwinder_module =
+      unwinder_module_cache.GetExistingModuleForAddress(
+          chrome_function_address);
+  ASSERT_NE(nullptr, unwinder_module);
 
   ModuleCache reference_module_cache;
   const ModuleCache::Module* reference_module =
diff --git a/base/safe_numerics_unittest.cc b/base/safe_numerics_unittest.cc
index aafcb58..2ed4cf54 100644
--- a/base/safe_numerics_unittest.cc
+++ b/base/safe_numerics_unittest.cc
@@ -1660,6 +1660,118 @@
   }
 }
 
+TEST(SafeNumerics, CeilInt) {
+  constexpr float kMax = std::numeric_limits<int>::max();
+  constexpr float kMin = std::numeric_limits<int>::min();
+  constexpr float kInfinity = std::numeric_limits<float>::infinity();
+  constexpr float kNaN = std::numeric_limits<float>::quiet_NaN();
+
+  constexpr int kIntMax = std::numeric_limits<int>::max();
+  constexpr int kIntMin = std::numeric_limits<int>::min();
+
+  EXPECT_EQ(kIntMax, Ceil(kInfinity));
+  EXPECT_EQ(kIntMax, Ceil(kMax));
+  EXPECT_EQ(kIntMax, Ceil(kMax + 100.0f));
+  EXPECT_EQ(0, Ceil(kNaN));
+
+  EXPECT_EQ(-100, Ceil(-100.5f));
+  EXPECT_EQ(0, Ceil(0.0f));
+  EXPECT_EQ(101, Ceil(100.5f));
+
+  EXPECT_EQ(kIntMin, Ceil(-kInfinity));
+  EXPECT_EQ(kIntMin, Ceil(kMin));
+  EXPECT_EQ(kIntMin, Ceil(kMin - 100.0f));
+  EXPECT_EQ(0, Ceil(-kNaN));
+}
+
+TEST(SafeNumerics, FloorInt) {
+  constexpr float kMax = std::numeric_limits<int>::max();
+  constexpr float kMin = std::numeric_limits<int>::min();
+  constexpr float kInfinity = std::numeric_limits<float>::infinity();
+  constexpr float kNaN = std::numeric_limits<float>::quiet_NaN();
+
+  constexpr int kIntMax = std::numeric_limits<int>::max();
+  constexpr int kIntMin = std::numeric_limits<int>::min();
+
+  EXPECT_EQ(kIntMax, Floor(kInfinity));
+  EXPECT_EQ(kIntMax, Floor(kMax));
+  EXPECT_EQ(kIntMax, Floor(kMax + 100.0f));
+  EXPECT_EQ(0, Floor(kNaN));
+
+  EXPECT_EQ(-101, Floor(-100.5f));
+  EXPECT_EQ(0, Floor(0.0f));
+  EXPECT_EQ(100, Floor(100.5f));
+
+  EXPECT_EQ(kIntMin, Floor(-kInfinity));
+  EXPECT_EQ(kIntMin, Floor(kMin));
+  EXPECT_EQ(kIntMin, Floor(kMin - 100.0f));
+  EXPECT_EQ(0, Floor(-kNaN));
+}
+
+TEST(SafeNumerics, RoundInt) {
+  constexpr float kMax = std::numeric_limits<int>::max();
+  constexpr float kMin = std::numeric_limits<int>::min();
+  constexpr float kInfinity = std::numeric_limits<float>::infinity();
+  constexpr float kNaN = std::numeric_limits<float>::quiet_NaN();
+
+  constexpr int kIntMax = std::numeric_limits<int>::max();
+  constexpr int kIntMin = std::numeric_limits<int>::min();
+
+  EXPECT_EQ(kIntMax, Round(kInfinity));
+  EXPECT_EQ(kIntMax, Round(kMax));
+  EXPECT_EQ(kIntMax, Round(kMax + 100.0f));
+  EXPECT_EQ(0, Round(kNaN));
+
+  EXPECT_EQ(-100, Round(-100.1f));
+  EXPECT_EQ(-101, Round(-100.5f));
+  EXPECT_EQ(-101, Round(-100.9f));
+  EXPECT_EQ(0, Round(0.0f));
+  EXPECT_EQ(100, Round(100.1f));
+  EXPECT_EQ(101, Round(100.5f));
+  EXPECT_EQ(101, Round(100.9f));
+
+  EXPECT_EQ(kIntMin, Round(-kInfinity));
+  EXPECT_EQ(kIntMin, Round(kMin));
+  EXPECT_EQ(kIntMin, Round(kMin - 100.0f));
+  EXPECT_EQ(0, Round(-kNaN));
+}
+
+TEST(SafeNumerics, Int64) {
+  constexpr double kMax = std::numeric_limits<int64_t>::max();
+  constexpr double kMin = std::numeric_limits<int64_t>::min();
+  constexpr double kInfinity = std::numeric_limits<double>::infinity();
+  constexpr double kNaN = std::numeric_limits<double>::quiet_NaN();
+
+  constexpr int64_t kInt64Max = std::numeric_limits<int64_t>::max();
+  constexpr int64_t kInt64Min = std::numeric_limits<int64_t>::min();
+
+  EXPECT_EQ(kInt64Max, Floor<int64_t>(kInfinity));
+  EXPECT_EQ(kInt64Max, Ceil<int64_t>(kInfinity));
+  EXPECT_EQ(kInt64Max, Round<int64_t>(kInfinity));
+  EXPECT_EQ(kInt64Max, Floor<int64_t>(kMax));
+  EXPECT_EQ(kInt64Max, Ceil<int64_t>(kMax));
+  EXPECT_EQ(kInt64Max, Round<int64_t>(kMax));
+  EXPECT_EQ(kInt64Max, Floor<int64_t>(kMax + 100.0));
+  EXPECT_EQ(kInt64Max, Ceil<int64_t>(kMax + 100.0));
+  EXPECT_EQ(kInt64Max, Round<int64_t>(kMax + 100.0));
+  EXPECT_EQ(0, Floor<int64_t>(kNaN));
+  EXPECT_EQ(0, Ceil<int64_t>(kNaN));
+  EXPECT_EQ(0, Round<int64_t>(kNaN));
+
+  EXPECT_EQ(kInt64Min, Floor<int64_t>(-kInfinity));
+  EXPECT_EQ(kInt64Min, Ceil<int64_t>(-kInfinity));
+  EXPECT_EQ(kInt64Min, Round<int64_t>(-kInfinity));
+  EXPECT_EQ(kInt64Min, Floor<int64_t>(kMin));
+  EXPECT_EQ(kInt64Min, Ceil<int64_t>(kMin));
+  EXPECT_EQ(kInt64Min, Round<int64_t>(kMin));
+  EXPECT_EQ(kInt64Min, Floor<int64_t>(kMin - 100.0));
+  EXPECT_EQ(kInt64Min, Ceil<int64_t>(kMin - 100.0));
+  EXPECT_EQ(kInt64Min, Round<int64_t>(kMin - 100.0));
+  EXPECT_EQ(0, Floor<int64_t>(-kNaN));
+  EXPECT_EQ(0, Ceil<int64_t>(-kNaN));
+  EXPECT_EQ(0, Round<int64_t>(-kNaN));
+}
+
 #if defined(__clang__)
 #pragma clang diagnostic pop  // -Winteger-overflow
 #endif
diff --git a/base/task/post_job.cc b/base/task/post_job.cc
index 0fe80ff1..210ed57b 100644
--- a/base/task/post_job.cc
+++ b/base/task/post_job.cc
@@ -26,6 +26,8 @@
 }
 
 JobDelegate::~JobDelegate() {
+  if (task_id_ != kInvalidTaskId)
+    task_source_->ReleaseTaskId(task_id_);
 #if DCHECK_IS_ON()
   // When ShouldYield() returns false, the worker task is expected to do
   // work before returning.
@@ -60,6 +62,12 @@
   task_source_->NotifyConcurrencyIncrease();
 }
 
+uint8_t JobDelegate::GetTaskId() {
+  if (task_id_ == kInvalidTaskId)
+    task_id_ = task_source_->AcquireTaskId();
+  return task_id_;
+}
+
 void JobDelegate::AssertExpectedConcurrency(size_t expected_max_concurrency) {
   // In dcheck builds, verify that max concurrency falls in one of the following
   // cases:
diff --git a/base/task/post_job.h b/base/task/post_job.h
index 3ae3186..75aebb3 100644
--- a/base/task/post_job.h
+++ b/base/task/post_job.h
@@ -5,6 +5,8 @@
 #ifndef BASE_TASK_POST_JOB_H_
 #define BASE_TASK_POST_JOB_H_
 
+#include <limits>
+
 #include "base/base_export.h"
 #include "base/callback.h"
 #include "base/check_op.h"
@@ -50,7 +52,14 @@
   // of worker should be adjusted accordingly. See PostJob() for more details.
   void NotifyConcurrencyIncrease();
 
+  // Returns a task_id unique among threads currently running this job, such
+  // that GetTaskId() < worker count. To achieve this, the same task_id may be
+  // reused by a different thread after a worker_task returns.
+  uint8_t GetTaskId();
+
  private:
+  static constexpr uint8_t kInvalidTaskId = std::numeric_limits<uint8_t>::max();
+
   // Verifies that either max concurrency is lower or equal to
   // |expected_max_concurrency|, or there is an increase version update
   // triggered by NotifyConcurrencyIncrease().
@@ -58,6 +67,7 @@
 
   internal::JobTaskSource* const task_source_;
   internal::PooledTaskRunnerDelegate* const pooled_task_runner_delegate_;
+  uint8_t task_id_ = kInvalidTaskId;
 
 #if DCHECK_IS_ON()
   // Used in AssertExpectedConcurrency(), see that method's impl for details.
diff --git a/base/task/thread_pool/job_task_source.cc b/base/task/thread_pool/job_task_source.cc
index 5ff698c..a9427db 100644
--- a/base/task/thread_pool/job_task_source.cc
+++ b/base/task/thread_pool/job_task_source.cc
@@ -4,10 +4,12 @@
 
 #include "base/task/thread_pool/job_task_source.h"
 
+#include <type_traits>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/bits.h"
 #include "base/check_op.h"
 #include "base/memory/ptr_util.h"
 #include "base/task/common/checked_lock.h"
@@ -20,6 +22,18 @@
 namespace base {
 namespace internal {
 
+namespace {
+
+// Capped to allow assigning task_ids from a bitfield.
+constexpr size_t kMaxWorkersPerJob = 32;
+static_assert(
+    kMaxWorkersPerJob <=
+        std::numeric_limits<std::result_of<
+            decltype (&JobDelegate::GetTaskId)(JobDelegate)>::type>::max(),
+    "AcquireTaskId return type isn't big enough to fit kMaxWorkersPerJob");
+
+}  // namespace
+
 // Memory ordering on |state_| operations
 //
 // The write operation on |state_| in WillRunTask() uses
@@ -317,7 +331,30 @@
 }
 
 size_t JobTaskSource::GetMaxConcurrency() const {
-  return max_concurrency_callback_.Run();
+  return std::min(max_concurrency_callback_.Run(), kMaxWorkersPerJob);
+}
+
+uint8_t JobTaskSource::AcquireTaskId() {
+  static_assert(kMaxWorkersPerJob <= sizeof(assigned_task_ids_) * 8,
+                "TaskId bitfield isn't big enough to fit kMaxWorkersPerJob.");
+  uint32_t assigned_task_ids =
+      assigned_task_ids_.load(std::memory_order_relaxed);
+  uint32_t new_assigned_task_ids = 0;
+  uint8_t task_id = 0;
+  do {
+    // Count trailing one bits. This is the id of the right-most 0-bit in
+    // |assigned_task_ids|.
+    task_id = bits::CountTrailingZeroBits(~assigned_task_ids);
+    new_assigned_task_ids = assigned_task_ids | (uint32_t(1) << task_id);
+  } while (assigned_task_ids_.compare_exchange_weak(
+      assigned_task_ids, new_assigned_task_ids, std::memory_order_relaxed));
+  return task_id;
+}
+
+void JobTaskSource::ReleaseTaskId(uint8_t task_id) {
+  uint32_t previous_task_ids =
+      assigned_task_ids_.fetch_and(~(uint32_t(1) << task_id));
+  DCHECK(previous_task_ids & (uint32_t(1) << task_id));
 }
 
 bool JobTaskSource::ShouldYield() {
diff --git a/base/task/thread_pool/job_task_source.h b/base/task/thread_pool/job_task_source.h
index b043f326..7b8c13e4 100644
--- a/base/task/thread_pool/job_task_source.h
+++ b/base/task/thread_pool/job_task_source.h
@@ -73,6 +73,9 @@
   // concurrently.
   size_t GetMaxConcurrency() const;
 
+  uint8_t AcquireTaskId();
+  void ReleaseTaskId(uint8_t task_id);
+
   // Returns true if a worker should return from the worker task on the current
   // thread ASAP.
   bool ShouldYield();
@@ -194,6 +197,7 @@
 
   // Current atomic state.
   State state_;
+  std::atomic<uint32_t> assigned_task_ids_{0};
   // Normally, |join_flag_| is protected by |lock_|, except in ShouldYield()
   // hence the use of atomics.
   JoinFlag join_flag_ GUARDED_BY(lock_);
diff --git a/base/task/thread_pool/job_task_source_unittest.cc b/base/task/thread_pool/job_task_source_unittest.cc
index 28847ee..16a2414 100644
--- a/base/task/thread_pool/job_task_source_unittest.cc
+++ b/base/task/thread_pool/job_task_source_unittest.cc
@@ -475,5 +475,54 @@
   EXPECT_DCHECK_DEATH(registered_task_source.DidProcessTask());
 }
 
+TEST_F(ThreadPoolJobTaskSourceTest, AcquireTaskId) {
+  auto job_task =
+      base::MakeRefCounted<test::MockJobTask>(DoNothing(),
+                                              /* num_tasks_to_run */ 4);
+  scoped_refptr<JobTaskSource> task_source =
+      job_task->GetJobTaskSource(FROM_HERE, {}, &pooled_task_runner_delegate_);
+
+  EXPECT_EQ(0U, task_source->AcquireTaskId());
+  EXPECT_EQ(1U, task_source->AcquireTaskId());
+  EXPECT_EQ(2U, task_source->AcquireTaskId());
+  EXPECT_EQ(3U, task_source->AcquireTaskId());
+  EXPECT_EQ(4U, task_source->AcquireTaskId());
+  task_source->ReleaseTaskId(1);
+  task_source->ReleaseTaskId(3);
+  EXPECT_EQ(1U, task_source->AcquireTaskId());
+  EXPECT_EQ(3U, task_source->AcquireTaskId());
+  EXPECT_EQ(5U, task_source->AcquireTaskId());
+}
+
+// Verifies that task id is released after worker_task returns.
+TEST_F(ThreadPoolJobTaskSourceTest, GetTaskId) {
+  auto task_source = MakeRefCounted<JobTaskSource>(
+      FROM_HERE, TaskTraits{}, BindRepeating([](JobDelegate* delegate) {
+        // Confirm that task id 0 is reused on the second run.
+        EXPECT_EQ(0U, delegate->GetTaskId());
+
+        // Allow running the task again.
+        delegate->NotifyConcurrencyIncrease();
+      }),
+      BindRepeating([]() -> size_t { return 1; }),
+      &pooled_task_runner_delegate_);
+
+  auto registered_task_source =
+      RegisteredTaskSource::CreateForTesting(task_source);
+
+  // Run the worker_task twice.
+  ASSERT_EQ(registered_task_source.WillRunTask(),
+            TaskSource::RunStatus::kAllowedSaturated);
+  auto task1 = registered_task_source.TakeTask();
+  std::move(task1.task).Run();
+  registered_task_source.DidProcessTask();
+
+  ASSERT_EQ(registered_task_source.WillRunTask(),
+            TaskSource::RunStatus::kAllowedSaturated);
+  auto task2 = registered_task_source.TakeTask();
+  std::move(task2.task).Run();
+  registered_task_source.DidProcessTask();
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 5566b86..a6617c5 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200629.1.1
+0.20200629.2.4
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 5566b86..a6617c5 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200629.1.1
+0.20200629.2.4
diff --git a/build/mac_toolchain.py b/build/mac_toolchain.py
index ff78830..d85bc510 100755
--- a/build/mac_toolchain.py
+++ b/build/mac_toolchain.py
@@ -101,7 +101,7 @@
   sys.stderr.flush()
 
 
-def InstallXcodeBinaries(version):
+def InstallXcodeBinaries(version, binaries_root=None):
   """Installs the Xcode binaries needed to build Chrome and accepts the license.
 
   This is the replacement for InstallXcode that installs a trimmed down version
@@ -109,7 +109,8 @@
   """
   # First make sure the directory exists. It will serve as the cipd root. This
   # also ensures that there will be no conflicts of cipd root.
-  binaries_root = os.path.join(TOOLCHAIN_ROOT, 'xcode_binaries')
+  if binaries_root is None:
+    binaries_root = os.path.join(TOOLCHAIN_ROOT, 'xcode_binaries')
   if not os.path.exists(binaries_root):
     os.makedirs(binaries_root)
 
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index e60770e9..fee52de 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -419,10 +419,10 @@
       if is_optional:
         continue
       else:
-        raise Exception('%s not found in "%s"\r\nYou must install the '
-                        '"Debugging Tools for Windows" feature from the Windows'
-                        ' 10 SDK, the 10.0.19041.0 version.'
-                        % (debug_file, full_path))
+        raise Exception('%s not found in "%s"\r\nYou must install'
+                        'Windows 10 SDK version 10.0.19041.0 including the '
+                        '"Debugging Tools for Windows" feature.' %
+                        (debug_file, full_path))
     target_path = os.path.join(target_dir, debug_file)
     _CopyRuntimeImpl(target_path, full_path)
 
diff --git a/cc/metrics/video_playback_roughness_reporter.cc b/cc/metrics/video_playback_roughness_reporter.cc
index a7b77e99..72c57b9 100644
--- a/cc/metrics/video_playback_roughness_reporter.cc
+++ b/cc/metrics/video_playback_roughness_reporter.cc
@@ -59,6 +59,8 @@
   FrameInfo info;
   info.token = token;
   info.decode_time = frame.metadata()->decode_end_time;
+  info.refresh_rate_hz = int{std::round(1.0 / render_interval.InSecondsF())};
+  info.size = frame.natural_size();
 
   info.intended_duration = frame.metadata()->wallclock_frame_duration;
   if (info.intended_duration) {
@@ -108,7 +110,8 @@
   }
 
   auto it = worst_windows_.begin() + index_to_submit;
-  reporting_cb_.Run(it->size, it->intended_duration, it->roughness());
+  reporting_cb_.Run(it->size, it->intended_duration, it->roughness(),
+                    it->refresh_rate_hz, it->frame_size);
 
   worst_windows_.clear();
   windows_seen_ = 0;
@@ -163,15 +166,26 @@
   // let's calculate window metrics and report it.
   if (next_frame_it - frames_.begin() > frames_window_size_) {
     ConsecutiveFramesWindow win;
+    bool observed_change_in_parameters = false;
     double mean_square_error_ms2 = 0.0;
     base::TimeDelta total_error;
-    if (frames_.front().presentation_time.has_value())
-      win.first_frame_time = frames_.front().presentation_time.value();
+    auto& first_frame = frames_.front();
+    if (first_frame.presentation_time.has_value()) {
+      win.first_frame_time = first_frame.presentation_time.value();
+      win.refresh_rate_hz = first_frame.refresh_rate_hz;
+      win.frame_size = first_frame.size;
+    }
 
     for (auto i = 0; i < frames_window_size_; i++) {
       FrameInfo& frame = frames_[i];
       base::TimeDelta error;
 
+      if (win.frame_size != frame.size ||
+          win.refresh_rate_hz != frame.refresh_rate_hz) {
+        observed_change_in_parameters = true;
+        break;
+      }
+
       if (frame.actual_duration.has_value() &&
           frame.intended_duration.has_value()) {
         error = frame.actual_duration.value() - frame.intended_duration.value();
@@ -187,7 +201,20 @@
     win.root_mean_square_error = base::TimeDelta::FromMillisecondsD(
         std::sqrt(mean_square_error_ms2 / frames_window_size_));
 
-    ReportWindow(win);
+    if (observed_change_in_parameters) {
+      // There has been a change in the frame size or the screen refresh rate,
+      // whatever roughness stats were accumulated up to this point need to be
+      // reported or discarded, because there is no point in mixing together
+      // roughess for different resolutions or refresh rates.
+      if (windows_seen_ >= kMinWindowsBeforeSubmit) {
+        SubmitPlaybackRoughness();
+      } else {
+        worst_windows_.clear();
+        windows_seen_ = 0;
+      }
+    } else {
+      ReportWindow(win);
+    }
 
     // The frames in the window have been reported,
     // no need to keep them around any longer.
diff --git a/cc/metrics/video_playback_roughness_reporter.h b/cc/metrics/video_playback_roughness_reporter.h
index d001df9..820ac9f 100644
--- a/cc/metrics/video_playback_roughness_reporter.h
+++ b/cc/metrics/video_playback_roughness_reporter.h
@@ -13,6 +13,7 @@
 #include "cc/cc_export.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_types.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace cc {
 
@@ -20,8 +21,14 @@
 // |frames| - number of frames in the interval
 // |duration| - intended wallclock duration of the interval
 // |roughness| - roughness of the interval
-using PlaybackRoughnessReportingCallback = base::RepeatingCallback<
-    void(int frames, base::TimeDelta duration, double roughness)>;
+// |refresh_rate_hz| - display refresh rate, usually 60Hz
+// |frame_size| - size of the video frames in the interval
+using PlaybackRoughnessReportingCallback =
+    base::RepeatingCallback<void(int frames,
+                                 base::TimeDelta duration,
+                                 double roughness,
+                                 int refresh_rate_hz,
+                                 gfx::Size frame_size)>;
 
 // This class tracks moments when each frame was submitted
 // and when it was displayed. Then series of frames split into groups
@@ -91,12 +98,16 @@
     base::Optional<base::TimeTicks> presentation_time;
     base::Optional<base::TimeDelta> actual_duration;
     base::Optional<base::TimeDelta> intended_duration;
+    int refresh_rate_hz = 60;
+    gfx::Size size;
   };
 
   struct ConsecutiveFramesWindow {
     int size;
     base::TimeTicks first_frame_time;
     base::TimeDelta intended_duration;
+    int refresh_rate_hz = 60;
+    gfx::Size frame_size;
 
     // Worst case difference between a frame's intended duration and
     // actual duration, calculated for all frames in the window.
diff --git a/cc/metrics/video_playback_roughness_reporter_unittest.cc b/cc/metrics/video_playback_roughness_reporter_unittest.cc
index 053c1e77..3d7acf8 100644
--- a/cc/metrics/video_playback_roughness_reporter_unittest.cc
+++ b/cc/metrics/video_playback_roughness_reporter_unittest.cc
@@ -22,6 +22,8 @@
 class VideoPlaybackRoughnessReporterTest : public ::testing::Test {
  protected:
   std::unique_ptr<VideoPlaybackRoughnessReporter> reporter_;
+  base::TimeTicks time_;
+  int token_ = 0;
 
   template <class T>
   void SetReportingCallabck(T cb) {
@@ -34,9 +36,10 @@
     return reporter_.get();
   }
 
-  scoped_refptr<VideoFrame> MakeFrame(base::TimeDelta duration) {
+  scoped_refptr<VideoFrame> MakeFrame(base::TimeDelta duration,
+                                      int frame_size = 100) {
     scoped_refptr<VideoFrame> result = media::VideoFrame::CreateColorFrame(
-        gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta());
+        gfx::Size(frame_size, frame_size), 0x80, 0x80, 0x80, base::TimeDelta());
     result->metadata()->wallclock_frame_duration = duration;
     return result;
   }
@@ -57,18 +60,21 @@
     return ::testing::AssertionSuccess();
   }
 
-  void NormalRun(double fps, double hz, std::vector<int> cadence, int frames) {
+  void NormalRun(double fps,
+                 double hz,
+                 std::vector<int> cadence,
+                 int frames,
+                 int frame_size = 100) {
     base::TimeDelta vsync = base::TimeDelta::FromSecondsD(1 / hz);
     base::TimeDelta ideal_duration = base::TimeDelta::FromSecondsD(1 / fps);
-    base::TimeTicks time;
     for (int idx = 0; idx < frames; idx++) {
       int frame_cadence = cadence[idx % cadence.size()];
       base::TimeDelta duration = vsync * frame_cadence;
-      auto frame = MakeFrame(ideal_duration);
-      reporter()->FrameSubmitted(idx, *frame, vsync);
-      reporter()->FramePresented(idx, time, true);
+      auto frame = MakeFrame(ideal_duration, frame_size);
+      reporter()->FrameSubmitted(token_, *frame, vsync);
+      reporter()->FramePresented(token_++, time_, true);
       reporter()->ProcessFrameWindow();
-      time += duration;
+      time_ += duration;
     }
   }
 
@@ -78,19 +84,17 @@
                             int frames) {
     base::TimeDelta vsync = base::TimeDelta::FromSecondsD(1 / hz);
     base::TimeDelta ideal_duration = base::TimeDelta::FromSecondsD(1 / fps);
-    base::TimeTicks time;
     constexpr int batch_size = 3;
     for (int idx = 0; idx < frames; idx++) {
       auto frame = MakeFrame(ideal_duration);
       reporter()->FrameSubmitted(idx, *frame, vsync);
-
       if (idx % batch_size == batch_size - 1) {
         for (int i = batch_size - 1; i >= 0; i--) {
           int presented_idx = idx - i;
           int frame_cadence = cadence[presented_idx % cadence.size()];
           base::TimeDelta duration = vsync * frame_cadence;
-          reporter()->FramePresented(presented_idx, time, true);
-          time += duration;
+          reporter()->FramePresented(presented_idx, time_, true);
+          time_ += duration;
         }
       }
 
@@ -102,13 +106,14 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, BestCase24fps) {
   int call_count = 0;
   int fps = 24;
-  SetReportingCallabck(
-      [&](int size, base::TimeDelta duration, double roughness) {
-        ASSERT_EQ(size, fps);
-        ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
-        ASSERT_NEAR(roughness, 5.9, 0.1);
-        call_count++;
-      });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) {
+    ASSERT_EQ(size, fps);
+    ASSERT_EQ(hz, 60);
+    ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+    ASSERT_NEAR(roughness, 5.9, 0.1);
+    call_count++;
+  });
   int frames_to_run =
       VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 10;
   NormalRun(fps, 60, {2, 3}, frames_to_run);
@@ -118,13 +123,14 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, BestCase24fpsOn120Hz) {
   int call_count = 0;
   int fps = 24;
-  SetReportingCallabck(
-      [&](int size, base::TimeDelta duration, double roughness) {
-        ASSERT_EQ(size, fps);
-        ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
-        ASSERT_NEAR(roughness, 0.0, 0.1);
-        call_count++;
-      });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) {
+    ASSERT_EQ(size, fps);
+    ASSERT_EQ(hz, 120);
+    ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+    ASSERT_NEAR(roughness, 0.0, 0.1);
+    call_count++;
+  });
   int frames_to_run =
       VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 10;
   NormalRun(fps, 120, {5}, frames_to_run);
@@ -134,13 +140,13 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, BestCase30fps) {
   int call_count = 0;
   int fps = 30;
-  SetReportingCallabck(
-      [&](int size, base::TimeDelta duration, double roughness) {
-        ASSERT_EQ(size, fps);
-        ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
-        ASSERT_NEAR(roughness, 0.0, 0.1);
-        call_count++;
-      });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) {
+    ASSERT_EQ(size, fps);
+    ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+    ASSERT_NEAR(roughness, 0.0, 0.1);
+    call_count++;
+  });
   int frames_to_run =
       VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
   NormalRun(fps, 60, {2}, frames_to_run);
@@ -156,13 +162,13 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, UserStudyOkay) {
   int call_count = 0;
   int fps = 30;
-  SetReportingCallabck(
-      [&](int size, base::TimeDelta duration, double roughness) {
-        ASSERT_EQ(size, fps);
-        ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
-        ASSERT_NEAR(roughness, 4.3, 0.1);
-        call_count++;
-      });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) {
+    ASSERT_EQ(size, fps);
+    ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+    ASSERT_NEAR(roughness, 4.3, 0.1);
+    call_count++;
+  });
   int frames_to_run =
       VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
   NormalRun(fps, 60, {2, 2, 2, 2, 2, 2, 1, 3, 2, 2, 2, 2, 2, 2, 2},
@@ -178,13 +184,13 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, UserStudyBad) {
   int call_count = 0;
   int fps = 30;
-  SetReportingCallabck(
-      [&](int size, base::TimeDelta duration, double roughness) {
-        ASSERT_EQ(size, fps);
-        ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
-        ASSERT_NEAR(roughness, 7.46, 0.1);
-        call_count++;
-      });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) {
+    ASSERT_EQ(size, fps);
+    ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+    ASSERT_NEAR(roughness, 7.46, 0.1);
+    call_count++;
+  });
   int frames_to_run =
       VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
   NormalRun(fps, 60, {2, 2, 2, 2, 2, 1, 2, 2, 3, 2, 2, 2, 2, 2, 2},
@@ -195,13 +201,13 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, Glitchy24fps) {
   int call_count = 0;
   int fps = 24;
-  SetReportingCallabck(
-      [&](int size, base::TimeDelta duration, double roughness) {
-        ASSERT_EQ(size, fps);
-        ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
-        ASSERT_NEAR(roughness, 14.8, 0.1);
-        call_count++;
-      });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) {
+    ASSERT_EQ(size, fps);
+    ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+    ASSERT_NEAR(roughness, 14.8, 0.1);
+    call_count++;
+  });
   int frames_to_run =
       VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
   NormalRun(fps, 60, {2, 3, 1, 3, 2, 4, 2, 3, 2, 3, 3, 3}, frames_to_run);
@@ -211,13 +217,13 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, BestCase60fps) {
   int call_count = 0;
   int fps = 60;
-  SetReportingCallabck(
-      [&](int size, base::TimeDelta duration, double roughness) {
-        ASSERT_EQ(size, fps);
-        ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
-        ASSERT_NEAR(roughness, 0.0, 0.1);
-        call_count++;
-      });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) {
+    ASSERT_EQ(size, fps);
+    ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+    ASSERT_NEAR(roughness, 0.0, 0.1);
+    call_count++;
+  });
   int frames_to_run =
       VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
   NormalRun(fps, 60, {1}, frames_to_run);
@@ -227,13 +233,13 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, BestCase50fps) {
   int call_count = 0;
   int fps = 50;
-  SetReportingCallabck(
-      [&](int size, base::TimeDelta duration, double roughness) {
-        ASSERT_EQ(size, fps);
-        ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
-        ASSERT_NEAR(roughness, 8.1, 01);
-        call_count++;
-      });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) {
+    ASSERT_EQ(size, fps);
+    ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+    ASSERT_NEAR(roughness, 8.1, 01);
+    call_count++;
+  });
   int frames_to_run =
       VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
   NormalRun(fps, 60, {1, 1, 1, 1, 2}, frames_to_run);
@@ -253,7 +259,8 @@
   base::TimeDelta error = base::TimeDelta::FromMillisecondsD(
       std::sqrt(intended_roughness * intended_roughness * frames_in_window));
 
-  auto callback = [&](int size, base::TimeDelta duration, double roughness) {
+  auto callback = [&](int size, base::TimeDelta duration, double roughness,
+                      int hz, gfx::Size frame_size) {
     ASSERT_EQ(frames_in_window, size);
     ASSERT_NEAR(roughness, intended_roughness, 0.1);
     call_count++;
@@ -295,7 +302,8 @@
   std::mt19937 rnd(1);
   std::shuffle(targets.begin(), targets.end(), rnd);
 
-  auto callback = [&](int size, base::TimeDelta duration, double roughness) {
+  auto callback = [&](int size, base::TimeDelta duration, double roughness,
+                      int hz, gfx::Size frame_size) {
     ASSERT_EQ(frames_in_window, size);
     ASSERT_NEAR(roughness, expected_roughness, 0.05);
     call_count++;
@@ -330,8 +338,8 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, LongRunWithoutWindows) {
   int call_count = 0;
   base::TimeDelta vsync = base::TimeDelta::FromMilliseconds(1);
-  SetReportingCallabck([&](int size, base::TimeDelta duration,
-                           double roughness) { call_count++; });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) { call_count++; });
   for (int i = 0; i < 10000; i++) {
     auto frame = MakeFrame(vsync);
     reporter()->FrameSubmitted(i, *frame, vsync);
@@ -348,8 +356,8 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, PresentingUnknownFrames) {
   int call_count = 0;
   base::TimeDelta vsync = base::TimeDelta::FromMilliseconds(1);
-  SetReportingCallabck([&](int size, base::TimeDelta duration,
-                           double roughness) { call_count++; });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) { call_count++; });
   for (int i = 0; i < 10000; i++) {
     auto frame = MakeFrame(vsync);
     reporter()->FrameSubmitted(i, *frame, vsync);
@@ -365,8 +373,8 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, IgnoringUnreliableTimings) {
   int call_count = 0;
   base::TimeDelta vsync = base::TimeDelta::FromMilliseconds(1);
-  SetReportingCallabck([&](int size, base::TimeDelta duration,
-                           double roughness) { call_count++; });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) { call_count++; });
   for (int i = 0; i < 10000; i++) {
     auto frame = MakeFrame(vsync);
     reporter()->FrameSubmitted(i, *frame, vsync);
@@ -382,9 +390,8 @@
 TEST_F(VideoPlaybackRoughnessReporterTest, ReportingInReset) {
   int call_count = 0;
   int fps = 60;
-  auto callback = [&](int size, base::TimeDelta duration, double roughness) {
-    call_count++;
-  };
+  auto callback = [&](int size, base::TimeDelta duration, double roughness,
+                      int hz, gfx::Size frame_size) { call_count++; };
   SetReportingCallabck(callback);
 
   // Set number of frames insufficient for reporting in Reset()
@@ -411,6 +418,50 @@
   EXPECT_EQ(call_count, 1);
 }
 
+// Test that a change of display refresh rate or frame size causes reporting
+// iff there is sufficient number of windows accumulated.
+TEST_F(VideoPlaybackRoughnessReporterTest, ReportingAfterParameterChange) {
+  struct Report {
+    int hz;
+    int height;
+    double roughness;
+  };
+  std::vector<Report> reports;
+  int fps = 60;
+  auto callback = [&](int size, base::TimeDelta duration, double roughness,
+                      int hz, gfx::Size frame_size) {
+    reports.push_back({hz, frame_size.height(), roughness});
+  };
+  SetReportingCallabck(callback);
+
+  int frames_to_run =
+      (VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit - 1) * fps + 3;
+  NormalRun(fps, 59, {1}, frames_to_run, 480);
+  ASSERT_TRUE(reports.empty());
+
+  frames_to_run =
+      (VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit + 1) * fps + 3;
+  NormalRun(fps, 60, {1}, frames_to_run, 480);
+  // Check that if parameters change after only a few windows, nothing gets
+  // reported.
+  ASSERT_TRUE(reports.empty());
+
+  frames_to_run =
+      (VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit + 1) * fps + 3;
+  NormalRun(fps, 120, {2}, frames_to_run, 481);
+
+  // Check that if parameters change after sufficient number of windows
+  // roughness is reported. The second report is done normally after max
+  // number of windows is seen.
+  ASSERT_EQ(reports.size(), 2u);
+  EXPECT_EQ(reports[0].hz, 60);
+  EXPECT_EQ(reports[0].height, 480);
+  EXPECT_EQ(reports[0].roughness, 0.0);
+  EXPECT_EQ(reports[1].hz, 120);
+  EXPECT_EQ(reports[1].height, 481);
+  EXPECT_EQ(reports[1].roughness, 0.0);
+}
+
 // Test that reporting works even if frame presentation signal come out of
 // order.
 TEST_F(VideoPlaybackRoughnessReporterTest, BatchPresentation) {
@@ -418,26 +469,26 @@
   int fps = 60;
 
   // Try 60 fps
-  SetReportingCallabck(
-      [&](int size, base::TimeDelta duration, double roughness) {
-        ASSERT_EQ(size, fps);
-        ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
-        ASSERT_NEAR(roughness, 0.0, 0.1);
-        call_count++;
-      });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) {
+    ASSERT_EQ(size, fps);
+    ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+    ASSERT_NEAR(roughness, 0.0, 0.1);
+    call_count++;
+  });
   int frames_to_run =
       VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 10;
   BatchPresentationRun(fps, 60, {1}, frames_to_run);
   EXPECT_EQ(call_count, 1);
 
   // Try 24fps
-  SetReportingCallabck(
-      [&](int size, base::TimeDelta duration, double roughness) {
-        ASSERT_EQ(size, fps);
-        ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
-        ASSERT_NEAR(roughness, 5.9, 0.1);
-        call_count++;
-      });
+  SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+                           int hz, gfx::Size frame_size) {
+    ASSERT_EQ(size, fps);
+    ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+    ASSERT_NEAR(roughness, 5.9, 0.1);
+    call_count++;
+  });
   fps = 24;
   frames_to_run =
       VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 10;
diff --git a/cc/tiles/picture_layer_tiling.cc b/cc/tiles/picture_layer_tiling.cc
index aefe88d2..3e6492e 100644
--- a/cc/tiles/picture_layer_tiling.cc
+++ b/cc/tiles/picture_layer_tiling.cc
@@ -25,7 +25,6 @@
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
 
 namespace cc {
diff --git a/chrome/VERSION b/chrome/VERSION
index b4aa23b..a2e08d1 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=86
 MINOR=0
-BUILD=4187
+BUILD=4188
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationCoordinator.java
index 55162cc..e1fce46 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationCoordinator.java
@@ -9,6 +9,8 @@
 import android.os.Build;
 import android.view.View;
 
+import androidx.annotation.VisibleForTesting;
+
 import org.chromium.base.Consumer;
 import org.chromium.base.Function;
 import org.chromium.base.supplier.Supplier;
@@ -18,7 +20,6 @@
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
@@ -28,8 +29,8 @@
 /**
  * Coordinator object for gesture navigation.
  */
-public class HistoryNavigationCoordinator implements InsetObserverView.WindowInsetObserver,
-                                                     Destroyable, PauseResumeWithNativeObserver {
+public class HistoryNavigationCoordinator
+        implements InsetObserverView.WindowInsetObserver, PauseResumeWithNativeObserver {
     private final Runnable mUpdateNavigationStateRunnable = this::onNavigationStateChanged;
 
     private CompositorViewHolder mCompositorViewHolder;
@@ -60,16 +61,18 @@
      * @param bottomSheetControllerSupplier Supplier for {@link BottomSheetController}.
      * @return HistoryNavigationCoordinator object or null if not enabled via feature flag.
      */
-    public static void create(ActivityLifecycleDispatcher lifecycleDispatcher,
+    public static HistoryNavigationCoordinator create(
+            ActivityLifecycleDispatcher lifecycleDispatcher,
             CompositorViewHolder compositorViewHolder, ActivityTabProvider tabProvider,
             InsetObserverView insetObserverView, Function<Tab, Boolean> backShouldCloseTab,
             Runnable onBackPressed, Consumer<Tab> showHistoryManager, String historyMenu,
             Supplier<BottomSheetController> bottomSheetControllerSupplier) {
-        if (!isFeatureFlagEnabled()) return;
+        if (!isFeatureFlagEnabled()) return null;
         HistoryNavigationCoordinator coordinator = new HistoryNavigationCoordinator();
         coordinator.init(lifecycleDispatcher, compositorViewHolder, tabProvider, insetObserverView,
                 backShouldCloseTab, onBackPressed, showHistoryManager, historyMenu,
                 bottomSheetControllerSupplier);
+        return coordinator;
     }
 
     /**
@@ -232,7 +235,9 @@
     @Override
     public void onPauseWithNative() {}
 
-    @Override
+    /**
+     * Destroy HistoryNavigationCoordinator object.
+     */
     public void destroy() {
         if (mActivityTabObserver != null) {
             mActivityTabObserver.destroy();
@@ -256,4 +261,10 @@
             mActivityLifecycleDispatcher = null;
         }
     }
+
+    @VisibleForTesting
+    NavigationHandler getNavigationHandlerForTesting() {
+        assert mNavigationLayout != null;
+        return mNavigationLayout.getNavigationHandler();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index 692f080..789ff88e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -68,6 +68,7 @@
     private UrlFocusChangeListener mUrlFocusChangeListener;
     private @Nullable ToolbarButtonInProductHelpController mToolbarButtonInProductHelpController;
     private boolean mIntentWithEffect;
+    private HistoryNavigationCoordinator mHistoryNavigationCoordinator;
 
     /**
      * Construct a new TabbedRootUiCoordinator.
@@ -130,6 +131,10 @@
             mToolbarButtonInProductHelpController.destroy();
         }
 
+        if (mHistoryNavigationCoordinator != null) {
+            mHistoryNavigationCoordinator.destroy();
+            mHistoryNavigationCoordinator = null;
+        }
         super.destroy();
     }
 
@@ -188,11 +193,10 @@
         initStatusIndicatorCoordinator(layoutManager);
 
         // clang-format off
-        HistoryNavigationCoordinator.create(mActivity.getLifecycleDispatcher(),
-                mActivity.getCompositorViewHolder(), mActivity.getActivityTabProvider(),
-                mActivity.getInsetObserverView(),
-                mActivity::backShouldCloseTab,
-                mActivity::onBackPressed,
+        mHistoryNavigationCoordinator = HistoryNavigationCoordinator.create(
+                mActivity.getLifecycleDispatcher(), mActivity.getCompositorViewHolder(),
+                mActivity.getActivityTabProvider(), mActivity.getInsetObserverView(),
+                mActivity::backShouldCloseTab, mActivity::onBackPressed,
                 tab -> HistoryManagerUtils.showHistoryManager(mActivity, tab),
                 mActivity.getResources().getString(R.string.show_full_history),
                 () -> mActivity.isActivityFinishingOrDestroyed() ? null
@@ -302,6 +306,11 @@
         return mEphemeralTabCoordinatorSupplier.get();
     }
 
+    @VisibleForTesting
+    public HistoryNavigationCoordinator getHistoryNavigationCoordinatorForTesting() {
+        return mHistoryNavigationCoordinator;
+    }
+
     /**
      * Triggers the display of an appropriate promo, if any, returning true if a promo is actually
      * displayed.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java
index 1dd5130..1ebbff09 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java
@@ -6,9 +6,7 @@
 
 import android.app.Activity;
 import android.graphics.Point;
-import android.os.Build;
 import android.os.Handler;
-import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.util.DisplayMetrics;
 
@@ -24,11 +22,10 @@
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisableIf;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.browser.compositor.animation.CompositorAnimationHandler;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabbed_mode.TabbedRootUiCoordinator;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
@@ -36,7 +33,6 @@
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.content_public.browser.test.util.TouchCommon;
 import org.chromium.content_public.common.ContentUrlConstants;
 import org.chromium.net.test.EmbeddedTestServer;
 
@@ -54,6 +50,7 @@
     private static final int PAGELOAD_TIMEOUT_MS = 4000;
 
     private EmbeddedTestServer mTestServer;
+    private NavigationHandler mNavigationHandler;
     private float mEdgeWidthPx;
 
     @Rule
@@ -67,6 +64,11 @@
         mActivityTestRule.getActivity().getWindowManager().getDefaultDisplay().getMetrics(
                 displayMetrics);
         mEdgeWidthPx = displayMetrics.density * NavigationHandler.EDGE_WIDTH_DP;
+        TabbedRootUiCoordinator uiCoordinator =
+                (TabbedRootUiCoordinator) mActivityTestRule.getActivity()
+                        .getRootUiCoordinatorForTesting();
+        mNavigationHandler = uiCoordinator.getHistoryNavigationCoordinatorForTesting()
+                                     .getNavigationHandlerForTesting();
     }
 
     @After
@@ -94,16 +96,19 @@
         mActivityTestRule.getActivity().getWindowManager().getDefaultDisplay().getSize(size);
 
         // Swipe from an edge toward the middle of the screen.
-        float dragStartX = leftEdge ? mEdgeWidthPx / 2 : size.x - mEdgeWidthPx / 2;
-        float dragEndX = size.x / 2;
-        float dragStartY = size.y / 2;
-        float dragEndY = size.y / 2;
-        long downTime = SystemClock.uptimeMillis();
+        final int eventCounts = 100;
+        final float startx = leftEdge ? mEdgeWidthPx / 2 : size.x - mEdgeWidthPx / 2;
+        final float y = size.y / 2;
+        final float step = (size.x / 2 - startx) / eventCounts;
 
-        TouchCommon.dragStart(mActivityTestRule.getActivity(), dragStartX, dragStartY, downTime);
-        TouchCommon.dragTo(mActivityTestRule.getActivity(), dragStartX, dragEndX, dragStartY,
-                dragEndY, /* stepCount= */ 100, downTime);
-        TouchCommon.dragEnd(mActivityTestRule.getActivity(), dragEndX, dragEndY, downTime);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mNavigationHandler.onDown();
+            float endx = startx + step;
+            for (int i = 0; i < eventCounts; i++, endx += step) {
+                mNavigationHandler.onScroll(startx, -step, 0, endx, y);
+            }
+            mNavigationHandler.release(true);
+        });
     }
 
     @Test
@@ -120,7 +125,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "Flaky, see https://crbug.com/1041233 and https://crbug.com/1091417")
     public void testSwipeNavigateOnNativePage() {
         mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
         mActivityTestRule.loadUrl(UrlConstants.RECENT_TABS_URL);
@@ -130,8 +134,6 @@
 
     @Test
     @SmallTest
-    @DisableIf.
-    Build(sdk_is_greater_than = Build.VERSION_CODES.O_MR1, message = "Flaky on P crbug.com/1041233")
     public void testSwipeNavigateOnRenderedPage() {
         mTestServer = EmbeddedTestServer.createAndStartServer(
                 InstrumentationRegistry.getInstrumentation().getContext());
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index a004a0f..74a44f5 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6931,7 +6931,7 @@
         <message name="IDS_EXTENSIONS_INSTALL_LOCATION_3RD_PARTY" desc="The text explaining the the installation came from a 3rd party app.">
           Installed by a third party.
         </message>
-        <message name="IDS_EXTENSIONS_INSTALL_LOCATION_ENTERPRISE" desc="The text explaining the the installation of the extension came from an enterprise policy.">
+        <message name="IDS_EXTENSIONS_INSTALL_LOCATION_ENTERPRISE" desc="The text explaining that the installation of the extension came from an enterprise policy.">
           Installed by enterprise policy.
         </message>
         <message name="IDS_EXTENSIONS_INSTALL_LOCATION_SHARED_MODULE" desc="The text explaining the the installation of the extension was because of extensions that depend on this shared module">
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 71239ac..1d2ce12 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -2801,6 +2801,14 @@
     App details
   </message>
 
+  <!-- Apps > Manage your apps > Parallels -->
+  <message name="IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_CAMERA_LABEL" desc="Label for the Parallels permissions dialog when camera permissions are being adjusted">
+    The change in camera setting requires Parallels Desktop to relaunch
+  </message>
+  <message name="IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_MICROPHONE_LABEL" desc="Label for the Parallels permissions dialog when microphone permissions are being adjusted">
+    The change in microphone setting requires Parallels Desktop to relaunch
+  </message>
+
   <!-- Apps > Manage your apps > Parallels > Shared folders -->
   <message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS" desc="Label for managing shared folders for Parallels.">
     Manage shared folders
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_CAMERA_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_CAMERA_LABEL.png.sha1
new file mode 100644
index 0000000..a8bec807
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_CAMERA_LABEL.png.sha1
@@ -0,0 +1 @@
+2bbc97e60c184025e970669308edeeecc357b8c6
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_MICROPHONE_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_MICROPHONE_LABEL.png.sha1
new file mode 100644
index 0000000..ce9f9ef
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_MICROPHONE_LABEL.png.sha1
@@ -0,0 +1 @@
+861539d267a6410e439a59d64fbaa42539c15bd6
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 919994a7..13b5d8e 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3980,6 +3980,8 @@
 
   if (chromeos_is_browser_only) {
     sources += [
+      "chrome_browser_main_extra_parts_lacros.cc",
+      "chrome_browser_main_extra_parts_lacros.h",
       "metrics/lacros_metrics_provider.cc",
       "metrics/lacros_metrics_provider.h",
     ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 8efe621..41ebdda9 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -5410,6 +5410,9 @@
     {"new-os-settings-search", flag_descriptions::kNewOsSettingsSearchName,
      flag_descriptions::kNewOsSettingsSearchDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kNewOsSettingsSearch)},
+    {"os-settings-deep-linking", flag_descriptions::kOsSettingsDeepLinkingName,
+     flag_descriptions::kOsSettingsDeepLinkingDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kNewOsSettingsSearch)},
     {"dlc-settings-ui", flag_descriptions::kDlcSettingsUiName,
      flag_descriptions::kDlcSettingsUiDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kDlcSettingsUi)},
diff --git a/chrome/browser/android/compositor/layer/tab_layer.cc b/chrome/browser/android/compositor/layer/tab_layer.cc
index ee38250..687c861 100644
--- a/chrome/browser/android/compositor/layer/tab_layer.cc
+++ b/chrome/browser/android/compositor/layer/tab_layer.cc
@@ -25,7 +25,6 @@
 #include "ui/gfx/geometry/insets_f.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/transform.h"
 
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc
index 4113622..e976b84 100644
--- a/chrome/browser/android/webapk/webapk_installer_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -278,7 +278,8 @@
 
 class WebApkInstallerTest : public ::testing::Test {
  public:
-  typedef base::Callback<std::unique_ptr<net::test_server::HttpResponse>(void)>
+  typedef base::RepeatingCallback<
+      std::unique_ptr<net::test_server::HttpResponse>(void)>
       WebApkResponseBuilder;
 
   WebApkInstallerTest()
@@ -326,7 +327,7 @@
 
   // Sets the function that should be used to build the response to the
   // WebAPK creation request.
-  void SetWebApkResponseBuilder(const WebApkResponseBuilder& builder) {
+  void SetWebApkResponseBuilder(WebApkResponseBuilder builder) {
     webapk_response_builder_ = builder;
   }
 
@@ -341,7 +342,8 @@
   // Sets default configuration for running WebApkInstaller.
   void SetDefaults() {
     SetWebApkServerUrl(test_server_.GetURL(kServerUrl));
-    SetWebApkResponseBuilder(base::Bind(&BuildValidWebApkResponse, kToken));
+    SetWebApkResponseBuilder(
+        base::BindRepeating(&BuildValidWebApkResponse, kToken));
   }
 
   std::unique_ptr<net::test_server::HttpResponse> HandleWebApkRequest(
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc
index b754ce7b..e98b050 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.cc
+++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -240,7 +240,7 @@
 }
 
 // This pipeline is meant to:
-// * Simplify loading icons, as things like effects and compression are common
+// * Simplify loading icons, as things like effects and type are common
 //   to all loading.
 // * Allow the caller to halt the process by destructing the loader at any time,
 // * Allow easy additions to the pipeline if necessary (like new effects or
@@ -251,13 +251,13 @@
   static const int kFaviconFallbackImagePx =
       extension_misc::EXTENSION_ICON_BITTY;
 
-  IconLoadingPipeline(apps::mojom::IconCompression icon_compression,
+  IconLoadingPipeline(apps::mojom::IconType icon_type,
                       int size_hint_in_dip,
                       bool is_placeholder_icon,
                       apps::IconEffects icon_effects,
                       int fallback_icon_resource,
                       apps::mojom::Publisher::LoadIconCallback callback)
-      : IconLoadingPipeline(icon_compression,
+      : IconLoadingPipeline(icon_type,
                             size_hint_in_dip,
                             is_placeholder_icon,
                             icon_effects,
@@ -267,7 +267,7 @@
                             std::move(callback)) {}
 
   IconLoadingPipeline(
-      apps::mojom::IconCompression icon_compression,
+      apps::mojom::IconType icon_type,
       int size_hint_in_dip,
       bool is_placeholder_icon,
       apps::IconEffects icon_effects,
@@ -275,7 +275,7 @@
       base::OnceCallback<void(apps::mojom::Publisher::LoadIconCallback)>
           fallback,
       apps::mojom::Publisher::LoadIconCallback callback)
-      : icon_compression_(icon_compression),
+      : icon_type_(icon_type),
         size_hint_in_dip_(size_hint_in_dip),
         is_placeholder_icon_(is_placeholder_icon),
         icon_effects_(icon_effects),
@@ -314,7 +314,7 @@
 
   void MaybeLoadFallbackOrCompleteEmpty();
 
-  apps::mojom::IconCompression icon_compression_;
+  apps::mojom::IconType icon_type_;
   int size_hint_in_dip_;
   bool is_placeholder_icon_;
   apps::IconEffects icon_effects_;
@@ -352,8 +352,8 @@
   fallback_favicon_url_ = launch_url;
   profile_ = profile;
   if (icon_manager.HasSmallestIcon(web_app_id, icon_size_in_px)) {
-    switch (icon_compression_) {
-      case apps::mojom::IconCompression::kCompressed:
+    switch (icon_type_) {
+      case apps::mojom::IconType::kCompressed:
         if (icon_effects_ == apps::IconEffects::kNone) {
           icon_manager.ReadSmallestCompressedIcon(
               web_app_id, icon_size_in_px,
@@ -362,7 +362,7 @@
           return;
         }
         FALLTHROUGH;
-      case apps::mojom::IconCompression::kUncompressed: {
+      case apps::mojom::IconType::kUncompressed: {
         // For uncompressed icon, apply the resize and pad effect.
         icon_effects_ = static_cast<apps::IconEffects>(
             icon_effects_ | apps::IconEffects::kResizeAndPad);
@@ -375,7 +375,7 @@
             icon_effects_ & ~apps::IconEffects::kCrOsStandardMask);
         FALLTHROUGH;
       }
-      case apps::mojom::IconCompression::kStandard:
+      case apps::mojom::IconType::kStandard:
         // If |icon_effects| are requested, we must always load the
         // uncompressed image to apply the icon effects, and then re-encode the
         // image if the compressed icon is requested.
@@ -385,7 +385,7 @@
                 &IconLoadingPipeline::MaybeApplyEffectsAndComplete,
                 base::WrapRefCounted(this))));
         return;
-      case apps::mojom::IconCompression::kUnknown:
+      case apps::mojom::IconType::kUnknown:
         break;
     }
   }
@@ -405,8 +405,8 @@
   fallback_favicon_url_ =
       extensions::AppLaunchInfo::GetFullLaunchURL(extension);
   profile_ = Profile::FromBrowserContext(context);
-  switch (icon_compression_) {
-    case apps::mojom::IconCompression::kCompressed:
+  switch (icon_type_) {
+    case apps::mojom::IconType::kCompressed:
       // For compressed icons with no |icon_effects|, serve the
       // already-compressed bytes.
       if (icon_effects_ == apps::IconEffects::kNone) {
@@ -426,9 +426,9 @@
         return;
       }
       FALLTHROUGH;
-    case apps::mojom::IconCompression::kUncompressed:
+    case apps::mojom::IconType::kUncompressed:
       FALLTHROUGH;
-    case apps::mojom::IconCompression::kStandard:
+    case apps::mojom::IconType::kStandard:
       // If |icon_effects| are requested, we must always load the
       // uncompressed image to apply the icon effects, and then re-encode
       // the image if the compressed icon is requested.
@@ -438,7 +438,7 @@
               base::BindOnce(&IconLoadingPipeline::MaybeApplyEffectsAndComplete,
                              base::WrapRefCounted(this))));
       return;
-    case apps::mojom::IconCompression::kUnknown:
+    case apps::mojom::IconType::kUnknown:
       break;
   }
 
@@ -474,8 +474,8 @@
     return;
   }
 
-  switch (icon_compression_) {
-    case apps::mojom::IconCompression::kCompressed:
+  switch (icon_type_) {
+    case apps::mojom::IconType::kCompressed:
       // For compressed icons with no |icon_effects|, serve the
       // already-compressed bytes.
       if (icon_effects_ == apps::IconEffects::kNone) {
@@ -486,9 +486,9 @@
         return;
       }
       FALLTHROUGH;
-    case apps::mojom::IconCompression::kUncompressed:
+    case apps::mojom::IconType::kUncompressed:
       FALLTHROUGH;
-    case apps::mojom::IconCompression::kStandard: {
+    case apps::mojom::IconType::kStandard: {
       // For compressed icons with |icon_effects|, or for uncompressed
       // icons, we load the uncompressed image, apply the icon effects, and
       // then re-encode the image if necessary.
@@ -518,7 +518,7 @@
       MaybeApplyEffectsAndComplete(scaled);
       return;
     }
-    case apps::mojom::IconCompression::kUnknown:
+    case apps::mojom::IconType::kUnknown:
       break;
   }
   MaybeLoadFallbackOrCompleteEmpty();
@@ -544,8 +544,8 @@
     apps::ApplyIconEffects(icon_effects_, size_hint_in_dip_, &processed_image);
   }
 
-  if (icon_compression_ == apps::mojom::IconCompression::kUncompressed ||
-      icon_compression_ == apps::mojom::IconCompression::kStandard) {
+  if (icon_type_ == apps::mojom::IconType::kUncompressed ||
+      icon_type_ == apps::mojom::IconType::kStandard) {
     CompleteWithImageSkia(processed_image);
     return;
   }
@@ -559,27 +559,27 @@
 }
 
 void IconLoadingPipeline::CompleteWithCompressed(std::vector<uint8_t> data) {
-  DCHECK_EQ(icon_compression_, apps::mojom::IconCompression::kCompressed);
+  DCHECK_EQ(icon_type_, apps::mojom::IconType::kCompressed);
   if (data.empty()) {
     MaybeLoadFallbackOrCompleteEmpty();
     return;
   }
   apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
-  iv->icon_compression = apps::mojom::IconCompression::kCompressed;
+  iv->icon_type = apps::mojom::IconType::kCompressed;
   iv->compressed = std::move(data);
   iv->is_placeholder_icon = is_placeholder_icon_;
   std::move(callback_).Run(std::move(iv));
 }
 
 void IconLoadingPipeline::CompleteWithImageSkia(gfx::ImageSkia image) {
-  DCHECK_NE(icon_compression_, apps::mojom::IconCompression::kCompressed);
-  DCHECK_NE(icon_compression_, apps::mojom::IconCompression::kUnknown);
+  DCHECK_NE(icon_type_, apps::mojom::IconType::kCompressed);
+  DCHECK_NE(icon_type_, apps::mojom::IconType::kUnknown);
   if (image.isNull()) {
     MaybeLoadFallbackOrCompleteEmpty();
     return;
   }
   apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
-  iv->icon_compression = icon_compression_;
+  iv->icon_type = icon_type_;
   iv->uncompressed = std::move(image);
   iv->is_placeholder_icon = is_placeholder_icon_;
   std::move(callback_).Run(std::move(iv));
@@ -708,7 +708,7 @@
   }
 }
 
-void LoadIconFromExtension(apps::mojom::IconCompression icon_compression,
+void LoadIconFromExtension(apps::mojom::IconType icon_type,
                            int size_hint_in_dip,
                            content::BrowserContext* context,
                            const std::string& extension_id,
@@ -719,7 +719,7 @@
   constexpr bool is_placeholder_icon = false;
   scoped_refptr<IconLoadingPipeline> icon_loader =
       base::MakeRefCounted<IconLoadingPipeline>(
-          icon_compression, size_hint_in_dip, is_placeholder_icon, icon_effects,
+          icon_type, size_hint_in_dip, is_placeholder_icon, icon_effects,
           IDR_APP_DEFAULT_ICON, std::move(callback));
   icon_loader->LoadExtensionIcon(
       extensions::ExtensionRegistry::Get(context)->GetInstalledExtension(
@@ -728,7 +728,7 @@
 }
 
 void LoadIconFromWebApp(content::BrowserContext* context,
-                        apps::mojom::IconCompression icon_compression,
+                        apps::mojom::IconType icon_type,
                         int size_hint_in_dip,
                         const std::string& web_app_id,
                         IconEffects icon_effects,
@@ -742,7 +742,7 @@
   constexpr bool is_placeholder_icon = false;
   scoped_refptr<IconLoadingPipeline> icon_loader =
       base::MakeRefCounted<IconLoadingPipeline>(
-          icon_compression, size_hint_in_dip, is_placeholder_icon, icon_effects,
+          icon_type, size_hint_in_dip, is_placeholder_icon, icon_effects,
           IDR_APP_DEFAULT_ICON, std::move(callback));
   icon_loader->LoadWebAppIcon(
       web_app_id, web_app_provider->registrar().GetAppLaunchURL(web_app_id),
@@ -750,7 +750,7 @@
 }
 
 void LoadIconFromFileWithFallback(
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int size_hint_in_dip,
     const base::FilePath& path,
     IconEffects icon_effects,
@@ -762,13 +762,13 @@
 
   scoped_refptr<IconLoadingPipeline> icon_loader =
       base::MakeRefCounted<IconLoadingPipeline>(
-          icon_compression, size_hint_in_dip, is_placeholder_icon, icon_effects,
+          icon_type, size_hint_in_dip, is_placeholder_icon, icon_effects,
           kInvalidIconResource, std::move(fallback), std::move(callback));
   icon_loader->LoadCompressedIconFromFile(path);
 }
 
 void LoadIconFromCompressedData(
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int size_hint_in_dip,
     IconEffects icon_effects,
     const std::string& compressed_icon_data,
@@ -778,12 +778,12 @@
 
   scoped_refptr<IconLoadingPipeline> icon_loader =
       base::MakeRefCounted<IconLoadingPipeline>(
-          icon_compression, size_hint_in_dip, is_placeholder_icon, icon_effects,
+          icon_type, size_hint_in_dip, is_placeholder_icon, icon_effects,
           kInvalidIconResource, std::move(callback));
   icon_loader->LoadIconFromCompressedData(compressed_icon_data);
 }
 
-void LoadIconFromResource(apps::mojom::IconCompression icon_compression,
+void LoadIconFromResource(apps::mojom::IconType icon_type,
                           int size_hint_in_dip,
                           int resource_id,
                           bool is_placeholder_icon,
@@ -795,7 +795,7 @@
 
   scoped_refptr<IconLoadingPipeline> icon_loader =
       base::MakeRefCounted<IconLoadingPipeline>(
-          icon_compression, size_hint_in_dip, is_placeholder_icon, icon_effects,
+          icon_type, size_hint_in_dip, is_placeholder_icon, icon_effects,
           fallback_icon_resource, std::move(callback));
   icon_loader->LoadIconFromResource(resource_id);
 }
diff --git a/chrome/browser/apps/app_service/app_icon_factory.h b/chrome/browser/apps/app_service/app_icon_factory.h
index 4312841..e5394e7d 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.h
+++ b/chrome/browser/apps/app_service/app_icon_factory.h
@@ -51,7 +51,7 @@
                       gfx::ImageSkia* image_skia);
 
 // Loads an icon from an extension.
-void LoadIconFromExtension(apps::mojom::IconCompression icon_compression,
+void LoadIconFromExtension(apps::mojom::IconType icon_type,
                            int size_hint_in_dip,
                            content::BrowserContext* context,
                            const std::string& extension_id,
@@ -60,7 +60,7 @@
 
 // Loads an icon from a web app.
 void LoadIconFromWebApp(content::BrowserContext* context,
-                        apps::mojom::IconCompression icon_compression,
+                        apps::mojom::IconType icon_type,
                         int size_hint_in_dip,
                         const std::string& web_app_id,
                         IconEffects icon_effects,
@@ -76,7 +76,7 @@
 // failure. A failure should be indicated by passing nullptr, in which case the
 // pipeline will use a generic fallback icon.
 void LoadIconFromFileWithFallback(
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int size_hint_in_dip,
     const base::FilePath& path,
     IconEffects icon_effects,
@@ -86,7 +86,7 @@
 
 // Creates an icon with the specified effects from |compressed_icon_data|.
 void LoadIconFromCompressedData(
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int size_hint_in_dip,
     IconEffects icon_effects,
     const std::string& compressed_icon_data,
@@ -94,7 +94,7 @@
 
 // Loads an icon from a compiled-into-the-binary resource, with a resource_id
 // named IDR_XXX, for some value of XXX.
-void LoadIconFromResource(apps::mojom::IconCompression icon_compression,
+void LoadIconFromResource(apps::mojom::IconType icon_type,
                           int size_hint_in_dip,
                           int resource_id,
                           bool is_placeholder_icon,
diff --git a/chrome/browser/apps/app_service/app_icon_factory_unittest.cc b/chrome/browser/apps/app_service/app_icon_factory_unittest.cc
index d8a5e6af..9c12e0f 100644
--- a/chrome/browser/apps/app_service/app_icon_factory_unittest.cc
+++ b/chrome/browser/apps/app_service/app_icon_factory_unittest.cc
@@ -34,7 +34,7 @@
     *fallback_called = false;
 
     apps::LoadIconFromFileWithFallback(
-        apps::mojom::IconCompression::kUncompressed, 200, GetPath(),
+        apps::mojom::IconType::kUncompressed, 200, GetPath(),
         apps::IconEffects::kNone,
         base::BindOnce(
             [](bool* called, apps::mojom::IconValuePtr* result,
@@ -114,7 +114,7 @@
   bool callback_called = false, fallback_called = false;
 
   apps::LoadIconFromFileWithFallback(
-      apps::mojom::IconCompression::kUncompressed, 200, GetPath(),
+      apps::mojom::IconType::kUncompressed, 200, GetPath(),
       apps::IconEffects::kNone,
       base::BindOnce(
           [](bool* called, apps::mojom::IconValuePtr* result,
diff --git a/chrome/browser/apps/app_service/app_icon_source.cc b/chrome/browser/apps/app_service/app_icon_source.cc
index afcc97f..952388d 100644
--- a/chrome/browser/apps/app_service/app_icon_source.cc
+++ b/chrome/browser/apps/app_service/app_icon_source.cc
@@ -104,7 +104,7 @@
       app_service_proxy->AppRegistryCache().GetAppType(app_id);
   constexpr bool allow_placeholder_icon = false;
   app_service_proxy->LoadIcon(
-      app_type, app_id, apps::mojom::IconCompression::kCompressed, size_in_dip,
+      app_type, app_id, apps::mojom::IconType::kCompressed, size_in_dip,
       allow_placeholder_icon,
       base::BindOnce(&RunCallback, std::move(callback)));
 }
diff --git a/chrome/browser/apps/app_service/app_service_proxy.cc b/chrome/browser/apps/app_service/app_service_proxy.cc
index 3928a7b..1c15017 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy.cc
@@ -68,14 +68,14 @@
     apps::mojom::AppType app_type,
     const std::string& app_id,
     apps::mojom::IconKeyPtr icon_key,
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int32_t size_hint_in_dip,
     bool allow_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback) {
   if (overriding_icon_loader_for_testing_) {
     return overriding_icon_loader_for_testing_->LoadIconFromIconKey(
-        app_type, app_id, std::move(icon_key), icon_compression,
-        size_hint_in_dip, allow_placeholder_icon, std::move(callback));
+        app_type, app_id, std::move(icon_key), icon_type, size_hint_in_dip,
+        allow_placeholder_icon, std::move(callback));
   }
 
   if (host_->app_service_.is_connected() && icon_key) {
@@ -86,7 +86,7 @@
     // yet and you resolve old one instead. Now new icon arrives asynchronously
     // but you no longer notify the app or do?"
     host_->app_service_->LoadIcon(app_type, app_id, std::move(icon_key),
-                                  icon_compression, size_hint_in_dip,
+                                  icon_type, size_hint_in_dip,
                                   allow_placeholder_icon, std::move(callback));
   } else {
     std::move(callback).Run(apps::mojom::IconValue::New());
@@ -241,12 +241,12 @@
     apps::mojom::AppType app_type,
     const std::string& app_id,
     apps::mojom::IconKeyPtr icon_key,
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int32_t size_hint_in_dip,
     bool allow_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback) {
   return outer_icon_loader_.LoadIconFromIconKey(
-      app_type, app_id, std::move(icon_key), icon_compression, size_hint_in_dip,
+      app_type, app_id, std::move(icon_key), icon_type, size_hint_in_dip,
       allow_placeholder_icon, std::move(callback));
 }
 
@@ -682,6 +682,10 @@
   apps::mojom::IconKeyPtr icon_key = update.IconKey();
   constexpr bool kAllowPlaceholderIcon = false;
   constexpr int32_t kIconSize = 48;
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
 
   // For browser tests, load the app icon, because there is no family link
   // logo for browser tests.
@@ -690,23 +694,26 @@
   // admin.
   if (!dialog_created_callback_.is_null() || !profile_->IsChild()) {
     LoadIconFromIconKey(update.AppType(), update.AppId(), std::move(icon_key),
-                        apps::mojom::IconCompression::kUncompressed, kIconSize,
-                        kAllowPlaceholderIcon, std::move(callback));
+                        icon_type, kIconSize, kAllowPlaceholderIcon,
+                        std::move(callback));
     return;
   }
 
   // Load the family link kite logo icon for the app pause dialog or the app
   // block dialog for the child profile.
-  LoadIconFromResource(apps::mojom::IconCompression::kUncompressed, kIconSize,
-                       IDR_SUPERVISED_USER_ICON, kAllowPlaceholderIcon,
-                       IconEffects::kNone, std::move(callback));
+  LoadIconFromResource(icon_type, kIconSize, IDR_SUPERVISED_USER_ICON,
+                       kAllowPlaceholderIcon, IconEffects::kNone,
+                       std::move(callback));
 }
 
 void AppServiceProxy::OnLoadIconForBlockDialog(
     const std::string& app_name,
     apps::mojom::IconValuePtr icon_value) {
-  if (icon_value->icon_compression !=
-      apps::mojom::IconCompression::kUncompressed) {
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
+  if (icon_value->icon_type != icon_type) {
     return;
   }
 
@@ -725,8 +732,11 @@
     const std::string& app_name,
     const PauseData& pause_data,
     apps::mojom::IconValuePtr icon_value) {
-  if (icon_value->icon_compression !=
-      apps::mojom::IconCompression::kUncompressed) {
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
+  if (icon_value->icon_type != icon_type) {
     OnPauseDialogClosed(app_type, app_id);
     return;
   }
diff --git a/chrome/browser/apps/app_service/app_service_proxy.h b/chrome/browser/apps/app_service/app_service_proxy.h
index 4826194..f7fbf98 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.h
+++ b/chrome/browser/apps/app_service/app_service_proxy.h
@@ -98,7 +98,7 @@
       apps::mojom::AppType app_type,
       const std::string& app_id,
       apps::mojom::IconKeyPtr icon_key,
-      apps::mojom::IconCompression icon_compression,
+      apps::mojom::IconType icon_type,
       int32_t size_hint_in_dip,
       bool allow_placeholder_icon,
       apps::mojom::Publisher::LoadIconCallback callback) override;
@@ -266,7 +266,7 @@
         apps::mojom::AppType app_type,
         const std::string& app_id,
         apps::mojom::IconKeyPtr icon_key,
-        apps::mojom::IconCompression icon_compression,
+        apps::mojom::IconType icon_type,
         int32_t size_hint_in_dip,
         bool allow_placeholder_icon,
         apps::mojom::Publisher::LoadIconCallback callback) override;
diff --git a/chrome/browser/apps/app_service/app_service_proxy_unittest.cc b/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
index 09f986c..22cfb83 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
@@ -20,7 +20,7 @@
     void FlushPendingCallbacks() {
       for (auto& callback : pending_callbacks_) {
         auto iv = apps::mojom::IconValue::New();
-        iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
+        iv->icon_type = apps::mojom::IconType::kUncompressed;
         iv->uncompressed =
             gfx::ImageSkia(gfx::ImageSkiaRep(gfx::Size(1, 1), 1.0f));
         iv->is_placeholder_icon = false;
@@ -43,11 +43,11 @@
         apps::mojom::AppType app_type,
         const std::string& app_id,
         apps::mojom::IconKeyPtr icon_key,
-        apps::mojom::IconCompression icon_compression,
+        apps::mojom::IconType icon_type,
         int32_t size_hint_in_dip,
         bool allow_placeholder_icon,
         apps::mojom::Publisher::LoadIconCallback callback) override {
-      if (icon_compression == apps::mojom::IconCompression::kUncompressed) {
+      if (icon_type == apps::mojom::IconType::kUncompressed) {
         pending_callbacks_.push_back(std::move(callback));
       }
       return nullptr;
@@ -59,13 +59,12 @@
 
   UniqueReleaser LoadIcon(apps::IconLoader* loader, const std::string& app_id) {
     static constexpr auto app_type = apps::mojom::AppType::kWeb;
-    static constexpr auto icon_compression =
-        apps::mojom::IconCompression::kUncompressed;
+    static constexpr auto icon_type = apps::mojom::IconType::kUncompressed;
     static constexpr int32_t size_hint_in_dip = 1;
     static bool allow_placeholder_icon = false;
 
-    return loader->LoadIcon(app_type, app_id, icon_compression,
-                            size_hint_in_dip, allow_placeholder_icon,
+    return loader->LoadIcon(app_type, app_id, icon_type, size_hint_in_dip,
+                            allow_placeholder_icon,
                             base::BindOnce(&AppServiceProxyTest::OnLoadIcon,
                                            base::Unretained(this)));
   }
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index fb4caad5..fd28637 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -49,7 +49,7 @@
 namespace {
 
 void OnArcAppIconCompletelyLoaded(
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int32_t size_hint_in_dip,
     apps::IconEffects icon_effects,
     apps::mojom::Publisher::LoadIconCallback callback,
@@ -60,11 +60,11 @@
   }
 
   apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
-  iv->icon_compression = icon_compression;
+  iv->icon_type = icon_type;
   iv->is_placeholder_icon = false;
 
-  switch (icon_compression) {
-    case apps::mojom::IconCompression::kCompressed: {
+  switch (icon_type) {
+    case apps::mojom::IconType::kCompressed: {
       auto& compressed_images = icon->compressed_images();
       auto iter =
           compressed_images.find(apps_util::GetPrimaryDisplayUIScaleFactor());
@@ -80,8 +80,8 @@
       }
       break;
     }
-    case apps::mojom::IconCompression::kUncompressed:
-    case apps::mojom::IconCompression::kStandard: {
+    case apps::mojom::IconType::kUncompressed:
+    case apps::mojom::IconType::kStandard: {
       if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) {
         iv->uncompressed = gfx::ImageSkiaOperations::CreateSuperimposedImage(
             icon->background_image_skia(), icon->foreground_image_skia());
@@ -94,7 +94,7 @@
       }
       break;
     }
-    case apps::mojom::IconCompression::kUnknown:
+    case apps::mojom::IconType::kUnknown:
       NOTREACHED();
       break;
   }
@@ -171,16 +171,11 @@
 
 arc::mojom::IntentInfoPtr CreateArcIntent(apps::mojom::IntentPtr intent) {
   arc::mojom::IntentInfoPtr arc_intent;
-  if (!intent->scheme.has_value() || !intent->host.has_value() ||
-      !intent->path.has_value()) {
+  if (!intent->url.has_value()) {
     return arc_intent;
   }
 
   arc_intent = arc::mojom::IntentInfo::New();
-  auto uri_components = arc::mojom::UriComponents::New();
-  uri_components->scheme = intent->scheme.value();
-  uri_components->authority = intent->host.value();
-  uri_components->path = intent->path.value();
   if (intent->action.has_value()) {
     if (intent->action.value() == apps_util::kIntentActionView) {
       arc_intent->action = arc::kIntentActionView;
@@ -194,7 +189,7 @@
   } else {
     arc_intent->action = arc::kIntentActionView;
   }
-  arc_intent->uri_components = std::move(uri_components);
+  arc_intent->data = intent->url->spec();
   return arc_intent;
 }
 
@@ -568,11 +563,11 @@
 
 void ArcApps::LoadIcon(const std::string& app_id,
                        apps::mojom::IconKeyPtr icon_key,
-                       apps::mojom::IconCompression icon_compression,
+                       apps::mojom::IconType icon_type,
                        int32_t size_hint_in_dip,
                        bool allow_placeholder_icon,
                        LoadIconCallback callback) {
-  if (!icon_key || icon_compression == apps::mojom::IconCompression::kUnknown) {
+  if (!icon_key || icon_type == apps::mojom::IconType::kUnknown) {
     std::move(callback).Run(apps::mojom::IconValue::New());
     return;
   }
@@ -585,7 +580,7 @@
   // should be showable even before the user has installed their first
   // Android app and before bringing up an Android VM for the first time.
   if (app_id == arc::kPlayStoreAppId) {
-    LoadPlayStoreIcon(icon_compression, size_hint_in_dip, icon_effects,
+    LoadPlayStoreIcon(icon_type, size_hint_in_dip, icon_effects,
                       std::move(callback));
   } else {
     const ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile_);
@@ -601,8 +596,8 @@
     }
 
     arc_icon_once_loader_.LoadIcon(
-        app_id, size_hint_in_dip, icon_compression,
-        base::BindOnce(&OnArcAppIconCompletelyLoaded, icon_compression,
+        app_id, size_hint_in_dip, icon_type,
+        base::BindOnce(&OnArcAppIconCompletelyLoaded, icon_type,
                        size_hint_in_dip, icon_effects, std::move(callback)));
   }
 }
@@ -1129,7 +1124,7 @@
   instance_registry_observer_.Remove(instance_registry);
 }
 
-void ArcApps::LoadPlayStoreIcon(apps::mojom::IconCompression icon_compression,
+void ArcApps::LoadPlayStoreIcon(apps::mojom::IconType icon_type,
                                 int32_t size_hint_in_dip,
                                 IconEffects icon_effects,
                                 LoadIconCallback callback) {
@@ -1140,7 +1135,7 @@
   int resource_id = (size_hint_in_px <= 32) ? IDR_ARC_SUPPORT_ICON_32
                                             : IDR_ARC_SUPPORT_ICON_192;
   constexpr bool is_placeholder_icon = false;
-  LoadIconFromResource(icon_compression, size_hint_in_dip, resource_id,
+  LoadIconFromResource(icon_type, size_hint_in_dip, resource_id,
                        is_placeholder_icon, icon_effects, std::move(callback));
 }
 
diff --git a/chrome/browser/apps/app_service/arc_apps.h b/chrome/browser/apps/app_service/arc_apps.h
index e466035f..4bcbef7 100644
--- a/chrome/browser/apps/app_service/arc_apps.h
+++ b/chrome/browser/apps/app_service/arc_apps.h
@@ -76,7 +76,7 @@
                apps::mojom::ConnectOptionsPtr opts) override;
   void LoadIcon(const std::string& app_id,
                 apps::mojom::IconKeyPtr icon_key,
-                apps::mojom::IconCompression icon_compression,
+                apps::mojom::IconType icon_type,
                 int32_t size_hint_in_dip,
                 bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
@@ -153,7 +153,7 @@
   void OnInstanceRegistryWillBeDestroyed(
       apps::InstanceRegistry* instance_registry) override;
 
-  void LoadPlayStoreIcon(apps::mojom::IconCompression icon_compression,
+  void LoadPlayStoreIcon(apps::mojom::IconType icon_type,
                          int32_t size_hint_in_dip,
                          IconEffects icon_effects,
                          LoadIconCallback callback);
diff --git a/chrome/browser/apps/app_service/arc_icon_once_loader.cc b/chrome/browser/apps/app_service/arc_icon_once_loader.cc
index 451988a..13b91a1 100644
--- a/chrome/browser/apps/app_service/arc_icon_once_loader.cc
+++ b/chrome/browser/apps/app_service/arc_icon_once_loader.cc
@@ -9,14 +9,14 @@
 namespace apps {
 
 // A part of an ArcIconOnceLoader, for a specific size_in_dip and
-// icon_compression. This two-level structure (an ArcIconOnceLoader contains
+// icon_type. This two-level structure (an ArcIconOnceLoader contains
 // multiple SizeSpecificLoader instances) is needed because each ArcAppIcon is
-// for a specific size_in_dip and compressed-ness.
+// for a specific size_in_dip and type.
 class ArcIconOnceLoader::SizeSpecificLoader : public ArcAppIcon::Observer {
  public:
   SizeSpecificLoader(Profile* profile,
                      int32_t size_in_dip,
-                     apps::mojom::IconCompression icon_compression);
+                     apps::mojom::IconType icon_type);
   ~SizeSpecificLoader() override;
 
   void LoadIcon(const std::string& app_id,
@@ -30,7 +30,7 @@
  private:
   Profile* const profile_;
   const int32_t size_in_dip_;
-  const apps::mojom::IconCompression icon_compression_;
+  const apps::mojom::IconType icon_type_;
 
   // Maps App IDs to their icon loaders (for a specific size_in_dip and
   // icon_compression).
@@ -45,10 +45,8 @@
 ArcIconOnceLoader::SizeSpecificLoader::SizeSpecificLoader(
     Profile* profile,
     int32_t size_in_dip,
-    apps::mojom::IconCompression icon_compression)
-    : profile_(profile),
-      size_in_dip_(size_in_dip),
-      icon_compression_(icon_compression) {}
+    apps::mojom::IconType icon_type)
+    : profile_(profile), size_in_dip_(size_in_dip), icon_type_(icon_type) {}
 
 ArcIconOnceLoader::SizeSpecificLoader::~SizeSpecificLoader() {
   for (auto& kv_pair : callbacks_) {
@@ -72,15 +70,15 @@
     return;
   }
   ArcAppIcon::IconType icon_type;
-  switch (icon_compression_) {
-    case apps::mojom::IconCompression::kUnknown:
-    case apps::mojom::IconCompression::kUncompressed:
+  switch (icon_type_) {
+    case apps::mojom::IconType::kUnknown:
+    case apps::mojom::IconType::kUncompressed:
       icon_type = ArcAppIcon::IconType::kUncompressed;
       break;
-    case apps::mojom::IconCompression::kCompressed:
+    case apps::mojom::IconType::kCompressed:
       icon_type = ArcAppIcon::IconType::kCompressed;
       break;
-    case apps::mojom::IconCompression::kStandard:
+    case apps::mojom::IconType::kStandard:
       icon_type = ArcAppIcon::IconType::kAdaptive;
       break;
   }
@@ -169,16 +167,16 @@
 void ArcIconOnceLoader::LoadIcon(
     const std::string& app_id,
     int32_t size_in_dip,
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     base::OnceCallback<void(ArcAppIcon*)> callback) {
-  auto key = std::make_pair(size_in_dip, icon_compression);
+  auto key = std::make_pair(size_in_dip, icon_type);
   auto iter = size_specific_loaders_.find(key);
   if (iter == size_specific_loaders_.end()) {
-    iter = size_specific_loaders_
-               .insert(std::make_pair(
-                   key, std::make_unique<SizeSpecificLoader>(
-                            profile_, size_in_dip, icon_compression)))
-               .first;
+    iter =
+        size_specific_loaders_
+            .insert(std::make_pair(key, std::make_unique<SizeSpecificLoader>(
+                                            profile_, size_in_dip, icon_type)))
+            .first;
   }
   iter->second->LoadIcon(app_id, std::move(callback));
 }
@@ -192,10 +190,10 @@
 void ArcIconOnceLoader::OnAppIconUpdated(
     const std::string& app_id,
     const ArcAppIconDescriptor& descriptor) {
-  for (int i = static_cast<int>(apps::mojom::IconCompression::kUncompressed);
-       i <= static_cast<int>(apps::mojom::IconCompression::kStandard); ++i) {
+  for (int i = static_cast<int>(apps::mojom::IconType::kUncompressed);
+       i <= static_cast<int>(apps::mojom::IconType::kStandard); ++i) {
     auto iter = size_specific_loaders_.find(std::make_pair(
-        descriptor.dip_size, static_cast<apps::mojom::IconCompression>(i)));
+        descriptor.dip_size, static_cast<apps::mojom::IconType>(i)));
     if (iter != size_specific_loaders_.end()) {
       iter->second->Reload(app_id, descriptor.scale_factor);
     }
diff --git a/chrome/browser/apps/app_service/arc_icon_once_loader.h b/chrome/browser/apps/app_service/arc_icon_once_loader.h
index da2a0d1..3cd83b4 100644
--- a/chrome/browser/apps/app_service/arc_icon_once_loader.h
+++ b/chrome/browser/apps/app_service/arc_icon_once_loader.h
@@ -45,7 +45,7 @@
   // loaded.
   void LoadIcon(const std::string& app_id,
                 int32_t size_in_dip,
-                apps::mojom::IconCompression icon_compression,
+                apps::mojom::IconType icon_type,
                 base::OnceCallback<void(ArcAppIcon*)> callback);
 
   // ArcAppListPrefs::Observer overrides.
@@ -56,11 +56,11 @@
  private:
   class SizeSpecificLoader;
 
-  using SizeAndCompression = std::pair<int32_t, apps::mojom::IconCompression>;
+  using SizeAndType = std::pair<int32_t, apps::mojom::IconType>;
 
   Profile* const profile_;
   bool stop_observing_called_;
-  std::map<SizeAndCompression, std::unique_ptr<SizeSpecificLoader>>
+  std::map<SizeAndType, std::unique_ptr<SizeSpecificLoader>>
       size_specific_loaders_;
 
   DISALLOW_COPY_AND_ASSIGN(ArcIconOnceLoader);
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
index 6add7ed4..84da674a 100644
--- a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
+++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
@@ -117,20 +117,18 @@
   // lifetime of the Chrome OS session. There won't be any further updates.
 }
 
-void BuiltInChromeOsApps::LoadIcon(
-    const std::string& app_id,
-    apps::mojom::IconKeyPtr icon_key,
-    apps::mojom::IconCompression icon_compression,
-    int32_t size_hint_in_dip,
-    bool allow_placeholder_icon,
-    LoadIconCallback callback) {
+void BuiltInChromeOsApps::LoadIcon(const std::string& app_id,
+                                   apps::mojom::IconKeyPtr icon_key,
+                                   apps::mojom::IconType icon_type,
+                                   int32_t size_hint_in_dip,
+                                   bool allow_placeholder_icon,
+                                   LoadIconCallback callback) {
   constexpr bool is_placeholder_icon = false;
   if (icon_key &&
       (icon_key->resource_id != apps::mojom::IconKey::kInvalidResourceId)) {
-    LoadIconFromResource(icon_compression, size_hint_in_dip,
-                         icon_key->resource_id, is_placeholder_icon,
-                         static_cast<IconEffects>(icon_key->icon_effects),
-                         std::move(callback));
+    LoadIconFromResource(
+        icon_type, size_hint_in_dip, icon_key->resource_id, is_placeholder_icon,
+        static_cast<IconEffects>(icon_key->icon_effects), std::move(callback));
     return;
   }
   // On failure, we still run the callback, with the zero IconValue.
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.h b/chrome/browser/apps/app_service/built_in_chromeos_apps.h
index fd8a0ac4d..04db60d 100644
--- a/chrome/browser/apps/app_service/built_in_chromeos_apps.h
+++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.h
@@ -35,7 +35,7 @@
                apps::mojom::ConnectOptionsPtr opts) override;
   void LoadIcon(const std::string& app_id,
                 apps::mojom::IconKeyPtr icon_key,
-                apps::mojom::IconCompression icon_compression,
+                apps::mojom::IconType icon_type,
                 int32_t size_hint_in_dip,
                 bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc
index 4036781..3056d4ac 100644
--- a/chrome/browser/apps/app_service/crostini_apps.cc
+++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -127,7 +127,7 @@
 
 void CrostiniApps::LoadIcon(const std::string& app_id,
                             apps::mojom::IconKeyPtr icon_key,
-                            apps::mojom::IconCompression icon_compression,
+                            apps::mojom::IconType icon_type,
                             int32_t size_hint_in_dip,
                             bool allow_placeholder_icon,
                             LoadIconCallback callback) {
@@ -135,8 +135,8 @@
     if (icon_key->resource_id != apps::mojom::IconKey::kInvalidResourceId) {
       // The icon is a resource built into the Chrome OS binary.
       constexpr bool is_placeholder_icon = false;
-      LoadIconFromResource(icon_compression, size_hint_in_dip,
-                           icon_key->resource_id, is_placeholder_icon,
+      LoadIconFromResource(icon_type, size_hint_in_dip, icon_key->resource_id,
+                           is_placeholder_icon,
                            static_cast<IconEffects>(icon_key->icon_effects),
                            std::move(callback));
       return;
@@ -146,12 +146,12 @@
       // Try loading the icon from an on-disk cache. If that fails, fall back
       // to LoadIconFromVM.
       LoadIconFromFileWithFallback(
-          icon_compression, size_hint_in_dip,
+          icon_type, size_hint_in_dip,
           registry_->GetIconPath(app_id, scale_factor),
           static_cast<IconEffects>(icon_key->icon_effects), std::move(callback),
           base::BindOnce(&CrostiniApps::LoadIconFromVM,
-                         weak_ptr_factory_.GetWeakPtr(), app_id,
-                         icon_compression, size_hint_in_dip, scale_factor,
+                         weak_ptr_factory_.GetWeakPtr(), app_id, icon_type,
+                         size_hint_in_dip, scale_factor,
                          static_cast<IconEffects>(icon_key->icon_effects)));
       return;
     }
@@ -263,7 +263,7 @@
 }
 
 void CrostiniApps::LoadIconFromVM(const std::string app_id,
-                                  apps::mojom::IconCompression icon_compression,
+                                  apps::mojom::IconType icon_type,
                                   int32_t size_hint_in_dip,
                                   ui::ScaleFactor scale_factor,
                                   IconEffects icon_effects,
@@ -271,17 +271,16 @@
   registry_->RequestIcon(
       app_id, scale_factor,
       base::BindOnce(&CrostiniApps::OnLoadIconFromVM,
-                     weak_ptr_factory_.GetWeakPtr(), app_id, icon_compression,
+                     weak_ptr_factory_.GetWeakPtr(), app_id, icon_type,
                      size_hint_in_dip, icon_effects, std::move(callback)));
 }
 
-void CrostiniApps::OnLoadIconFromVM(
-    const std::string app_id,
-    apps::mojom::IconCompression icon_compression,
-    int32_t size_hint_in_dip,
-    IconEffects icon_effects,
-    LoadIconCallback callback,
-    std::string compressed_icon_data) {
+void CrostiniApps::OnLoadIconFromVM(const std::string app_id,
+                                    apps::mojom::IconType icon_type,
+                                    int32_t size_hint_in_dip,
+                                    IconEffects icon_effects,
+                                    LoadIconCallback callback,
+                                    std::string compressed_icon_data) {
   if (compressed_icon_data.empty()) {
     auto registration = registry_->GetRegistration(app_id);
     if (registration && registration->VmType() ==
@@ -290,14 +289,14 @@
       // Load default penguin for crostini. We must set is_placeholder_icon to
       // false to stop endless recursive calls.
       LoadIconFromResource(
-          icon_compression, size_hint_in_dip, IDR_LOGO_CROSTINI_DEFAULT_192,
+          icon_type, size_hint_in_dip, IDR_LOGO_CROSTINI_DEFAULT_192,
           /*is_placeholder_icon=*/false, icon_effects, std::move(callback));
     } else {
       // Leave it for app service to get a default for Plugin VM.
       std::move(callback).Run(apps::mojom::IconValue::New());
     }
   } else {
-    LoadIconFromCompressedData(icon_compression, size_hint_in_dip, icon_effects,
+    LoadIconFromCompressedData(icon_type, size_hint_in_dip, icon_effects,
                                compressed_icon_data, std::move(callback));
   }
 }
diff --git a/chrome/browser/apps/app_service/crostini_apps.h b/chrome/browser/apps/app_service/crostini_apps.h
index d7e7654..ebaa567 100644
--- a/chrome/browser/apps/app_service/crostini_apps.h
+++ b/chrome/browser/apps/app_service/crostini_apps.h
@@ -55,7 +55,7 @@
                apps::mojom::ConnectOptionsPtr opts) override;
   void LoadIcon(const std::string& app_id,
                 apps::mojom::IconKeyPtr icon_key,
-                apps::mojom::IconCompression icon_compression,
+                apps::mojom::IconType icon_type,
                 int32_t size_hint_in_dip,
                 bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
@@ -84,14 +84,14 @@
   void OnCrostiniEnabledChanged();
 
   void LoadIconFromVM(const std::string app_id,
-                      apps::mojom::IconCompression icon_compression,
+                      apps::mojom::IconType icon_type,
                       int32_t size_hint_in_dip,
                       ui::ScaleFactor scale_factor,
                       IconEffects icon_effects,
                       LoadIconCallback callback);
 
   void OnLoadIconFromVM(const std::string app_id,
-                        apps::mojom::IconCompression icon_compression,
+                        apps::mojom::IconType icon_type,
                         int32_t size_hint_in_dip,
                         IconEffects icon_effects,
                         LoadIconCallback callback,
diff --git a/chrome/browser/apps/app_service/extension_apps_base.cc b/chrome/browser/apps/app_service/extension_apps_base.cc
index c1b179226..8939bd6 100644
--- a/chrome/browser/apps/app_service/extension_apps_base.cc
+++ b/chrome/browser/apps/app_service/extension_apps_base.cc
@@ -463,12 +463,12 @@
 
 void ExtensionAppsBase::LoadIcon(const std::string& app_id,
                                  apps::mojom::IconKeyPtr icon_key,
-                                 apps::mojom::IconCompression icon_compression,
+                                 apps::mojom::IconType icon_type,
                                  int32_t size_hint_in_dip,
                                  bool allow_placeholder_icon,
                                  LoadIconCallback callback) {
   if (icon_key) {
-    LoadIconFromExtension(icon_compression, size_hint_in_dip, profile_, app_id,
+    LoadIconFromExtension(icon_type, size_hint_in_dip, profile_, app_id,
                           static_cast<IconEffects>(icon_key->icon_effects),
                           std::move(callback));
     return;
diff --git a/chrome/browser/apps/app_service/extension_apps_base.h b/chrome/browser/apps/app_service/extension_apps_base.h
index df6e7c2..d8a799a6 100644
--- a/chrome/browser/apps/app_service/extension_apps_base.h
+++ b/chrome/browser/apps/app_service/extension_apps_base.h
@@ -125,7 +125,7 @@
                apps::mojom::ConnectOptionsPtr opts) override;
   void LoadIcon(const std::string& app_id,
                 apps::mojom::IconKeyPtr icon_key,
-                apps::mojom::IconCompression icon_compression,
+                apps::mojom::IconType icon_type,
                 int32_t size_hint_in_dip,
                 bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
diff --git a/chrome/browser/apps/app_service/lacros_apps.cc b/chrome/browser/apps/app_service/lacros_apps.cc
index 0f81d50..c02aa69 100644
--- a/chrome/browser/apps/app_service/lacros_apps.cc
+++ b/chrome/browser/apps/app_service/lacros_apps.cc
@@ -84,16 +84,16 @@
 
 void LacrosApps::LoadIcon(const std::string& app_id,
                           apps::mojom::IconKeyPtr icon_key,
-                          apps::mojom::IconCompression icon_compression,
+                          apps::mojom::IconType icon_type,
                           int32_t size_hint_in_dip,
                           bool allow_placeholder_icon,
                           LoadIconCallback callback) {
   if (icon_key &&
       icon_key->resource_id != apps::mojom::IconKey::kInvalidResourceId) {
-    LoadIconFromResource(
-        icon_compression, size_hint_in_dip, icon_key->resource_id,
-        /*is_placeholder_icon=*/false,
-        static_cast<IconEffects>(icon_key->icon_effects), std::move(callback));
+    LoadIconFromResource(icon_type, size_hint_in_dip, icon_key->resource_id,
+                         /*is_placeholder_icon=*/false,
+                         static_cast<IconEffects>(icon_key->icon_effects),
+                         std::move(callback));
     return;
   }
   // On failure, we still run the callback, with the zero IconValue.
diff --git a/chrome/browser/apps/app_service/lacros_apps.h b/chrome/browser/apps/app_service/lacros_apps.h
index 9037e4f..7cd4e96d 100644
--- a/chrome/browser/apps/app_service/lacros_apps.h
+++ b/chrome/browser/apps/app_service/lacros_apps.h
@@ -43,7 +43,7 @@
                apps::mojom::ConnectOptionsPtr opts) override;
   void LoadIcon(const std::string& app_id,
                 apps::mojom::IconKeyPtr icon_key,
-                apps::mojom::IconCompression icon_compression,
+                apps::mojom::IconType icon_type,
                 int32_t size_hint_in_dip,
                 bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
diff --git a/chrome/browser/apps/app_service/launch_utils.cc b/chrome/browser/apps/app_service/launch_utils.cc
index 73c45b9a..fd302a0 100644
--- a/chrome/browser/apps/app_service/launch_utils.cc
+++ b/chrome/browser/apps/app_service/launch_utils.cc
@@ -161,16 +161,18 @@
   auto params = CreateAppIdLaunchParamsWithEventFlags(
       app_id, event_flags, source, display_id, fallback_container);
 
-  if (intent->scheme.has_value() && intent->host.has_value() &&
-      intent->path.has_value()) {
+  if (intent->url.has_value()) {
     params.source = apps::mojom::AppLaunchSource::kSourceIntentUrl;
+    params.override_url = intent->url.value();
+    LOG(ERROR) << "url is:" << params.override_url.spec();
     std::string port;
-    if (intent->port.has_value()) {
-      port = ":" + intent->port.value();
+    if (intent->url->has_port()) {
+      port = ":" + intent->url->port();
     }
     params.override_url =
-        GURL(intent->scheme.value() + url::kStandardSchemeSeparator +
-             intent->host.value() + port + intent->path.value());
+        GURL(intent->url->scheme() + url::kStandardSchemeSeparator +
+             intent->url->host() + port + intent->url->path());
+    LOG(ERROR) << "url is:" << params.override_url.spec();
     DCHECK(params.override_url.is_valid());
   }
 
diff --git a/chrome/browser/apps/app_service/plugin_vm_apps.cc b/chrome/browser/apps/app_service/plugin_vm_apps.cc
index 40813b9..b4b5bf1d 100644
--- a/chrome/browser/apps/app_service/plugin_vm_apps.cc
+++ b/chrome/browser/apps/app_service/plugin_vm_apps.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/apps/app_service/menu_util.h"
 #include "chrome/browser/chromeos/guest_os/guest_os_registry_service.h"
 #include "chrome/browser/chromeos/guest_os/guest_os_registry_service_factory.h"
-#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager_factory.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
@@ -34,13 +33,21 @@
   const char* pref_name;
 };
 
+struct PluginVmHandledPermissionInfo {
+  app_management::mojom::PluginVmPermissionType permission;
+  plugin_vm::PermissionType permission_type;
+};
+
 constexpr PermissionInfo permission_infos[] = {
     {app_management::mojom::PluginVmPermissionType::PRINTING,
      plugin_vm::prefs::kPluginVmPrintersAllowed},
+};
+
+constexpr PluginVmHandledPermissionInfo plugin_vm_handled_permission_infos[] = {
     {app_management::mojom::PluginVmPermissionType::CAMERA,
-     plugin_vm::prefs::kPluginVmCameraAllowed},
+     plugin_vm::PermissionType::kCamera},
     {app_management::mojom::PluginVmPermissionType::MICROPHONE,
-     plugin_vm::prefs::kPluginVmMicrophoneAllowed},
+     plugin_vm::PermissionType::kMicrophone},
 };
 
 const char* PermissionToPrefName(
@@ -75,7 +82,9 @@
                                       : apps::mojom::OptionalBool::kFalse;
 }
 
-void PopulatePermissions(apps::mojom::App* app, Profile* profile) {
+void PopulatePermissions(apps::mojom::App* app,
+                         Profile* profile,
+                         bool allowed) {
   for (const PermissionInfo& info : permission_infos) {
     auto permission = apps::mojom::Permission::New();
     permission->permission_id = static_cast<uint32_t>(info.permission);
@@ -85,6 +94,21 @@
     permission->is_managed = false;
     app->permissions.push_back(std::move(permission));
   }
+  for (const PluginVmHandledPermissionInfo& info :
+       plugin_vm_handled_permission_infos) {
+    auto permission = apps::mojom::Permission::New();
+    permission->permission_id = static_cast<uint32_t>(info.permission);
+    permission->value_type = apps::mojom::PermissionValueType::kBool;
+    if (allowed) {
+      permission->value = static_cast<uint32_t>(
+          plugin_vm::PluginVmManagerFactory::GetForProfile(profile)
+              ->GetPermission(info.permission_type));
+    } else {
+      permission->value = static_cast<uint32_t>(false);
+    }
+    permission->is_managed = false;
+    app->permissions.push_back(std::move(permission));
+  }
 }
 
 apps::mojom::AppPtr GetPluginVmApp(Profile* profile, bool allowed) {
@@ -100,7 +124,7 @@
       IDR_LOGO_PLUGIN_VM_DEFAULT_192, apps::IconEffects::kNone);
 
   SetShowInAppManagement(app.get(), plugin_vm::IsPluginVmConfigured(profile));
-  PopulatePermissions(app.get(), profile);
+  PopulatePermissions(app.get(), profile, allowed);
   SetAppAllowed(app.get(), allowed);
 
   return app;
@@ -113,7 +137,7 @@
 PluginVmApps::PluginVmApps(
     const mojo::Remote<apps::mojom::AppService>& app_service,
     Profile* profile)
-    : profile_(profile) {
+    : profile_(profile), permissions_observer_(this) {
   PublisherBase::Initialize(app_service, apps::mojom::AppType::kPluginVm);
 
   // Register for Plugin VM changes to policy and installed state, so that we
@@ -130,6 +154,10 @@
                           base::Unretained(this)));
 
   is_allowed_ = plugin_vm::IsPluginVmAllowedForProfile(profile_);
+  if (is_allowed_) {
+    permissions_observer_.Add(
+        plugin_vm::PluginVmManagerFactory::GetForProfile(profile_));
+  }
 }
 
 PluginVmApps::~PluginVmApps() = default;
@@ -148,17 +176,16 @@
 
 void PluginVmApps::LoadIcon(const std::string& app_id,
                             apps::mojom::IconKeyPtr icon_key,
-                            apps::mojom::IconCompression icon_compression,
+                            apps::mojom::IconType icon_type,
                             int32_t size_hint_in_dip,
                             bool allow_placeholder_icon,
                             LoadIconCallback callback) {
   constexpr bool is_placeholder_icon = false;
   if (icon_key &&
       (icon_key->resource_id != apps::mojom::IconKey::kInvalidResourceId)) {
-    LoadIconFromResource(icon_compression, size_hint_in_dip,
-                         icon_key->resource_id, is_placeholder_icon,
-                         static_cast<IconEffects>(icon_key->icon_effects),
-                         std::move(callback));
+    LoadIconFromResource(
+        icon_type, size_hint_in_dip, icon_key->resource_id, is_placeholder_icon,
+        static_cast<IconEffects>(icon_key->icon_effects), std::move(callback));
     return;
   }
   // On failure, we still run the callback, with the zero IconValue.
@@ -192,7 +219,7 @@
   apps::mojom::AppPtr app = apps::mojom::App::New();
   app->app_type = apps::mojom::AppType::kPluginVm;
   app->app_id = plugin_vm::kPluginVmShelfAppId;
-  PopulatePermissions(app.get(), profile_);
+  PopulatePermissions(app.get(), profile_, is_allowed_);
   Publish(std::move(app), subscribers_);
 }
 
@@ -252,4 +279,14 @@
   Publish(std::move(app), subscribers_);
 }
 
+void PluginVmApps::OnPluginVmPermissionsChanged(
+    plugin_vm::PermissionType permission_type,
+    bool allowed) {
+  apps::mojom::AppPtr app = apps::mojom::App::New();
+  app->app_type = apps::mojom::AppType::kPluginVm;
+  app->app_id = plugin_vm::kPluginVmShelfAppId;
+  PopulatePermissions(app.get(), profile_, is_allowed_);
+  Publish(std::move(app), subscribers_);
+}
+
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/plugin_vm_apps.h b/chrome/browser/apps/app_service/plugin_vm_apps.h
index 793c9bd4..d0ab6108 100644
--- a/chrome/browser/apps/app_service/plugin_vm_apps.h
+++ b/chrome/browser/apps/app_service/plugin_vm_apps.h
@@ -8,6 +8,8 @@
 #include <memory>
 #include <string>
 
+#include "base/scoped_observer.h"
+#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/services/app_service/public/cpp/publisher_base.h"
@@ -24,7 +26,8 @@
 // An app publisher (in the App Service sense) of Plugin VM apps.
 //
 // See components/services/app_service/README.md.
-class PluginVmApps : public apps::PublisherBase {
+class PluginVmApps : public apps::PublisherBase,
+                     public plugin_vm::PluginVmPermissionsObserver {
  public:
   PluginVmApps(const mojo::Remote<apps::mojom::AppService>& app_service,
                Profile* profile);
@@ -39,7 +42,7 @@
                apps::mojom::ConnectOptionsPtr opts) override;
   void LoadIcon(const std::string& app_id,
                 apps::mojom::IconKeyPtr icon_key,
-                apps::mojom::IconCompression icon_compression,
+                apps::mojom::IconType icon_type,
                 int32_t size_hint_in_dip,
                 bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
@@ -59,6 +62,9 @@
 
   void OnPluginVmAllowedChanged(bool is_allowed);
   void OnPluginVmConfiguredChanged();
+  // plugin_vm::PluginVmPermissionsObserver
+  void OnPluginVmPermissionsChanged(plugin_vm::PermissionType permission_type,
+                                    bool allowed) override;
 
   mojo::RemoteSet<apps::mojom::Subscriber> subscribers_;
 
@@ -67,6 +73,12 @@
   // Whether the Plugin VM app is allowed by policy.
   bool is_allowed_;
 
+  ScopedObserver<plugin_vm::PluginVmManager,
+                 plugin_vm::PluginVmPermissionsObserver,
+                 &plugin_vm::PluginVmManager::AddPluginVmPermissionsObserver,
+                 &plugin_vm::PluginVmManager::RemovePluginVmPermissionsObserver>
+      permissions_observer_;
+
   std::unique_ptr<plugin_vm::PluginVmPolicySubscription> policy_subscription_;
   PrefChangeRegistrar pref_registrar_;
 };
diff --git a/chrome/browser/apps/app_service/uninstall_dialog.cc b/chrome/browser/apps/app_service/uninstall_dialog.cc
index a7a5318..1c0e33b7 100644
--- a/chrome/browser/apps/app_service/uninstall_dialog.cc
+++ b/chrome/browser/apps/app_service/uninstall_dialog.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/apps/app_service/extension_apps_chromeos.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/native_window_tracker.h"
+#include "chrome/common/chrome_features.h"
 #include "components/services/app_service/public/cpp/icon_loader.h"
 #include "extensions/browser/uninstall_reason.h"
 
@@ -60,9 +61,12 @@
   constexpr bool kAllowPlaceholderIcon = false;
   // Currently ARC apps only support 48*48 native icon.
   int32_t size_hint_in_dip = kUninstallIconSize;
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
   icon_loader->LoadIconFromIconKey(
-      app_type, app_id, std::move(icon_key),
-      apps::mojom::IconCompression::kUncompressed, size_hint_in_dip,
+      app_type, app_id, std::move(icon_key), icon_type, size_hint_in_dip,
       kAllowPlaceholderIcon,
       base::BindOnce(&UninstallDialog::OnLoadIcon,
                      weak_ptr_factory_.GetWeakPtr()));
@@ -88,8 +92,11 @@
 }
 
 void UninstallDialog::OnLoadIcon(apps::mojom::IconValuePtr icon_value) {
-  if (icon_value->icon_compression !=
-      apps::mojom::IconCompression::kUncompressed) {
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
+  if (icon_value->icon_type != icon_type) {
     OnDialogClosed(false, false, false);
     return;
   }
diff --git a/chrome/browser/apps/app_service/web_apps_base.cc b/chrome/browser/apps/app_service/web_apps_base.cc
index d5707c8..64c4f10 100644
--- a/chrome/browser/apps/app_service/web_apps_base.cc
+++ b/chrome/browser/apps/app_service/web_apps_base.cc
@@ -196,14 +196,14 @@
 
 void WebAppsBase::LoadIcon(const std::string& app_id,
                            apps::mojom::IconKeyPtr icon_key,
-                           apps::mojom::IconCompression icon_compression,
+                           apps::mojom::IconType icon_type,
                            int32_t size_hint_in_dip,
                            bool allow_placeholder_icon,
                            LoadIconCallback callback) {
   DCHECK(provider_);
 
   if (icon_key) {
-    LoadIconFromWebApp(profile_, icon_compression, size_hint_in_dip, app_id,
+    LoadIconFromWebApp(profile_, icon_type, size_hint_in_dip, app_id,
                        static_cast<IconEffects>(icon_key->icon_effects),
                        std::move(callback));
     return;
diff --git a/chrome/browser/apps/app_service/web_apps_base.h b/chrome/browser/apps/app_service/web_apps_base.h
index 026f29f..dde24618 100644
--- a/chrome/browser/apps/app_service/web_apps_base.h
+++ b/chrome/browser/apps/app_service/web_apps_base.h
@@ -99,7 +99,7 @@
                apps::mojom::ConnectOptionsPtr opts) override;
   void LoadIcon(const std::string& app_id,
                 apps::mojom::IconKeyPtr icon_key,
-                apps::mojom::IconCompression icon_compression,
+                apps::mojom::IconType icon_type,
                 int32_t size_hint_in_dip,
                 bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
diff --git a/chrome/browser/banners/app_banner_manager_android.cc b/chrome/browser/banners/app_banner_manager_android.cc
index 99ba201..e166ff3e 100644
--- a/chrome/browser/banners/app_banner_manager_android.cc
+++ b/chrome/browser/banners/app_banner_manager_android.cc
@@ -222,8 +222,8 @@
 
   bool was_shown = AddToHomescreenCoordinator::ShowForAppBanner(
       weak_factory_.GetWeakPtr(), std::move(a2hs_params),
-      base::Bind(&AppBannerManagerAndroid::RecordEventForAppBanner,
-                 weak_factory_.GetWeakPtr()));
+      base::BindRepeating(&AppBannerManagerAndroid::RecordEventForAppBanner,
+                          weak_factory_.GetWeakPtr()));
 
   // If we are installing from the ambient badge, it will remove itself.
   if (install_source != WebappInstallSource::AMBIENT_BADGE_CUSTOM_TAB &&
diff --git a/chrome/browser/captive_portal/captive_portal_browsertest.cc b/chrome/browser/captive_portal/captive_portal_browsertest.cc
index fde353e..e2f19734 100644
--- a/chrome/browser/captive_portal/captive_portal_browsertest.cc
+++ b/chrome/browser/captive_portal/captive_portal_browsertest.cc
@@ -439,8 +439,8 @@
           CaptivePortalServiceFactory::GetForProfile(profile)),
       captive_portal_result_(
           captive_portal_service_->last_detection_result()) {
-  subscription_ = captive_portal_service_->RegisterCallback(
-      base::Bind(&CaptivePortalObserver::Observe, base::Unretained(this)));
+  subscription_ = captive_portal_service_->RegisterCallback(base::BindRepeating(
+      &CaptivePortalObserver::Observe, base::Unretained(this)));
 }
 
 void CaptivePortalObserver::WaitForResults(int num_results_to_wait_for) {
diff --git a/chrome/browser/chrome_browser_main_extra_parts_lacros.cc b/chrome/browser/chrome_browser_main_extra_parts_lacros.cc
new file mode 100644
index 0000000..bba2605
--- /dev/null
+++ b/chrome/browser/chrome_browser_main_extra_parts_lacros.cc
@@ -0,0 +1,18 @@
+// 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 "chrome/browser/chrome_browser_main_extra_parts_lacros.h"
+
+#include "chromeos/lacros/browser/lacros_chrome_service_impl.h"
+
+ChromeBrowserMainExtraPartsLacros::ChromeBrowserMainExtraPartsLacros() =
+    default;
+
+ChromeBrowserMainExtraPartsLacros::~ChromeBrowserMainExtraPartsLacros() =
+    default;
+
+void ChromeBrowserMainExtraPartsLacros::PostCreateThreads() {
+  lacros_chrome_service_ =
+      std::make_unique<chromeos::LacrosChromeServiceImpl>();
+}
diff --git a/chrome/browser/chrome_browser_main_extra_parts_lacros.h b/chrome/browser/chrome_browser_main_extra_parts_lacros.h
new file mode 100644
index 0000000..e8217fd2
--- /dev/null
+++ b/chrome/browser/chrome_browser_main_extra_parts_lacros.h
@@ -0,0 +1,33 @@
+// 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 CHROME_BROWSER_CHROME_BROWSER_MAIN_EXTRA_PARTS_LACROS_H_
+#define CHROME_BROWSER_CHROME_BROWSER_MAIN_EXTRA_PARTS_LACROS_H_
+
+#include <memory>
+
+#include "chrome/browser/chrome_browser_main_extra_parts.h"
+
+namespace chromeos {
+class LacrosChromeServiceImpl;
+}
+
+// Startup and shutdown code for lacros-chrome.
+class ChromeBrowserMainExtraPartsLacros : public ChromeBrowserMainExtraParts {
+ public:
+  ChromeBrowserMainExtraPartsLacros();
+  ChromeBrowserMainExtraPartsLacros(const ChromeBrowserMainExtraPartsLacros&) =
+      delete;
+  ChromeBrowserMainExtraPartsLacros& operator=(
+      const ChromeBrowserMainExtraPartsLacros&) = delete;
+  ~ChromeBrowserMainExtraPartsLacros() override;
+
+ private:
+  // ChromeBrowserMainExtraParts:
+  void PostCreateThreads() override;
+
+  std::unique_ptr<chromeos::LacrosChromeServiceImpl> lacros_chrome_service_;
+};
+
+#endif  // CHROME_BROWSER_CHROME_BROWSER_MAIN_EXTRA_PARTS_LACROS_H_
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 6b0d8f05..7b22c977 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -626,9 +626,8 @@
 #endif
 
 #if BUILDFLAG(IS_LACROS)
+#include "chrome/browser/chrome_browser_main_extra_parts_lacros.h"
 #include "chromeos/lacros/browser/lacros_chrome_service_impl.h"
-#include "chromeos/lacros/mojom/lacros.mojom.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #endif
 
 using base::FileDescriptor;
@@ -1368,6 +1367,10 @@
   main_parts->AddParts(new ChromeBrowserMainExtraPartsAsh());
 #endif
 
+#if BUILDFLAG(IS_LACROS)
+  main_parts->AddParts(new ChromeBrowserMainExtraPartsLacros());
+#endif
+
 #if defined(USE_X11) || defined(USE_OZONE)
   main_parts->AddParts(new ChromeBrowserMainExtraPartsOzone());
 #endif
@@ -5793,8 +5796,7 @@
     mojo::GenericPendingReceiver receiver) {
 #if BUILDFLAG(IS_LACROS)
   if (auto r = receiver.As<lacros::mojom::LacrosChromeService>()) {
-    mojo::MakeSelfOwnedReceiver(
-        std::make_unique<chromeos::LacrosChromeServiceImpl>(), std::move(r));
+    chromeos::LacrosChromeServiceImpl::Get()->BindReceiver(std::move(r));
   }
 #endif
 }
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
index 8b633db..3b99790 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/singleton.h"
+#include "base/metrics/histogram_functions.h"
 #include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h"
 #include "chrome/browser/chromeos/arc/accessibility/geometry_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -318,6 +319,7 @@
   }
 
   UpdateWindowProperties(window);
+  base::UmaHistogramBoolean("Arc.AccessibilityWithTalkBack", !enabled);
 }
 
 bool ArcAccessibilityHelperBridge::RefreshTreeIfInActiveWindow(
@@ -860,6 +862,12 @@
     if (!tree_source) {
       tree_source = CreateFromKey(key, active_window);
       SetChildAxTreeIDForWindow(active_window, tree_source->ax_tree_id());
+      if (chromeos::AccessibilityManager::Get() &&
+          chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled()) {
+        // Record metrics only when SpokenFeedback is enabled in order to
+        // compare this with TalkBack usage.
+        base::UmaHistogramBoolean("Arc.AccessibilityWithTalkBack", false);
+      }
     } else {
       tree_source->set_device_scale_factor(
           DeviceScaleFactorFromWindow(active_window));
diff --git a/chrome/browser/chromeos/child_accounts/time_limits/app_service_wrapper.cc b/chrome/browser/chromeos/child_accounts/time_limits/app_service_wrapper.cc
index 3c02f7f..35f6958 100644
--- a/chrome/browser/chromeos/child_accounts/time_limits/app_service_wrapper.cc
+++ b/chrome/browser/chromeos/child_accounts/time_limits/app_service_wrapper.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/chromeos/child_accounts/time_limits/app_types.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "chrome/common/chrome_features.h"
 #include "components/services/app_service/public/cpp/app_update.h"
 #include "components/services/app_service/public/cpp/instance_update.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
@@ -171,16 +172,22 @@
   const std::string app_service_id = AppServiceIdFromAppId(app_id, profile_);
   DCHECK(!app_service_id.empty());
 
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
   proxy->LoadIconFromIconKey(
-      app_id.app_type(), app_service_id, apps::mojom::IconKey::New(),
-      apps::mojom::IconCompression::kUncompressed, size_hint_in_dp,
+      app_id.app_type(), app_service_id, apps::mojom::IconKey::New(), icon_type,
+      size_hint_in_dp,
       /* allow_placeholder_icon */ false,
       base::BindOnce(
           [](base::OnceCallback<void(base::Optional<gfx::ImageSkia>)> callback,
              apps::mojom::IconValuePtr icon_value) {
-            if (!icon_value ||
-                icon_value->icon_compression !=
-                    apps::mojom::IconCompression::kUncompressed) {
+            auto icon_type = (base::FeatureList::IsEnabled(
+                                 features::kAppServiceAdaptiveIcon))
+                                 ? apps::mojom::IconType::kStandard
+                                 : apps::mojom::IconType::kUncompressed;
+            if (!icon_value || icon_value->icon_type != icon_type) {
               std::move(callback).Run(base::nullopt);
             } else {
               std::move(callback).Run(icon_value->uncompressed);
diff --git a/chrome/browser/chromeos/child_accounts/time_limits/app_time_controller_unittest.cc b/chrome/browser/chromeos/child_accounts/time_limits/app_time_controller_unittest.cc
index 2b78ac8..3cd8fb6b 100644
--- a/chrome/browser/chromeos/child_accounts/time_limits/app_time_controller_unittest.cc
+++ b/chrome/browser/chromeos/child_accounts/time_limits/app_time_controller_unittest.cc
@@ -89,13 +89,17 @@
         apps::mojom::AppType app_type,
         const std::string& app_id,
         apps::mojom::IconKeyPtr icon_key,
-        apps::mojom::IconCompression icon_compression,
+        apps::mojom::IconType icon_type,
         int32_t size_hint_in_dip,
         bool allow_placeholder_icon,
         apps::mojom::Publisher::LoadIconCallback callback) override {
-      EXPECT_EQ(icon_compression, apps::mojom::IconCompression::kUncompressed);
+      auto expected_icon_type =
+          (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+              ? apps::mojom::IconType::kStandard
+              : apps::mojom::IconType::kUncompressed;
+      EXPECT_EQ(icon_type, expected_icon_type);
       auto iv = apps::mojom::IconValue::New();
-      iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
+      iv->icon_type = icon_type;
       iv->uncompressed =
           gfx::ImageSkia(gfx::ImageSkiaRep(gfx::Size(1, 1), 1.0f));
       iv->is_placeholder_icon = false;
diff --git a/chrome/browser/chromeos/local_search_service/inverted_index_search.cc b/chrome/browser/chromeos/local_search_service/inverted_index_search.cc
index 4bdc56b1..634cc802 100644
--- a/chrome/browser/chromeos/local_search_service/inverted_index_search.cc
+++ b/chrome/browser/chromeos/local_search_service/inverted_index_search.cc
@@ -6,11 +6,13 @@
 
 #include <utility>
 
+#include "base/i18n/rtl.h"
 #include "base/optional.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/chromeos/local_search_service/content_extraction_utils.h"
 #include "chrome/browser/chromeos/local_search_service/inverted_index.h"
+#include "chrome/common/string_matching/tokenized_string.h"
 
 namespace local_search_service {
 
@@ -68,11 +70,40 @@
   return num_deleted;
 }
 
-// TODO(jiameng): add impl.
 ResponseStatus InvertedIndexSearch::Find(const base::string16& query,
                                          uint32_t max_results,
                                          std::vector<Result>* results) {
-  return ResponseStatus::kEmptyIndex;
+  DCHECK(results);
+  results->clear();
+  if (query.empty()) {
+    return ResponseStatus::kEmptyQuery;
+  }
+  if (GetSize() == 0u)
+    return ResponseStatus::kEmptyIndex;
+
+  // TODO(jiameng): actual input query may not be the same as default locale.
+  // Need another way to determine actual language of the query.
+  const TokenizedString::Mode mode =
+      IsNonLatinLocale(base::i18n::GetConfiguredLocale())
+          ? TokenizedString::Mode::kCamelCase
+          : TokenizedString::Mode::kWords;
+
+  const TokenizedString tokenized_query(query, mode);
+  std::unordered_set<base::string16> tokens;
+  for (const auto& token : tokenized_query.tokens()) {
+    // TODO(jiameng): we are not removing stopword because they shouldn't exist
+    // in the index. However, for performance reason, it may be worth to be
+    // removed.
+    tokens.insert(token);
+  }
+
+  // TODO(jiameng): allow thresholds to be passed in as search params.
+  *results = inverted_index_->FindMatchingDocumentsApproximately(
+      tokens, 0.1 /* prefix_threhold */, 0.6 /* block_threshold */);
+
+  if (results->size() > max_results && max_results > 0u)
+    results->resize(max_results);
+  return ResponseStatus::kSuccess;
 }
 
 std::vector<std::pair<std::string, uint32_t>>
diff --git a/chrome/browser/chromeos/local_search_service/inverted_index_search_unittest.cc b/chrome/browser/chromeos/local_search_service/inverted_index_search_unittest.cc
index a42a555..013f49d 100644
--- a/chrome/browser/chromeos/local_search_service/inverted_index_search_unittest.cc
+++ b/chrome/browser/chromeos/local_search_service/inverted_index_search_unittest.cc
@@ -150,4 +150,110 @@
   }
 }
 
+TEST_F(InvertedIndexSearchTest, Find) {
+  const std::map<std::string, std::vector<ContentWithId>> data_to_register = {
+      {"id1",
+       {{"cid_1", "This is a help wi-fi article"},
+        {"cid_2", "Another help help wi-fi"}}},
+      {"id2", {{"cid_3", "help article on wi-fi"}}}};
+  const std::vector<Data> data = CreateTestData(data_to_register);
+
+  // Nothing has been added to the index.
+  std::vector<Result> results;
+  EXPECT_EQ(
+      search_.Find(base::UTF8ToUTF16("network"), /*max_results=*/10, &results),
+      ResponseStatus::kEmptyIndex);
+  EXPECT_TRUE(results.empty());
+
+  // Data is added and then deleted from index, making the index empty.
+  search_.AddOrUpdate(data);
+  EXPECT_EQ(search_.GetSize(), 2u);
+  EXPECT_EQ(search_.Delete({"id1", "id2"}), 2u);
+  EXPECT_EQ(search_.GetSize(), 0u);
+
+  EXPECT_EQ(
+      search_.Find(base::UTF8ToUTF16("network"), /*max_results=*/10, &results),
+      ResponseStatus::kEmptyIndex);
+  EXPECT_TRUE(results.empty());
+
+  // Index is populated again, but query is empty.
+  search_.AddOrUpdate(data);
+  EXPECT_EQ(search_.GetSize(), 2u);
+
+  EXPECT_EQ(search_.Find(base::UTF8ToUTF16(""), /*max_results=*/10, &results),
+            ResponseStatus::kEmptyQuery);
+  EXPECT_TRUE(results.empty());
+
+  // No document is found for a given query.
+  EXPECT_EQ(search_.Find(base::UTF8ToUTF16("networkstuff"), /*max_results=*/10,
+                         &results),
+            ResponseStatus::kSuccess);
+  EXPECT_TRUE(results.empty());
+
+  {
+    // A document is found.
+    // Query's case is normalized.
+    EXPECT_EQ(search_.Find(base::UTF8ToUTF16("ANOTHER networkstuff"),
+                           /*max_results=*/10, &results),
+              ResponseStatus::kSuccess);
+    EXPECT_EQ(results.size(), 1u);
+
+    // "another" only exists in "id1".
+    const float expected_score = TfIdfScore(/*num_docs=*/2,
+                                            /*num_docs_with_term=*/1,
+                                            /*num_term_occurrence_in_doc=*/1,
+                                            /*doc_length=*/7);
+    CheckResult(results[0], "id1", expected_score,
+                /*expected_number_positions=*/1);
+  }
+
+  {
+    // Two documents are found.
+    EXPECT_EQ(search_.Find(base::UTF8ToUTF16("another help"),
+                           /*max_results=*/10, &results),
+              ResponseStatus::kSuccess);
+    EXPECT_EQ(results.size(), 2u);
+
+    // "id1" score comes from both "another" and "help".
+    const float expected_score_id1 =
+        TfIdfScore(/*num_docs=*/2,
+                   /*num_docs_with_term=*/1,
+                   /*num_term_occurrence_in_doc=*/1,
+                   /*doc_length=*/7) +
+        TfIdfScore(/*num_docs=*/2,
+                   /*num_docs_with_term=*/2,
+                   /*num_term_occurrence_in_doc=*/3,
+                   /*doc_length=*/7);
+    // "id2" score comes "help".
+    const float expected_score_id2 =
+        TfIdfScore(/*num_docs=*/2,
+                   /*num_docs_with_term=*/2,
+                   /*num_term_occurrence_in_doc=*/1,
+                   /*doc_length=*/3);
+
+    EXPECT_GE(expected_score_id1, expected_score_id2);
+    CheckResult(results[0], "id1", expected_score_id1,
+                /*expected_number_positions=*/4);
+    CheckResult(results[1], "id2", expected_score_id2,
+                /*expected_number_positions=*/1);
+  }
+
+  {
+    // Same as above,  but max number of results is set to 1.
+    EXPECT_EQ(search_.Find(base::UTF8ToUTF16("another help"), /*max_results=*/1,
+                           &results),
+              ResponseStatus::kSuccess);
+    EXPECT_EQ(results.size(), 1u);
+    EXPECT_EQ(results[0].id, "id1");
+  }
+
+  {
+    // Same as above, but set max_results to 0, meaning no max.
+    EXPECT_EQ(search_.Find(base::UTF8ToUTF16("another help"), /*max_results=*/0,
+                           &results),
+              ResponseStatus::kSuccess);
+    EXPECT_EQ(results.size(), 2u);
+  }
+}
+
 }  // namespace local_search_service
diff --git a/chrome/browser/chromeos/local_search_service/inverted_index_unittest.cc b/chrome/browser/chromeos/local_search_service/inverted_index_unittest.cc
index 33b2f9a..205b84b 100644
--- a/chrome/browser/chromeos/local_search_service/inverted_index_unittest.cc
+++ b/chrome/browser/chromeos/local_search_service/inverted_index_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/local_search_service/shared_structs.h"
+#include "chrome/browser/chromeos/local_search_service/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -28,15 +29,6 @@
   return scores;
 }
 
-void CheckResult(const Result& result,
-                 const std::string& expected_id,
-                 float expected_score,
-                 size_t expected_number_token_positions) {
-  EXPECT_EQ(result.id, expected_id);
-  EXPECT_NEAR(result.score, expected_score, 0.001);
-  EXPECT_EQ(result.positions.size(), expected_number_token_positions);
-}
-
 }  // namespace
 
 class InvertedIndexTest : public ::testing::Test {
diff --git a/chrome/browser/chromeos/local_search_service/test_utils.cc b/chrome/browser/chromeos/local_search_service/test_utils.cc
index 7971e862..a21b3147 100644
--- a/chrome/browser/chromeos/local_search_service/test_utils.cc
+++ b/chrome/browser/chromeos/local_search_service/test_utils.cc
@@ -35,4 +35,23 @@
   return output;
 }
 
+void CheckResult(const Result& result,
+                 const std::string& expected_id,
+                 float expected_score,
+                 size_t expected_number_positions) {
+  EXPECT_EQ(result.id, expected_id);
+  EXPECT_NEAR(result.score, expected_score, 0.001);
+  EXPECT_EQ(result.positions.size(), expected_number_positions);
+}
+
+float TfIdfScore(size_t num_docs,
+                 size_t num_docs_with_term,
+                 size_t num_term_occurrence_in_doc,
+                 size_t doc_length) {
+  const float idf = 1.0 + log((1.0 + num_docs) / (1.0 + num_docs_with_term));
+
+  const float tf = static_cast<float>(num_term_occurrence_in_doc) / doc_length;
+  return tf * idf;
+}
+
 }  // namespace local_search_service
diff --git a/chrome/browser/chromeos/local_search_service/test_utils.h b/chrome/browser/chromeos/local_search_service/test_utils.h
index 04fb3d8..e9f0f08 100644
--- a/chrome/browser/chromeos/local_search_service/test_utils.h
+++ b/chrome/browser/chromeos/local_search_service/test_utils.h
@@ -23,6 +23,17 @@
     const std::map<std::string,
                    std::vector<std::pair<std::string, std::string>>>& input);
 
+// Checks |result|'s id, score and number of matching positions are expected.
+void CheckResult(const Result& result,
+                 const std::string& expected_id,
+                 float expected_score,
+                 size_t expected_number_positions);
+
+float TfIdfScore(size_t num_docs,
+                 size_t num_docs_with_term,
+                 size_t num_term_occurrence_in_doc,
+                 size_t doc_length);
+
 }  // namespace local_search_service
 
 #endif  // CHROME_BROWSER_CHROMEOS_LOCAL_SEARCH_SERVICE_TEST_UTILS_H_
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
index 296879d..175cf3f 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.cc
@@ -85,8 +85,7 @@
 
 class EasyUnlockService::PowerMonitor : public PowerManagerClient::Observer {
  public:
-  explicit PowerMonitor(EasyUnlockService* service)
-      : service_(service), waking_up_(false) {
+  explicit PowerMonitor(EasyUnlockService* service) : service_(service) {
     PowerManagerClient::Get()->AddObserver(this);
   }
 
@@ -103,8 +102,6 @@
     wake_up_time_ = base::Time();
   }
 
-  bool waking_up() const { return waking_up_; }
-
  private:
   // PowerManagerClient::Observer:
   void SuspendImminent(power_manager::SuspendImminent::Reason reason) override {
@@ -112,7 +109,6 @@
   }
 
   void SuspendDone(const base::TimeDelta& sleep_duration) override {
-    waking_up_ = true;
     wake_up_time_ = base::Time::Now();
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE,
@@ -125,12 +121,10 @@
   }
 
   void ResetWakingUp() {
-    waking_up_ = false;
     service_->UpdateAppState();
   }
 
   EasyUnlockService* service_;
-  bool waking_up_;
   base::Time wake_up_time_;
   base::WeakPtrFactory<PowerMonitor> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc
index e930bc6..aa1a8f1 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc
@@ -10,4 +10,31 @@
 
 PluginVmManager::~PluginVmManager() = default;
 
+bool PluginVmManager::GetPermission(PermissionType permission_type) {
+  auto it = permissions_.find(permission_type);
+  DCHECK(it != permissions_.end());
+  return it->second;
+}
+
+void PluginVmManager::SetPermission(PermissionType permission_type,
+                                    bool value) {
+  auto it = permissions_.find(permission_type);
+  DCHECK(it != permissions_.end());
+  if (it->second == value) {
+    return;
+  }
+  it->second = value;
+  for (auto& observer : plugin_vm_permissions_observers_) {
+    observer.OnPluginVmPermissionsChanged(permission_type, value);
+  }
+}
+void PluginVmManager::AddPluginVmPermissionsObserver(
+    PluginVmPermissionsObserver* observer) {
+  plugin_vm_permissions_observers_.AddObserver(observer);
+}
+void PluginVmManager::RemovePluginVmPermissionsObserver(
+    PluginVmPermissionsObserver* observer) {
+  plugin_vm_permissions_observers_.RemoveObserver(observer);
+}
+
 }  // namespace plugin_vm
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h b/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h
index 6eba7c6b..bbf2e05 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h
@@ -8,6 +8,8 @@
 #include <string>
 
 #include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
+#include "base/observer_list.h"
 #include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher.pb.h"
 #include "components/keyed_service/core/keyed_service.h"
 
@@ -17,6 +19,15 @@
 
 namespace plugin_vm {
 
+enum class PermissionType { kCamera = 0, kMicrophone = 1 };
+
+class PluginVmPermissionsObserver : public base::CheckedObserver {
+ public:
+  virtual void OnPluginVmPermissionsChanged(
+      plugin_vm::PermissionType permission_type,
+      bool allowed) = 0;
+};
+
 class PluginVmManager : public KeyedService {
  public:
   using LaunchPluginVmCallback = base::OnceCallback<void(bool success)>;
@@ -49,10 +60,24 @@
   virtual void RemoveVmStartingObserver(
       chromeos::VmStartingObserver* observer) = 0;
 
+  // Add/remove permissions observers
+  void AddPluginVmPermissionsObserver(PluginVmPermissionsObserver* observer);
+  void RemovePluginVmPermissionsObserver(PluginVmPermissionsObserver* observer);
+
   virtual vm_tools::plugin_dispatcher::VmState vm_state() const = 0;
+  bool GetPermission(PermissionType permission_type);
+  void SetPermission(PermissionType permission_type, bool value);
 
  protected:
   PluginVmManager();
+
+ private:
+  base::flat_map<PermissionType, bool> permissions_ = {
+      {PermissionType::kCamera, false},
+      {PermissionType::kMicrophone, false}};
+
+  base::ObserverList<PluginVmPermissionsObserver>
+      plugin_vm_permissions_observers_;
 };
 
 }  // namespace plugin_vm
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_util_unittest.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_util_unittest.cc
index a686e97d..057abf0 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_util_unittest.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_util_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/tpm/stub_install_attributes.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -27,6 +28,11 @@
   MOCK_METHOD(void, OnPolicyChanged, (bool));
 
  protected:
+  struct ScopedDBusThreadManager {
+    ScopedDBusThreadManager() { chromeos::DBusThreadManager::Initialize(); }
+    ~ScopedDBusThreadManager() { chromeos::DBusThreadManager::Shutdown(); }
+  } dbus_thread_manager_;
+
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<TestingProfile> testing_profile_;
   std::unique_ptr<PluginVmTestHelper> test_helper_;
diff --git a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc
index ca1c064..780632e 100644
--- a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc
@@ -83,16 +83,19 @@
   chunk[0] = '\0';
   last_chunk[0] = '\0';
 
+  size_t total_bytes_read = 0;
   size_t bytes_read = 0;
 
   // Since most logs are not seekable, read until the end keeping tracking of
   // last two chunks.
   while ((bytes_read = fread(chunk.get(), 1, max_size, fp.get())) == max_size) {
+    total_bytes_read += bytes_read;
     last_chunk.swap(chunk);
     chunk[0] = '\0';
   }
+  total_bytes_read += bytes_read;
 
-  if (last_chunk[0] == '\0') {
+  if (total_bytes_read < max_size) {
     // File is smaller than max_size
     contents->assign(chunk.get(), bytes_read);
   } else if (bytes_read == 0) {
diff --git a/chrome/browser/chromeos/system_logs/debug_daemon_log_source_unittest.cc b/chrome/browser/chromeos/system_logs/debug_daemon_log_source_unittest.cc
index 2116b9c..ab5354b 100644
--- a/chrome/browser/chromeos/system_logs/debug_daemon_log_source_unittest.cc
+++ b/chrome/browser/chromeos/system_logs/debug_daemon_log_source_unittest.cc
@@ -36,7 +36,7 @@
   const char kTestData[] = "0123456789";  // Length of 10
   std::string read_data;
 
-  base::FilePath file_path = temp_dir_.GetPath().Append("test.txt");
+  base::FilePath file_path = temp_dir_.GetPath().Append("test_small.txt");
 
   WriteFile(file_path, kTestData, strlen(kTestData));
 
@@ -61,6 +61,36 @@
   EXPECT_EQ("56789", read_data);
 }
 
+TEST_F(DebugDaemonLogSourceTest, ReadEndOfFileWithZeros) {
+  const size_t test_size = 10;
+  std::string test_data("abcd\0\0\0\0hi", test_size);
+  std::string read_data;
+
+  base::FilePath file_path = temp_dir_.GetPath().Append("test_zero.txt");
+
+  WriteFile(file_path, test_data.data(), test_size);
+
+  read_data.clear();
+  EXPECT_TRUE(ReadEndOfFile(file_path, &read_data, 15));
+  EXPECT_EQ(test_data, read_data);
+
+  read_data.clear();
+  EXPECT_TRUE(ReadEndOfFile(file_path, &read_data, 10));
+  EXPECT_EQ(test_data, read_data);
+
+  read_data.clear();
+  EXPECT_TRUE(ReadEndOfFile(file_path, &read_data, 2));
+  EXPECT_EQ(test_data.substr(test_size - 2, 2), read_data);
+
+  read_data.clear();
+  EXPECT_TRUE(ReadEndOfFile(file_path, &read_data, 3));
+  EXPECT_EQ(test_data.substr(test_size - 3, 3), read_data);
+
+  read_data.clear();
+  EXPECT_TRUE(ReadEndOfFile(file_path, &read_data, 5));
+  EXPECT_EQ(test_data.substr(test_size - 5, 5), read_data);
+}
+
 TEST_F(DebugDaemonLogSourceTest, ReadEndOfFileMedium) {
   std::string test_data = base::RandBytesAsString(10000);  // 10KB data
   std::string read_data;
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
index fe73390..901cfe6 100644
--- a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
+++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
@@ -523,7 +523,6 @@
   bool is_policy_location = Manifest::IsPolicyLocation(extension.location());
   if (is_policy_location) {
     info->controlled_info = std::make_unique<developer::ControlledInfo>();
-    info->controlled_info->type = developer::CONTROLLER_TYPE_POLICY;
     info->controlled_info->text =
         l10n_util::GetStringUTF8(IDS_EXTENSIONS_INSTALL_LOCATION_ENTERPRISE);
   }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 6014c6e..7947d8cf 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2620,7 +2620,7 @@
   {
     "name": "font-access",
     "owners": [ "cmp", "oyiptong" ],
-    "expiry_milestone": 85
+    "expiry_milestone": 89
   },
   {
     "name": "force-color-profile",
@@ -3547,6 +3547,11 @@
     "expiry_milestone": 87
   },
   {
+    "name": "os-settings-deep-linking",
+    "owners": [ "cros-customization@google.com", "hsuregan", "khorimoto" ],
+    "expiry_milestone": 90
+  },
+  {
     "name": "overlay-new-layout",
     "owners": [ "donnd", "jinsukkim", "contextual-search-eng" ],
     "expiry_milestone": 81
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index c2d05b5d..850c1d0c 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1606,6 +1606,11 @@
     "bugs, visual artifacts, and performance cost. This implementation may be "
     "removed at any time.";
 
+const char kOsSettingsDeepLinkingName[] = "CrOS Settings Deep Linking";
+const char kOsSettingsDeepLinkingDescription[] =
+    "Enables a unique URL for each path in CrOS settings. "
+    "This allows deep linking to individual settings, i.e. in settings search.";
+
 const char kOverlayNewLayoutName[] = "Overlay new layout";
 const char kOverlayNewLayoutDescription[] =
     "Enables a new layout for the "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 724f0a0..8c219ed 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -929,6 +929,9 @@
 extern const char kNewOsSettingsSearchName[];
 extern const char kNewOsSettingsSearchDescription[];
 
+extern const char kOsSettingsDeepLinkingName[];
+extern const char kOsSettingsDeepLinkingDescription[];
+
 extern const char kDlcSettingsUiName[];
 extern const char kDlcSettingsUiDescription[];
 
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
index 5429900..b7d651d6 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -318,19 +318,30 @@
 }
 
 bool ShouldBlockBySpoofCheckResult(const DomainInfo& navigated_domain) {
-  const url_formatter::IDNSpoofChecker::Result spoof_check_result =
-      navigated_domain.idn_result.spoof_check_result;
   // Here, only a subset of spoof checks that cause an IDN to fallback to
   // punycode are configured to show an interstitial.
-  if (spoof_check_result ==
-      url_formatter::IDNSpoofChecker::Result::kICUSpoofChecks) {
-    // If the eTLD+1 contains only a mix of ASCII + Emoji, allow.
-    return !IsASCIIAndEmojiOnly(navigated_domain.idn_result.result);
+  switch (navigated_domain.idn_result.spoof_check_result) {
+    case url_formatter::IDNSpoofChecker::Result::kNone:
+    case url_formatter::IDNSpoofChecker::Result::kSafe:
+      return false;
+
+    case url_formatter::IDNSpoofChecker::Result::kICUSpoofChecks:
+      // If the eTLD+1 contains only a mix of ASCII + Emoji, allow.
+      return !IsASCIIAndEmojiOnly(navigated_domain.idn_result.result);
+
+    case url_formatter::IDNSpoofChecker::Result::kDeviationCharacters:
+      // Failures because of deviation characters, especially ß, is common.
+      return false;
+
+    case url_formatter::IDNSpoofChecker::Result::kTLDSpecificCharacters:
+    case url_formatter::IDNSpoofChecker::Result::kUnsafeMiddleDot:
+    case url_formatter::IDNSpoofChecker::Result::kWholeScriptConfusable:
+    case url_formatter::IDNSpoofChecker::Result::kDigitLookalikes:
+    case url_formatter::IDNSpoofChecker::Result::
+        kNonAsciiLatinCharMixedWithNonLatin:
+    case url_formatter::IDNSpoofChecker::Result::kDangerousPattern:
+      return true;
   }
-  return spoof_check_result ==
-             url_formatter::IDNSpoofChecker::Result::kUnsafeMiddleDot ||
-         spoof_check_result ==
-             url_formatter::IDNSpoofChecker::Result::kTLDSpecificCharacters;
 }
 
 ThrottleCheckResult LookalikeUrlNavigationThrottle::PerformChecks(
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc
index 141a9fcf..d3674352 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc
@@ -8,6 +8,8 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/lookalikes/core/features.h"
+#include "components/url_formatter/spoof_checks/idn_spoof_checker.h"
+#include "components/url_formatter/url_formatter.h"
 #include "content/public/test/mock_navigation_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -90,18 +92,42 @@
   const struct TestCase {
     const char* hostname;
     bool expected_blocked;
+    url_formatter::IDNSpoofChecker::Result expected_spoof_check_result;
   } kTestCases[] = {
       // ASCII private domain.
-      {"private.hostname", false},
-      // Unsafe middle dot.
-      {"example·com.com", true},
-      // Fails ICU spoof checks.
-      {"lɔlocked.com", true},
-      // Contains a TLD specific character
-      {"þook.com", true},
-      // Also fails ICU spoof checks, but is allowed because consists of only
+      {"private.hostname", false,
+       url_formatter::IDNSpoofChecker::Result::kNone},
+
+      // lɔlocked.com, fails ICU spoof checks.
+      {"xn--llocked-9bd.com", true,
+       url_formatter::IDNSpoofChecker::Result::kICUSpoofChecks},
+      // þook.com, contains a TLD specific character (þ).
+      {"xn--ook-ooa.com", true,
+       url_formatter::IDNSpoofChecker::Result::kTLDSpecificCharacters},
+      // example·com.com, unsafe middle dot.
+      {"xn--examplecom-rra.com", true,
+       url_formatter::IDNSpoofChecker::Result::kUnsafeMiddleDot},
+      // scope.com, with scope in Cyrillic. Whole script confusable.
+      {"xn--e1argc3h.com", true,
+       url_formatter::IDNSpoofChecker::Result::kWholeScriptConfusable},
+      //  Non-ASCII Latin with Non-Latin character
+      {"xn--caf-dma9024xvpg.kr", true,
+       url_formatter::IDNSpoofChecker::Result::
+           kNonAsciiLatinCharMixedWithNonLatin},
+      // testーsite.com, has dangerous pattern (ー is CJK character).
+      {"xn--testsite-1g5g.com", true,
+       url_formatter::IDNSpoofChecker::Result::kDangerousPattern},
+
+      // TODO(crbug.com/1100485): Add an example for digit lookalikes.
+
+      // 🍕.com, fails ICU spoof checks, but is allowed because consists of only
       // emoji and ASCII.
-      {"🍕.com", false},
+      {"xn--vi8h.com", false,
+       url_formatter::IDNSpoofChecker::Result::kICUSpoofChecks},
+      // sparkasse-gießen.de, has a deviation character (ß). This is in punycode
+      // because GURL canonicalizes ß to ss.
+      {"xn--sparkasse-gieen-2ib.de", false,
+       url_formatter::IDNSpoofChecker::Result::kDeviationCharacters},
   };
 
   base::test::ScopedFeatureList feature_list;
@@ -109,6 +135,12 @@
       lookalikes::features::kLookalikeInterstitialForPunycode);
 
   for (const TestCase& test_case : kTestCases) {
+    url_formatter::IDNConversionResult idn_result =
+        url_formatter::UnsafeIDNToUnicodeWithDetails(test_case.hostname);
+    ASSERT_EQ(test_case.expected_spoof_check_result,
+              idn_result.spoof_check_result)
+        << test_case.hostname;
+
     GURL url(std::string("http://") + test_case.hostname);
     content::MockNavigationHandle handle(url, main_rfh());
     handle.set_page_transition(ui::PAGE_TRANSITION_TYPED);
diff --git a/chrome/browser/pdf/OWNERS b/chrome/browser/pdf/OWNERS
index b99975c..d9b356d 100644
--- a/chrome/browser/pdf/OWNERS
+++ b/chrome/browser/pdf/OWNERS
@@ -2,4 +2,6 @@
 
 per-file pdf_extension_test.*=dpapad@chromium.org
 per-file pdf_extension_test.*=rbpotter@chromium.org
+per-file pdf_extension_util.*=dpapad@chromium.org
+per-file pdf_extension_util.*=rbpotter@chromium.org
 # COMPONENT: Internals>Plugins>PDF
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 7dfecac..c44cc77f 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -815,10 +815,6 @@
   RunTestsInJsModule("whitespace_title_test.js", "test-whitespace-title.pdf");
 }
 
-IN_PROC_BROWSER_TEST_F(PDFExtensionJSTest, TwoUpViewFeature) {
-  RunTestsInJsModule("two_up_view_feature_test.js", "test.pdf");
-}
-
 IN_PROC_BROWSER_TEST_F(PDFExtensionJSTest, PageChange) {
   RunTestsInJsModule("page_change_test.js", "test-bookmarks.pdf");
 }
diff --git a/chrome/browser/printing/print_preview_message_handler.cc b/chrome/browser/printing/print_preview_message_handler.cc
index db10fc0..4963ed0 100644
--- a/chrome/browser/printing/print_preview_message_handler.cc
+++ b/chrome/browser/printing/print_preview_message_handler.cc
@@ -217,7 +217,7 @@
 
 void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
     content::RenderFrameHost* render_frame_host,
-    const PrintHostMsg_DidPreviewDocument_Params& params,
+    const mojom::DidPreviewDocumentParams& params,
     const PrintHostMsg_PreviewIds& ids) {
   // Always try to stop the worker.
   StopWorker(params.document_cookie);
@@ -229,7 +229,7 @@
   const bool composite_document_using_individual_pages =
       ShouldUseCompositor(print_preview_ui);
   const base::ReadOnlySharedMemoryRegion& metafile =
-      params.content.metafile_data_region;
+      params.content->metafile_data_region;
 
   // When the Print Compositor is active, the print document is composed from
   // the individual pages, so |metafile| should be invalid.
@@ -238,7 +238,7 @@
   if (composite_document_using_individual_pages == metafile.IsValid())
     return;
 
-  if (params.expected_pages_count <= 0) {
+  if (params.expected_pages_count == 0) {
     NOTREACHED();
     return;
   }
diff --git a/chrome/browser/printing/print_preview_message_handler.h b/chrome/browser/printing/print_preview_message_handler.h
index f92ea2b..9d42699 100644
--- a/chrome/browser/printing/print_preview_message_handler.h
+++ b/chrome/browser/printing/print_preview_message_handler.h
@@ -15,7 +15,6 @@
 #include "content/public/browser/web_contents_user_data.h"
 #include "printing/mojom/print.mojom-forward.h"
 
-struct PrintHostMsg_DidPreviewDocument_Params;
 struct PrintHostMsg_PreviewIds;
 struct PrintHostMsg_RequestPrintPreview_Params;
 
@@ -77,10 +76,9 @@
   void OnDidPreviewPage(content::RenderFrameHost* render_frame_host,
                         const mojom::DidPreviewPageParams& params,
                         const PrintHostMsg_PreviewIds& ids);
-  void OnMetafileReadyForPrinting(
-      content::RenderFrameHost* render_frame_host,
-      const PrintHostMsg_DidPreviewDocument_Params& params,
-      const PrintHostMsg_PreviewIds& ids);
+  void OnMetafileReadyForPrinting(content::RenderFrameHost* render_frame_host,
+                                  const mojom::DidPreviewDocumentParams& params,
+                                  const PrintHostMsg_PreviewIds& ids);
 
   void NotifyUIPreviewPageReady(
       PrintPreviewUI* print_preview_ui,
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/auto_scan_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/auto_scan_manager_test.js
index d12518f..9c8e970b 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/auto_scan_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/auto_scan_manager_test.js
@@ -6,17 +6,8 @@
 
 UNDEFINED_INTERVAL_DELAY = -1;
 
-/**
- * @constructor
- * @extends {SwitchAccessE2ETest}
- */
-function SwitchAccessAutoScanManagerTest() {
-  SwitchAccessE2ETest.call(this);
-}
-
-SwitchAccessAutoScanManagerTest.prototype = {
-  __proto__: SwitchAccessE2ETest.prototype,
-
+/** Test fixture for auto scan manager. */
+SwitchAccessAutoScanManagerTest = class extends SwitchAccessE2ETest {
   /** @override */
   setUp() {
     AutoScanManager.instance.primaryScanTime_ = 1000;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js
index e619f5de..4fc756e 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/navigation_manager_test.js
@@ -4,22 +4,13 @@
 
 GEN_INCLUDE(['switch_access_e2e_test_base.js']);
 
-/**
- * @constructor
- * @extends {SwitchAccessE2ETest}
- */
-function SwitchAccessNavigationManagerTest() {
-  SwitchAccessE2ETest.call(this);
-  this.navigator = NavigationManager.instance;
-  BackButtonNode
-      .locationForTesting = {top: 10, left: 10, width: 20, height: 20};
-}
-
-SwitchAccessNavigationManagerTest.prototype = {
-  __proto__: SwitchAccessE2ETest.prototype,
-
+/** Test fixture for the navigation manager. */
+SwitchAccessNavigationManagerTest = class extends SwitchAccessE2ETest {
   /** @override */
   setUp() {
+    this.navigator = NavigationManager.instance;
+    BackButtonNode
+        .locationForTesting = {top: 10, left: 10, width: 20, height: 20};
     MenuManager.initialize();
   }
 };
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js
index b53e4aa..cb3b50ee 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js
@@ -4,17 +4,8 @@
 
 GEN_INCLUDE(['../switch_access_e2e_test_base.js']);
 
-/**
- * @constructor
- * @extends {SwitchAccessE2ETest}
- */
-function SwitchAccessDesktopNodeTest() {
-  SwitchAccessE2ETest.call(this);
-}
-
-SwitchAccessDesktopNodeTest.prototype = {
-  __proto__: SwitchAccessE2ETest.prototype,
-};
+/** Test fixture for the desktop node. */
+SwitchAccessDesktopNodeTest = class extends SwitchAccessE2ETest {};
 
 TEST_F('SwitchAccessDesktopNodeTest', 'Build', function() {
   this.runWithLoadedTree('', (desktop) => {
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper_test.js
index 27ae6a4c..8781b3db 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/node_wrapper_test.js
@@ -4,17 +4,8 @@
 
 GEN_INCLUDE(['../switch_access_e2e_test_base.js']);
 
-/**
- * @constructor
- * @extends {SwitchAccessE2ETest}
- */
-function SwitchAccessNodeWrapperTest() {
-  SwitchAccessE2ETest.call(this);
-}
-
-SwitchAccessNodeWrapperTest.prototype = {
-  __proto__: SwitchAccessE2ETest.prototype,
-};
+/** Test fixture for the node wrapper type. */
+SwitchAccessNodeWrapperTest = class extends SwitchAccessE2ETest {};
 
 TEST_F('SwitchAccessNodeWrapperTest', 'AsRootNode', function() {
   const website = `<div aria-label="outer">
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js
index 8f6e43d..62b9181 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js
@@ -4,21 +4,12 @@
 
 GEN_INCLUDE(['../switch_access_e2e_test_base.js']);
 
-/**
- * @constructor
- * @extends {SwitchAccessE2ETest}
- */
-function SwitchAccessTabNodeTest() {
-  SwitchAccessE2ETest.call(this);
-}
-
-SwitchAccessTabNodeTest.prototype = {
-  __proto__: SwitchAccessE2ETest.prototype,
-
+/** Test fixture for the tab node type. */
+SwitchAccessTabNodeTest = class extends SwitchAccessE2ETest {
   /** @override */
   setUp() {
     MenuManager.initialize();
-  },
+  }
 };
 
 TEST_F('SwitchAccessTabNodeTest', 'FindCloseButton', function() {
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/rect_helper_unittest.js b/chrome/browser/resources/chromeos/accessibility/switch_access/rect_helper_unittest.js
index 86a9de79..80657ab 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/rect_helper_unittest.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/rect_helper_unittest.js
@@ -2,23 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * Test fixture for rect_helper.js.
- * @constructor
- * @extends {testing.Test}
- */
-function SwitchAccessRectHelperUnitTest() {
-  testing.Test.call(this);
-}
+/** Test fixture for rect_helper.js. */
+SwitchAccessRectHelperUnitTest = class extends testing.Test {};
 
-SwitchAccessRectHelperUnitTest.prototype = {
-  __proto__: testing.Test.prototype,
-
-  /** @override */
-  extraLibraries: [
-    'rect_helper.js',
-  ],
-};
+/** @override */
+SwitchAccessRectHelperUnitTest.prototype.extraLibraries = ['rect_helper.js'];
 
 TEST_F('SwitchAccessRectHelperUnitTest', 'Adjacent', function() {
   const baseRect = {left: 10, top: 10, width: 10, height: 10};
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
index 0c2796e..8d2909b 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
@@ -7,28 +7,12 @@
   '../common/testing/assert_additions.js'
 ]);
 
-/**
- * Base class for browser tests for Switch Access.
- * @constructor
- */
-function SwitchAccessE2ETest() {
-  this.callbackHelper_ = new CallbackHelper(this);
-}
-
-SwitchAccessE2ETest.prototype = {
-  __proto__: testing.Test.prototype,
-
-  /**
-   * @override
-   * No UI in the background context.
-   */
-  runAccessibilityChecks: false,
-
-  /** @override */
-  isAsync: true,
-
-  /** @override */
-  browsePreload: null,
+/** Base class for browser tests for Switch Access. */
+SwitchAccessE2ETest = class extends testing.Test {
+  constructor() {
+    super();
+    this.callbackHelper_ = new CallbackHelper(this);
+  }
 
   /** @override */
   testGenCppIncludes() {
@@ -43,7 +27,7 @@
 #include "ui/accessibility/accessibility_switches.h"
 #include "ash/keyboard/ui/keyboard_util.h"
     `);
-  },
+  }
 
   /** @override */
   testGenPreamble() {
@@ -57,7 +41,7 @@
   chromeos::AccessibilityManager::Get()->SetSwitchAccessEnabled(true);
   WaitForExtension(extension_misc::kSwitchAccessExtensionId, load_cb);
     `);
-  },
+  }
 
   /**
    * Creates a callback that optionally calls {@code opt_callback} when
@@ -69,7 +53,7 @@
    */
   newCallback(opt_callback) {
     return this.callbackHelper_.wrap(opt_callback);
-  },
+  }
 
   /**
    * @param {function(AutomationNode): boolean} predicate A predicate that
@@ -88,7 +72,7 @@
     assertNullOrUndefined(
         treeWalker.next().node, 'Found more than one ' + nodeString + '.');
     return node;
-  },
+  }
 
   /**
    * @param {string} id The HTML id of an element.
@@ -98,7 +82,7 @@
     const predicate = (node) => node.htmlAttributes.id === id;
     const nodeString = 'node with id "' + id + '"';
     return this.findNodeMatchingPredicate(predicate, nodeString);
-  },
+  }
 
   /**
    * @param {string} name The name of the node within the automation tree.
@@ -109,7 +93,7 @@
     const predicate = (node) => node.name === name && node.role === role;
     const nodeString = 'node with name "' + name + '" and role ' + role;
     return this.findNodeMatchingPredicate(predicate, nodeString);
-  },
+  }
 
   /**
    * @param {function(): boolean} predicate The condition under which the
@@ -130,7 +114,7 @@
     };
     NavigationManager.desktopNode.addEventListener(
         'childrenChanged', listener, false /* capture */);
-  },
+  }
 
   /**
    * From chromevox_next_e2e_test_base.js
@@ -156,7 +140,7 @@
       var createParams = {active: true, url};
       chrome.tabs.create(createParams, function(unused_tab) {
         chrome.automation.getTree(function(returnedRootNode) {
-          rootNode = returnedRootNode;
+          const rootNode = returnedRootNode;
           if (rootNode.docLoaded) {
             callback && callback(desktopRootNode);
             callback = null;
@@ -175,5 +159,12 @@
         });
       });
     }.bind(this));
-  },
+  }
 };
+
+/** @override */
+SwitchAccessE2ETest.prototype.isAsync = true;
+/** @override */
+SwitchAccessE2ETest.prototype.runAccessibilityChecks = false;
+/** @override */
+SwitchAccessE2ETest.prototype.browserPreload = null;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate_test.js
index 721b47bc..79f2b835 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate_test.js
@@ -4,17 +4,8 @@
 
 GEN_INCLUDE(['switch_access_e2e_test_base.js']);
 
-/**
- * @constructor
- * @extends {SwitchAccessE2ETest}
- */
-function SwitchAccessPredicateTest() {
-  SwitchAccessE2ETest.call(this);
-}
-
-SwitchAccessPredicateTest.prototype = {
-  __proto__: SwitchAccessE2ETest.prototype
-};
+/** Test fixture for the Switch Access predicates. */
+SwitchAccessPredicateTest = class extends SwitchAccessE2ETest {};
 
 function fakeLoc(x) {
   return {left: x, top: x, width: x, height: x};
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager_test.js
index 303138e1..20505703 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager_test.js
@@ -4,17 +4,8 @@
 
 GEN_INCLUDE(['switch_access_e2e_test_base.js']);
 
-/**
- * @constructor
- * @extends {SwitchAccessE2ETest}
- */
-function SwitchAccessTextNavigationManagerTest() {
-  SwitchAccessE2ETest.call(this);
-}
-
-SwitchAccessTextNavigationManagerTest.prototype = {
-  __proto__: SwitchAccessE2ETest.prototype,
-
+/** Text fixture for the text navigation manager. */
+SwitchAccessTextNavigationManagerTest = class extends SwitchAccessE2ETest {
   /** @override */
   setUp() {
     TextNavigationManager.initialize();
diff --git a/chrome/browser/resources/extensions/detail_view.html b/chrome/browser/resources/extensions/detail_view.html
index 04c47db..fc42d32 100644
--- a/chrome/browser/resources/extensions/detail_view.html
+++ b/chrome/browser/resources/extensions/detail_view.html
@@ -144,8 +144,8 @@
       <div class="layout horizontal">
         <cr-tooltip-icon hidden$="[[!data.controlledInfo]]"
             tooltip-text="[[data.controlledInfo.text]]"
-            icon-class="[[getIndicatorIcon_(data.controlledInfo.type)]]"
-            icon-aria-label="[[data.controlledInfo.type]]">
+            icon-class="cr20:domain"
+            icon-aria-label="[[data.controlledInfo.text]]">
         </cr-tooltip-icon>
         <template is="dom-if" if="[[showReloadButton_(data.state)]]">
           <cr-button id="terminated-reload-button" class="action-button"
diff --git a/chrome/browser/resources/extensions/detail_view.js b/chrome/browser/resources/extensions/detail_view.js
index fc461db1..36602fa 100644
--- a/chrome/browser/resources/extensions/detail_view.js
+++ b/chrome/browser/resources/extensions/detail_view.js
@@ -305,20 +305,6 @@
   },
 
   /**
-   * @param {chrome.developerPrivate.ControllerType} type
-   * @return {string}
-   * @private
-   */
-  getIndicatorIcon_(type) {
-    switch (type) {
-      case 'POLICY':
-        return 'cr20:domain';
-      default:
-        return '';
-    }
-  },
-
-  /**
    * @return {boolean}
    * @private
    */
diff --git a/chrome/browser/resources/extensions/item_util.js b/chrome/browser/resources/extensions/item_util.js
index 83e727c..a5cb36e 100644
--- a/chrome/browser/resources/extensions/item_util.js
+++ b/chrome/browser/resources/extensions/item_util.js
@@ -79,9 +79,7 @@
  * @return {SourceType}
  */
 export function getItemSource(item) {
-  if (item.controlledInfo &&
-      item.controlledInfo.type ===
-          chrome.developerPrivate.ControllerType.POLICY) {
+  if (item.controlledInfo) {
     return SourceType.POLICY;
   }
 
diff --git a/chrome/browser/resources/pdf/elements/BUILD.gn b/chrome/browser/resources/pdf/elements/BUILD.gn
index 0eaa9db..943f0f5 100644
--- a/chrome/browser/resources/pdf/elements/BUILD.gn
+++ b/chrome/browser/resources/pdf/elements/BUILD.gn
@@ -64,7 +64,6 @@
 }
 
 js_library("viewer-page-selector") {
-  deps = [ "//ui/webui/resources/cr_elements/cr_input:cr_input.m" ]
 }
 
 js_library("viewer-password-screen") {
diff --git a/chrome/browser/resources/pdf/elements/viewer-page-selector.html b/chrome/browser/resources/pdf/elements/viewer-page-selector.html
index d1e2c5d..14116b4 100644
--- a/chrome/browser/resources/pdf/elements/viewer-page-selector.html
+++ b/chrome/browser/resources/pdf/elements/viewer-page-selector.html
@@ -12,7 +12,7 @@
         background: rgba(255, 255, 255, 0.3);
       }
 
-      #pageselector::part(input),
+      input,
       #pagelength {
         /* --page-length-digits is set through JavaScript. 1px is added because
          * the unit 'ch' does not provide exact whole number pixels, and
@@ -20,28 +20,23 @@
         width: calc(max(2, var(--page-length-digits)) * 1ch + 1px);
       }
 
-      #pageselector {
-        --cr-input-background-color: transparent;
-        --cr-input-border-radius: 0;
-        --cr-input-color: white;
-        --cr-input-error-display: none;
-        --cr-input-focus-color: transparent;
-      }
-
-      #pageselector::part(input) {
+      input {
         background: rgba(0, 0, 0, 0.5);
-        box-sizing: content-box;
-        caret-color: var(--cr-input-color);
+        border: none;
+        color: white;
+        font-family: inherit;
+        line-height: inherit;
+        outline: none;
         padding: 0 var(--page-selector-spacing);
+        text-align: center;
       }
 
       #divider {
         margin: 0 var(--page-selector-spacing);
       }
     </style>
-    <cr-input id="pageselector" value="[[pageNo]]" on-mouseup="select"
-        on-value-changed="onInputValueChange_" on-change="pageNoCommitted"
+    <input type="text" id="pageselector" value="[[pageNo]]" on-mouseup="select"
+        on-input="onInput_" on-change="pageNoCommitted"
         aria-label$="$i18n{labelPageNumber}">
-    </cr-input>
     <span id="divider">/</span>
     <span id="pagelength">[[docLength]]</span>
diff --git a/chrome/browser/resources/pdf/elements/viewer-page-selector.js b/chrome/browser/resources/pdf/elements/viewer-page-selector.js
index 6b8b55a..14fadfa 100644
--- a/chrome/browser/resources/pdf/elements/viewer-page-selector.js
+++ b/chrome/browser/resources/pdf/elements/viewer-page-selector.js
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
-
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 Polymer({
@@ -65,7 +63,7 @@
    * Immediately remove any non-digit characters.
    * @private
    */
-  onInputValueChange_() {
+  onInput_() {
     this.pageSelector.value = this.pageSelector.value.replace(/[^\d]/, '');
   },
 });
diff --git a/chrome/browser/resources/pdf/elements/viewer-zoom-button.js b/chrome/browser/resources/pdf/elements/viewer-zoom-button.js
index 3182b90..7aea3a3 100644
--- a/chrome/browser/resources/pdf/elements/viewer-zoom-button.js
+++ b/chrome/browser/resources/pdf/elements/viewer-zoom-button.js
@@ -45,8 +45,7 @@
       reflectToAttribute: true,
     },
 
-    /** @type {?Array<string>} */
-    tooltips: Array,
+    tooltips: String,
 
     /** @private */
     closed_: {
@@ -67,6 +66,12 @@
       computed: 'computeIconsArray_(icons)',
     },
 
+    /** @private {!Array<string>} */
+    tooltips_: {
+      type: Array,
+      computed: 'computeTooltipsArray_(tooltips)',
+    },
+
     /**
      * Icon currently being displayed on the FAB.
      * @private
@@ -79,37 +84,40 @@
     /** @private */
     visibleTooltip_: {
       type: String,
-      computed: 'computeVisibleTooltip_(tooltips, activeIndex)',
-    }
+      computed: 'computeVisibleTooltip_(tooltips_, activeIndex)',
+    },
   },
 
   /**
-   * @param {string} icons Icon names in a string, delimited by spaces
    * @return {!Array<string>} Array of icon name strings
    * @private
    */
-  computeIconsArray_(icons) {
-    return icons.split(' ');
+  computeIconsArray_() {
+    return this.icons.split(' ');
   },
 
   /**
-   * @param {!Array<string>} icons Array of icon name strings.
-   * @param {number} activeIndex Index of the currently active icon.
+   * @return {!Array<string>} Array of tooltip strings
+   * @private
+   */
+  computeTooltipsArray_() {
+    return this.tooltips.split(',');
+  },
+
+  /**
    * @return {string} Icon name for the currently visible icon.
    * @private
    */
-  computeVisibleIcon_(icons, activeIndex) {
-    return icons[activeIndex];
+  computeVisibleIcon_() {
+    return this.icons_[this.activeIndex];
   },
 
   /**
-   * @param {?Array<string>} tooltips Array of tooltip strings.
-   * @param {number} activeIndex Index of the currently active icon.
    * @return {string} Tooltip for the currently visible icon.
    * @private
    */
-  computeVisibleTooltip_(tooltips, activeIndex) {
-    return tooltips === undefined ? '' : tooltips[activeIndex];
+  computeVisibleTooltip_() {
+    return this.tooltips_ === undefined ? '' : this.tooltips_[this.activeIndex];
   },
 
   /** @private */
diff --git a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html
index 341f6bb..a20b91f 100644
--- a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html
+++ b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.html
@@ -44,11 +44,7 @@
         display: block;
       }
 
-      /*
-       * A larger gap between the fit button and the two-up view button
-       * and above the bottom two zoom buttons.
-       */
-      #two-up-view-button,
+      /* A larger gap between the fit button and the bottom two zoom buttons. */
       #zoom-in-button {
         margin-top: 24px;
       }
@@ -60,21 +56,17 @@
     </style>
     <div id="zoom-buttons">
       <viewer-zoom-button id="fit-button" on-fabclick="fitToggle"
-          delay="[[fitButtonDelay_]]"
+          delay="100"
+          tooltips="$i18n{tooltipFitToPage},$i18n{tooltipFitToWidth}"
           keyboard-navigation-active="[[keyboardNavigationActive_]]"
           icons="pdf:fullscreen-exit cr:fullscreen">
       </viewer-zoom-button>
-      <!-- TODO(crbug.com/51472): Change icons for two-up-view-button -->
-      <!-- once they are finalized. -->
-      <viewer-zoom-button id="two-up-view-button" delay="100"
-          disabled="[[annotationMode]]" hidden$="[[!twoUpViewEnabled_]]"
-          on-fabclick="twoUpViewToggle_"
-          keyboard-navigation-active="[[keyboardNavigationActive_]]"
-          icons="pdf:create pdf:eraser"></viewer-zoom-button>
       <viewer-zoom-button id="zoom-in-button" icons="pdf:add"
+          tooltips="$i18n{tooltipZoomIn}"
           keyboard-navigation-active="[[keyboardNavigationActive_]]"
           on-fabclick="zoomIn" delay="50"></viewer-zoom-button>
       <viewer-zoom-button id="zoom-out-button" icons="pdf:remove"
+          tooltips="$i18n{tooltipZoomOut}"
           keyboard-navigation-active="[[keyboardNavigationActive_]]"
           on-fabclick="zoomOut" delay="0"></viewer-zoom-button>
     </div>
diff --git a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js
index f3bab8c..e3863f24 100644
--- a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js
+++ b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js
@@ -11,7 +11,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {FittingType, TwoUpViewAction} from '../constants.js';
+import {FittingType} from '../constants.js';
 
 /**
  * @typedef {{
@@ -33,28 +33,11 @@
   _template: html`{__html_template__}`,
 
   properties: {
-    /** Whether the viewer is currently in annotation mode. */
-    annotationMode: Boolean,
-
     isPrintPreview: {
       type: Boolean,
       value: false,
     },
 
-    strings: {
-      type: Object,
-      observer: 'onStringsSet_',
-    },
-
-    /** @private */
-    twoUpViewEnabled_: Boolean,
-
-    /** @private */
-    fitButtonDelay_: {
-      type: Number,
-      computed: 'computeFitButtonDelay_(twoUpViewEnabled_)',
-    },
-
     /** @private */
     keyboardNavigationActive_: {
       type: Boolean,
@@ -62,14 +45,6 @@
     },
   },
 
-  /**
-   * @return {number} Delay for the fit button.
-   * @private
-   */
-  computeFitButtonDelay_() {
-    return this.twoUpViewEnabled_ ? 150 : 100;
-  },
-
   listeners: {
     'focus': 'onFocus_',
     'keyup': 'onKeyUp_',
@@ -111,31 +86,6 @@
     this.keyboardNavigationActive_ = false;
   },
 
-  /**
-   * Change button tooltips to match any changes to localized strings.
-   * @private
-   */
-  onStringsSet_() {
-    const strings =
-        /**
-         * @type {{tooltipFitToPage: string,
-         *               tooltipFitToWidth: string,
-         *               tooltipTwoUpViewEnable: string,
-         *               tooltipTwoUpViewDisable: string,
-         *               tooltipZoomIn: string,
-         *               tooltipZoomOut: string}}
-         */
-        (this.strings);
-    this.$['fit-button'].tooltips =
-        [strings.tooltipFitToPage, strings.tooltipFitToWidth];
-    this.$['two-up-view-button'].tooltips =
-        [strings.tooltipTwoUpViewEnable, strings.tooltipTwoUpViewDisable];
-    this.$['zoom-in-button'].tooltips = [strings.tooltipZoomIn];
-    this.$['zoom-out-button'].tooltips = [strings.tooltipZoomOut];
-    this.twoUpViewEnabled_ =
-        loadTimeData.getBoolean('pdfTwoUpViewEnabled') && !this.isPrintPreview;
-  },
-
   /** Handle clicks of the fit-button. */
   fitToggle() {
     this.fireFitToChangedEvent_(
@@ -184,20 +134,6 @@
         {fittingType: fittingType, userInitiated: userInitiated});
   },
 
-  /**
-   * Handle clicks of the two-up-view button.
-   * @private
-   */
-  twoUpViewToggle_: function() {
-    assert(this.twoUpViewEnabled_);
-    const twoUpViewAction = this.$['two-up-view-button'].activeIndex ===
-            TWO_UP_VIEW_DISABLED_STATE ?
-        TwoUpViewAction.TWO_UP_VIEW_ENABLE :
-        TwoUpViewAction.TWO_UP_VIEW_DISABLE;
-
-    this.fire('two-up-view-changed', twoUpViewAction);
-  },
-
   /** Handle clicks of the zoom-in-button. */
   zoomIn() {
     this.fire('zoom-in');
@@ -212,7 +148,6 @@
     if (!this.visible_) {
       this.visible_ = true;
       this.$['fit-button'].show();
-      this.$['two-up-view-button'].show();
       this.$['zoom-in-button'].show();
       this.$['zoom-out-button'].show();
     }
@@ -222,7 +157,6 @@
     if (this.visible_) {
       this.visible_ = false;
       this.$['fit-button'].hide();
-      this.$['two-up-view-button'].hide();
       this.$['zoom-in-button'].hide();
       this.$['zoom-out-button'].hide();
     }
diff --git a/chrome/browser/resources/pdf/pdf_viewer.html b/chrome/browser/resources/pdf/pdf_viewer.html
index a56670db7..b4331c9 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.html
+++ b/chrome/browser/resources/pdf/pdf_viewer.html
@@ -43,13 +43,12 @@
     on-password-submitted="onPasswordSubmitted_">
 </viewer-password-screen>
 
-<viewer-zoom-toolbar id="zoom-toolbar" strings="[[strings]]"
-    annotation-mode="[[annotationMode_]]"
-    on-fit-to-changed="onFitToChanged"
-    on-two-up-view-changed="onTwoUpViewChanged_"
-    on-zoom-in="onZoomIn" on-zoom-out="onZoomOut"
-    hidden>
-</viewer-zoom-toolbar>
+<template is="dom-if" if="[[!pdfViewerUpdateEnabled_]]">
+  <viewer-zoom-toolbar id="zoom-toolbar"
+      on-fit-to-changed="onFitToChanged"
+      on-zoom-in="onZoomIn" on-zoom-out="onZoomOut">
+  </viewer-zoom-toolbar>
+</template>
 
 <viewer-error-screen id="error-screen"></viewer-error-screen>
 
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index bf4456f..4a433eb 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -221,11 +221,6 @@
   }
 
   /** @override */
-  getZoomToolbar() {
-    return /** @type {!ViewerZoomToolbarElement} */ (this.$$('#zoom-toolbar'));
-  }
-
-  /** @override */
   getErrorScreen() {
     return /** @type {!ViewerErrorScreenElement} */ (this.$$('#error-screen'));
   }
@@ -238,6 +233,14 @@
     return /** @type {!ViewerPdfToolbarElement} */ (this.$$('#toolbar'));
   }
 
+  /**
+   * @return {!ViewerZoomToolbarElement}
+   * @private
+   */
+  getZoomToolbar_() {
+    return /** @type {!ViewerZoomToolbarElement} */ (this.$$('#zoom-toolbar'));
+  }
+
   /** @override */
   getBackgroundColor() {
     return BACKGROUND_COLOR;
@@ -290,9 +293,10 @@
       }
     });
 
-    this.toolbarManager_ = new ToolbarManager(
-        window, this.pdfViewerUpdateEnabled_ ? null : this.getToolbar_(),
-        this.getZoomToolbar());
+    if (!this.pdfViewerUpdateEnabled_) {
+      this.toolbarManager_ = new ToolbarManager(
+          window, this.getToolbar_(), this.getZoomToolbar_());
+    }
 
     // Setup the keyboard event listener.
     document.addEventListener(
@@ -312,20 +316,14 @@
   }
 
   /**
-   * Handle key events. These may come from the user directly or via the
-   * scripting API.
+   * Helper for handleKeyEvent_ dealing with events that control toolbars.
    * @param {!KeyboardEvent} e the event to handle.
    * @private
    */
-  handleKeyEvent_(e) {
-    if (shouldIgnoreKeyEvents(document.activeElement) || e.defaultPrevented) {
-      return;
-    }
-
-    this.toolbarManager_.hideToolbarsAfterTimeout();
-
-    // Let the viewport handle directional key events.
-    if (this.viewport.handleDirectionalKeyEvent(e, this.isFormFieldFocused_)) {
+  handleToolbarKeyEvent_(e) {
+    if (this.pdfViewerUpdateEnabled_) {
+      // TODO: Add handling for any relevant hotkeys for the new unified
+      // toolbar.
       return;
     }
 
@@ -336,6 +334,46 @@
       case 'Escape':
         this.toolbarManager_.hideSingleToolbarLayer();
         return;
+      case 'g':
+        if (this.toolbarEnabled_ && (e.ctrlKey || e.metaKey) && e.altKey) {
+          this.toolbarManager_.showToolbars();
+          this.getToolbar_().selectPageNumber();
+        }
+        return;
+      case '\\':
+        if (e.ctrlKey) {
+          this.getZoomToolbar_().fitToggleFromHotKey();
+        }
+        return;
+    }
+
+    // Show toolbars as a fallback.
+    if (!(e.shiftKey || e.ctrlKey || e.altKey)) {
+      this.toolbarManager_.showToolbars();
+    }
+  }
+
+  /**
+   * Handle key events. These may come from the user directly or via the
+   * scripting API.
+   * @param {!KeyboardEvent} e the event to handle.
+   * @private
+   */
+  handleKeyEvent_(e) {
+    if (shouldIgnoreKeyEvents(document.activeElement) || e.defaultPrevented) {
+      return;
+    }
+
+    if (!this.pdfViewerUpdateEnabled_) {
+      this.toolbarManager_.hideToolbarsAfterTimeout();
+    }
+
+    // Let the viewport handle directional key events.
+    if (this.viewport.handleDirectionalKeyEvent(e, this.isFormFieldFocused_)) {
+      return;
+    }
+
+    switch (e.key) {
       case 'a':
         if (e.ctrlKey || e.metaKey) {
           this.pluginController.selectAll();
@@ -343,22 +381,11 @@
           e.preventDefault();
         }
         return;
-      case 'g':
-        if (this.toolbarEnabled_ && (e.ctrlKey || e.metaKey) && e.altKey) {
-          this.toolbarManager_.showToolbars();
-          this.getToolbar_().selectPageNumber();
-        }
-        return;
       case '[':
         if (e.ctrlKey) {
           this.rotateCounterclockwise();
         }
         return;
-      case '\\':
-        if (e.ctrlKey) {
-          this.getZoomToolbar().fitToggleFromHotKey();
-        }
-        return;
       case ']':
         if (e.ctrlKey) {
           this.rotateClockwise();
@@ -366,10 +393,8 @@
         return;
     }
 
-    // Show toolbars as a fallback.
-    if (!(e.shiftKey || e.ctrlKey || e.altKey)) {
-      this.toolbarManager_.showToolbars();
-    }
+    // Handle toolbar related key events.
+    this.handleToolbarKeyEvent_(e);
   }
 
   /**
@@ -452,6 +477,10 @@
   onFitToChanged(e) {
     super.onFitToChanged(e);
 
+    if (this.pdfViewerUpdateEnabled_) {
+      return;
+    }
+
     if (e.detail.fittingType === FittingType.FIT_TO_PAGE ||
         e.detail.fittingType === FittingType.FIT_TO_HEIGHT) {
       this.toolbarManager_.forceHideTopToolbar();
@@ -467,7 +496,9 @@
   onTwoUpViewChanged_(e) {
     this.currentController.setTwoUpView(
         e.detail === TwoUpViewAction.TWO_UP_VIEW_ENABLE);
-    this.toolbarManager_.forceHideTopToolbar();
+    if (!this.pdfViewerUpdateEnabled_) {
+      this.toolbarManager_.forceHideTopToolbar();
+    }
     this.getToolbar_().annotationAvailable =
         (e.detail !== TwoUpViewAction.TWO_UP_VIEW_ENABLE);
 
@@ -514,7 +545,7 @@
       this.getToolbar_().loadProgress = progress;
     }
     super.updateProgress(progress);
-    if (progress === 100) {
+    if (progress === 100 && !this.pdfViewerUpdateEnabled_) {
       this.toolbarManager_.hideToolbarsAfterTimeout();
     }
   }
@@ -538,23 +569,25 @@
     const horizontalScrollbarWidth =
         hasScrollbars.horizontal ? scrollbarWidth : 0;
 
-    // Shift the zoom toolbar to the left by half a scrollbar width. This
-    // gives a compromise: if there is no scrollbar visible then the toolbar
-    // will be half a scrollbar width further left than the spec but if there
-    // is a scrollbar visible it will be half a scrollbar width further right
-    // than the spec. In RTL layout normally, the zoom toolbar is on the left
-    // left side, but the scrollbar is still on the right, so this is not
-    // necessary.
-    const zoomToolbar = this.getZoomToolbar();
-    if (!isRTL()) {
-      zoomToolbar.style.right =
-          -verticalScrollbarWidth + (scrollbarWidth / 2) + 'px';
+    if (!this.pdfViewerUpdateEnabled_) {
+      // Shift the zoom toolbar to the left by half a scrollbar width. This
+      // gives a compromise: if there is no scrollbar visible then the toolbar
+      // will be half a scrollbar width further left than the spec but if there
+      // is a scrollbar visible it will be half a scrollbar width further right
+      // than the spec. In RTL layout normally, the zoom toolbar is on the left
+      // left side, but the scrollbar is still on the right, so this is not
+      // necessary.
+      const zoomToolbar = this.getZoomToolbar_();
+      if (!isRTL()) {
+        zoomToolbar.style.right =
+            -verticalScrollbarWidth + (scrollbarWidth / 2) + 'px';
+      }
+      // Having a horizontal scrollbar is much rarer so we don't offset the
+      // toolbar from the bottom any more than what the spec says. This means
+      // that when there is a scrollbar visible, it will be a full scrollbar
+      // width closer to the bottom of the screen than usual, but this is ok.
+      zoomToolbar.style.bottom = -horizontalScrollbarWidth + 'px';
     }
-    // Having a horizontal scrollbar is much rarer so we don't offset the
-    // toolbar from the bottom any more than what the spec says. This means
-    // that when there is a scrollbar visible, it will be a full scrollbar
-    // width closer to the bottom of the screen than usual, but this is ok.
-    zoomToolbar.style.bottom = -horizontalScrollbarWidth + 'px';
 
     // Update the page indicator.
     const visiblePage = this.viewport.getMostVisiblePage();
@@ -645,6 +678,14 @@
   }
 
   /** @override */
+  forceFit(view) {
+    if (!this.pdfViewerUpdateEnabled_) {
+      this.getZoomToolbar_().forceFit(view);
+    }
+    // TODO: Add handling for the case where the new toolbar is enabled.
+  }
+
+  /** @override */
   setDocumentDimensions(documentDimensions) {
     super.setDocumentDimensions(documentDimensions);
     // If we received the document dimensions, the password was good so we
diff --git a/chrome/browser/resources/pdf/pdf_viewer_base.js b/chrome/browser/resources/pdf/pdf_viewer_base.js
index 54e99aa..5dc7f71 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_base.js
+++ b/chrome/browser/resources/pdf/pdf_viewer_base.js
@@ -134,18 +134,18 @@
   getSizer() {}
 
   /**
-   * @return {!ViewerZoomToolbarElement}
-   * @protected
-   */
-  getZoomToolbar() {}
-
-  /**
    * @return {!ViewerErrorScreenElement}
    * @protected
    */
   getErrorScreen() {}
 
   /**
+   * @param {!FittingType} view
+   * @protected
+   */
+  forceFit(view) {}
+
+  /**
    * @param {string} query
    * @return {?Element}
    * @protected
@@ -479,10 +479,6 @@
     this.viewport_.setZoomFactorRange(presetZoomFactors);
 
     this.strings = strings;
-
-    // Display the zoom toolbar after the UI text direction is set, to ensure it
-    // appears on the correct side of the PDF viewer.
-    this.getZoomToolbar().hidden = false;
   }
 
   /**
@@ -506,7 +502,7 @@
 
     if (params.view) {
       this.isUserInitiatedEvent = false;
-      this.getZoomToolbar().forceFit(params.view);
+      this.forceFit(params.view);
       if (params.viewPosition) {
         const zoomedPositionShift =
             params.viewPosition * this.viewport_.getZoom();
diff --git a/chrome/browser/resources/pdf/pdf_viewer_pp.html b/chrome/browser/resources/pdf/pdf_viewer_pp.html
index dac20fda0..339c414 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_pp.html
+++ b/chrome/browser/resources/pdf/pdf_viewer_pp.html
@@ -8,10 +8,9 @@
 
 <div id="sizer"></div>
 
-<viewer-zoom-toolbar id="zoom-toolbar" strings="[[strings]]"
+<viewer-zoom-toolbar id="zoom-toolbar"
     on-fit-to-changed="onFitToChanged" is-print-preview
-    on-zoom-in="onZoomIn" on-zoom-out="onZoomOut"
-    hidden>
+    on-zoom-in="onZoomIn" on-zoom-out="onZoomOut">
 </viewer-zoom-toolbar>
 
 <viewer-error-screen id="error-screen"></viewer-error-screen>
diff --git a/chrome/browser/resources/pdf/pdf_viewer_pp.js b/chrome/browser/resources/pdf/pdf_viewer_pp.js
index 012d244..c3f4b46 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_pp.js
+++ b/chrome/browser/resources/pdf/pdf_viewer_pp.js
@@ -56,11 +56,6 @@
   }
 
   /** @override */
-  getZoomToolbar() {
-    return /** @type {!ViewerZoomToolbarElement} */ (this.$$('#zoom-toolbar'));
-  }
-
-  /** @override */
   getErrorScreen() {
     return /** @type {!ViewerErrorScreenElement} */ (this.$$('#error-screen'));
   }
@@ -70,12 +65,20 @@
     return PRINT_PREVIEW_BACKGROUND_COLOR;
   }
 
+  /**
+   * @return {!ViewerZoomToolbarElement}
+   * @private
+   */
+  getZoomToolbar_() {
+    return /** @type {!ViewerZoomToolbarElement} */ (this.$$('#zoom-toolbar'));
+  }
+
   /** @param {!BrowserApi} browserApi */
   init(browserApi) {
     super.init(browserApi);
 
     this.toolbarManager_ =
-        new ToolbarManager(window, null, this.getZoomToolbar());
+        new ToolbarManager(window, null, this.getZoomToolbar_());
 
     // Setup the keyboard event listener.
     document.addEventListener(
@@ -120,7 +123,7 @@
         return;
       case '\\':
         if (e.ctrlKey) {
-          this.getZoomToolbar().fitToggleFromHotKey();
+          this.getZoomToolbar_().fitToggleFromHotKey();
         }
         return;
       case ']':
@@ -165,7 +168,7 @@
     // than the spec. In LTR layout, the zoom toolbar is on the left
     // left side, but the scrollbar is still on the right, so this is not
     // necessary.
-    const zoomToolbar = this.getZoomToolbar();
+    const zoomToolbar = this.getZoomToolbar_();
     if (isRTL()) {
       zoomToolbar.style.right =
           -verticalScrollbarWidth + (scrollbarWidth / 2) + 'px';
@@ -235,7 +238,7 @@
         if (!this.inPrintPreviewMode_) {
           this.inPrintPreviewMode_ = true;
           this.isUserInitiatedEvent = false;
-          this.getZoomToolbar().forceFit(FittingType.FIT_TO_PAGE);
+          this.forceFit(FittingType.FIT_TO_PAGE);
           this.isUserInitiatedEvent = true;
         }
 
@@ -338,9 +341,13 @@
   }
 
   /** @override */
+  forceFit(view) {
+    this.getZoomToolbar_().forceFit(view);
+  }
+
+  /** @override */
   handleStrings(strings) {
     super.handleStrings(strings);
-
     if (!strings) {
       return;
     }
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index f6246c44..9a49bb22 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -52,6 +52,7 @@
       "chrome://resources/mojo/mojo/public/mojom/base/time.mojom.html",
       "chrome://resources/mojo/skia/public/mojom/bitmap.mojom-lite.js",
       "chrome://resources/mojo/skia/public/mojom/image_info.mojom-lite.js",
+      "chrome://resources/mojo/url/mojom/url.mojom-lite.js",
     ]
 
     deps = [ ":unpak" ]
@@ -71,6 +72,7 @@
       "chrome://resources/js/cr.m.js",
       "chrome://resources/css/cros_colors.generated.css",
       "chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js",
+      "chrome://resources/mojo/url/mojom/url.mojom-lite.js",
       "app-management/app_management.mojom-lite.js",
       "app-management/bitmap.mojom-lite.js",
       "app-management/file_path.mojom-lite.js",
@@ -90,6 +92,7 @@
     "../../ui/webui/settings/chromeos/constants/setting.mojom-lite.js",
     "../../../../mojo/public/mojom/base/file_path.mojom-lite.js",
     "../../../../ui/gfx/image/mojom/image.mojom-lite.js",
+    "../../../../url/mojom/url.mojom-lite.js",
     "../../ui/webui/app_management/app_management.mojom-lite.js",
     "../../../../components/services/app_service/public/mojom/types.mojom-lite.js",
     "../../ui/webui/settings/chromeos/search/search.mojom-lite.js",
@@ -267,12 +270,15 @@
   is_polymer3 = true
   deps = [
     ":metrics_recorder.m",
+
     #  ":os_icons.m",
     #  ":os_page_visibility.m",
     ":os_route.m",
+
     #  ":os_settings.m",
     #  ":os_settings_icons_css.m",
     ":os_settings_routes.m",
+
     #  ":route_origin_behavior.m",
     #  ":search_handler.m",
   ]
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.html b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.html
index a53c41de..0faa13c 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.html
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.html
@@ -47,21 +47,19 @@
         margin-inline-end: 10px;
       }
 
-      cr-toggle {
-        margin-inline-start: var(--settings-control-label-spacing);
-      }
-
       network-config-toggle {
         display: flex;
         padding: 0 var(--cr-section-padding);
       }
 
-      cr-policy-network-indicator-mojo,
-      cr-policy-indicator,
-      cr-policy-pref-indicator {
+      cr-policy-indicator {
         margin-inline-start: var(--settings-controlled-by-spacing);
       }
 
+      cr-policy-network-indicator-mojo {
+        margin: 0 var(--settings-controlled-by-spacing);
+      }
+
       #networkState[connected] {
         color: var(--google-green-500);
       }
@@ -242,7 +240,7 @@
       </template>
       <!-- Metered (WiFi and Cellular only). -->
       <template is="dom-if" if="[[showMetered_(managedProperties_)]]">
-        <network-config-toggle id="meteredToggle" class="hr"
+        <network-config-toggle id="meteredToggle" class="hr" policy-on-left
             label="$i18n{networkMetered}"
             checked="{{meteredOverride_}}"
             property="[[managedProperties_.metered]]"
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.html
index 9b1f6c4..b1285f7 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/browser_proxy.html
@@ -5,6 +5,7 @@
 
 <script src="chrome://resources/mojo/skia/public/mojom/image_info.mojom-lite.js"></script>
 <script src="chrome://resources/mojo/skia/public/mojom/bitmap.mojom-lite.js"></script>
+<script src="chrome://resources/mojo/url/mojom/url.mojom-lite.js"></script>
 <script src="chrome://os-settings/app-management/file_path.mojom-lite.js"></script>
 <script src="chrome://os-settings/app-management/image.mojom-lite.js"></script>
 <script src="chrome://os-settings/app-management/types.mojom-lite.js"></script>
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 198557b..b496834 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
@@ -113,6 +113,11 @@
     return app_management.util.getPermissionValueBool(app, permissionType);
   },
 
+  resetToggle() {
+    const currentValue = this.getValue_(this.app_, this.permissionType);
+    this.$$('#toggle-row').setToggle(currentValue);
+  },
+
   /**
    * @private
    */
@@ -125,6 +130,14 @@
    */
   togglePermission_() {
     assert(this.app_);
+    // Plugin VM handles microphone and camera permissions manually.
+    // TODO(crbug:1071872): remove in m86 when plugin_vm permissions are
+    // updated.
+    if (this.app_.type == AppType.kPluginVm &&
+        (this.permissionType == 'MICROPHONE' ||
+         this.permissionType == 'CAMERA')) {
+      return;
+    }
 
     /** @type {!Permission} */
     let newPermission;
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn
index 3c63e8b..a3971b9f 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/BUILD.gn
@@ -18,9 +18,13 @@
 
 js_library("plugin_vm_detail_view") {
   deps = [
+    ":plugin_vm_browser_proxy",
+    ":plugin_vm_permission_dialog",
     "../:constants",
+    "../:permission_item",
     "../:store_client",
     "../:util",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
 }
 
@@ -31,6 +35,10 @@
   ]
 }
 
+js_library("plugin_vm_permission_dialog") {
+  deps = [ ":plugin_vm_browser_proxy" ]
+}
+
 # TODO: Uncomment as the Polymer3 migration makes progress.
 #js_type_check("closure_compile_module") {
 #  is_polymer3 = true
@@ -71,6 +79,7 @@
   public_deps = [
     ":modulize",
     ":plugin_vm_detail_view_module",
+    ":plugin_vm_permission_dialog_module",
     ":plugin_vm_shared_paths_module",
   ]
 }
@@ -81,6 +90,12 @@
   html_type = "dom-module"
 }
 
+polymer_modulizer("plugin_vm_permission_dialog") {
+  js_file = "plugin_vm_permission_dialog.js"
+  html_file = "plugin_vm_permission_dialog.html"
+  html_type = "dom-module"
+}
+
 polymer_modulizer("plugin_vm_shared_paths") {
   js_file = "plugin_vm_shared_paths.js"
   html_file = "plugin_vm_shared_paths.html"
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.js
index bfd60c1..9bb110250 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.js
@@ -3,6 +3,22 @@
 // found in the LICENSE file.
 
 /**
+ * These values should remain consistent with their C++ counterpart
+ * (chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h).
+ * @enum {number}
+ */
+const PermissionType = {
+  CAMERA: 0,
+  MICROPHONE: 1,
+};
+
+/**
+ * @typedef {{permissionType: !PermissionType,
+ *            proposedValue: boolean}}
+ */
+let PermissionSetting;
+
+/**
  * @fileoverview A helper object used by the Plugin VM section
  * to manage the Plugin VM.
  */
@@ -20,6 +36,20 @@
      * @param {string} path Path to stop sharing.
      */
     removePluginVmSharedPath(vmName, path) {}
+
+    /**
+     * @param {!PermissionSetting} permissionSetting The proposed change to
+     *     permissions
+     * @return {!Promise<boolean>} Whether Plugin VM needs to be relaunched for
+     *     permissions to take effect.
+     */
+    wouldPermissionChangeRequireRelaunch(permissionSetting) {}
+
+    /**
+     * @param {!PermissionSetting} permissionSetting The change to make to the
+     *     permissions
+     */
+    setPluginVmPermission(permissionSetting) {}
   }
 
   /** @implements {settings.PluginVmBrowserProxy} */
@@ -33,6 +63,20 @@
     removePluginVmSharedPath(vmName, path) {
       chrome.send('removePluginVmSharedPath', [vmName, path]);
     }
+
+    /** @override */
+    wouldPermissionChangeRequireRelaunch(permissionSetting) {
+      return cr.sendWithPromise(
+          'wouldPermissionChangeRequireRelaunch',
+          permissionSetting.permissionType, permissionSetting.proposedValue);
+    }
+
+    /** @override */
+    setPluginVmPermission(permissionSetting) {
+      chrome.send(
+          'setPluginVmPermission',
+          [permissionSetting.permissionType, permissionSetting.proposedValue]);
+    }
   }
 
   // The singleton instance_ can be replaced with a test version of this wrapper
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html
index 1ff2270..9548e1cd 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html
@@ -8,6 +8,7 @@
 <link rel="import" href="../store_client.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="plugin_vm_permission_dialog.html">
 
 <dom-module id="app-management-plugin-vm-detail-view">
   <template>
@@ -27,16 +28,20 @@
         <div class="permission-list indented-permission-block">
           <template is="dom-if" if="[[showCameraPermissions_]]">
             <app-management-permission-item
+                id="camera-permission"
                 class="subpermission-row" icon="app-management:camera"
                 permission-label="$i18n{appManagementCameraPermissionLabel}"
-                permission-type="CAMERA">
+                permission-type="CAMERA"
+                on-change="onPermissionChanged_">
             </app-management-permission-item>
           </template>
           <template is="dom-if" if="[[showMicrophonePermissions_]]">
             <app-management-permission-item
+                id="microphone-permission"
                 class="subpermission-row" icon="app-management:microphone"
                 permission-label="$i18n{appManagementMicrophonePermissionLabel}"
-                permission-type="MICROPHONE">
+                permission-type="MICROPHONE"
+                on-change="onPermissionChanged_">
             </app-management-permission-item>
           </template>
           <app-management-permission-item
@@ -59,6 +64,14 @@
         </div>
       </div>
     </div>
+
+    <template is="dom-if" if="[[showPluginVmPermissionDialog_]]" restamp>
+      <app-management-plugin-vm-permission-dialog
+          on-close="onPluginVmPermissionDialogClose_"
+          pending-permission-change="[[pendingPermissionChange_]]"
+          reset-permission-change="{{resetPermissionChange}}">
+      </app-management-plugin-vm-permission-dialog>
+    </template>
   </template>
   <script src="plugin_vm_detail_view.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js
index b2fe619..b16e1db9 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js
@@ -7,6 +7,7 @@
 
   behaviors: [
     app_management.StoreClient,
+    WebUIListenerBehavior,
   ],
 
   properties: {
@@ -36,6 +37,31 @@
         return loadTimeData.getBoolean('showPluginVmMicrophonePermissions');
       },
     },
+
+    /** @private {boolean} */
+    showPluginVmPermissionDialog_: {
+      type: Boolean,
+      value: false,
+    },
+
+    /**
+     * The current permssion type that is being changed
+     * and its proposed value.
+     * @private {?PermissionSetting}
+     */
+    pendingPermissionChange_: {
+      type: Object,
+      value: null,
+    },
+
+    /**
+     * If the last permission change should be reset.
+     * {boolean}
+     */
+    resetPermissionChange: {
+      type: Boolean,
+      value: false,
+    },
   },
 
   attached() {
@@ -45,11 +71,68 @@
     this.updateFromStore();
   },
 
-  /**
-   * @private
-   */
+  /** @private */
   onSharedPathsClick_() {
     settings.Router.getInstance().navigateTo(
         settings.routes.APP_MANAGEMENT_PLUGIN_VM_SHARED_PATHS);
   },
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onPermissionChanged_: function(e) {
+    const permissionTypeString =
+        /** @type {{permissionType:string}} */ (e.target).permissionType;
+    let permissionType;
+    switch (permissionTypeString) {
+      case 'CAMERA':
+        permissionType = PermissionType.CAMERA;
+        break;
+      case 'MICROPHONE':
+        permissionType = PermissionType.MICROPHONE;
+        break;
+      default:
+        assertNotReached();
+    }
+    const permissionSetting = /** @type {!PermissionSetting} */ ({
+      permissionType: permissionType,
+      proposedValue: !app_management.util.getPermissionValueBool(
+          this.app_, permissionTypeString)
+    });
+    settings.PluginVmBrowserProxyImpl.getInstance()
+        .wouldPermissionChangeRequireRelaunch(permissionSetting)
+        .then(requiresRestart => {
+          if (requiresRestart) {
+            this.pendingPermissionChange_ = permissionSetting;
+            this.showPluginVmPermissionDialog_ = true;
+          } else {
+            settings.PluginVmBrowserProxyImpl.getInstance()
+                .setPluginVmPermission(permissionSetting);
+          }
+        });
+  },
+
+  /** @private */
+  onPluginVmPermissionDialogClose_: function() {
+    if (this.resetPermissionChange) {
+      switch (this.pendingPermissionChange_.permissionType) {
+        case PermissionType.CAMERA:
+          /* @type {!AppManagementPermissionItem} */ (
+              this.$$('#camera-permission'))
+              .resetToggle();
+          break;
+        case PermissionType.MICROPHONE:
+          /* @type @{!AppManagementPermissionItem} */ (
+              this.$$('#microphone-permission'))
+              .resetToggle();
+          break;
+        default:
+          assertNotReached();
+      }
+    }
+    this.resetPermissionChange = false;
+    this.showPluginVmPermissionDialog_ = false;
+    this.pendingPermissionChange_ = null;
+  },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_permission_dialog.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_permission_dialog.html
new file mode 100644
index 0000000..6cd595b8
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_permission_dialog.html
@@ -0,0 +1,27 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="../browser_proxy.html">
+<link rel="import" href="../../../../settings_shared_css.html">
+
+<dom-module id="app-management-plugin-vm-permission-dialog">
+  <template>
+    <style include="settings-shared"></style>
+    <cr-dialog id="dialog" close-text="$i18n{close}">
+      <div id="dialog-text" slot="body">[[dialogText_]]</div>
+      <div slot="button-container">
+        <cr-button id="dialogCancelButton" class="cancel-button"
+            on-click="onCancelTap_">
+            $i18n{cancel}
+        </cr-button>
+        <cr-button id="dialogOkButton" class="action-button"
+            on-click="onOkTap_">
+            $i18n{ok}
+        </cr-button>
+      </div>
+    </cr-dialog>
+  </template>
+  <script src="plugin_vm_permission_dialog.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_permission_dialog.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_permission_dialog.js
new file mode 100644
index 0000000..3e2f96e2
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_permission_dialog.js
@@ -0,0 +1,72 @@
+// 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.
+
+/**
+ * @fileoverview 'app-management-plugin-vm-permission-dialog' is a component
+ * that alerts the user when Plugin Vm needs to be relaunched in order for
+ * changes to the permission settings to take effect.
+ */
+Polymer({
+  is: 'app-management-plugin-vm-permission-dialog',
+
+  properties: {
+    /**
+     * An attribute indicating the type of permission that is being
+     * changed and what value to change it to.
+     * {!PermissionSetting}
+     */
+    pendingPermissionChange: Object,
+
+    /**
+     * If the last permission change should be reset.
+     * {boolean}
+     */
+    resetPermissionChange: {
+      type: Boolean,
+      notify: true,
+    },
+
+    /** @private {string} */
+    dialogText_: {
+      type: String,
+      computed: 'getDialogText_()',
+    },
+
+  },
+  /** @override */
+  attached() {
+    this.$.dialog.showModal();
+  },
+
+  /** @private */
+  onCancelTap_() {
+    this.resetPermissionChange = true;
+    this.$.dialog.close();
+  },
+
+  /** @private */
+  onOkTap_() {
+    // Reinitializing PermissionSetting Object to keep the closure compiler
+    // happy.
+    settings.PluginVmBrowserProxyImpl.getInstance().setPluginVmPermission({
+      permissionType: this.pendingPermissionChange.permissionType,
+      proposedValue: this.pendingPermissionChange.proposedValue
+    });
+    // TODO(1071872): Restart Plugin Vm.
+    this.$.dialog.close();
+  },
+
+  /** @private */
+  getDialogText_() {
+    switch (this.pendingPermissionChange.permissionType) {
+      case PermissionType.CAMERA:
+        return loadTimeData.getString('pluginVmPermissionDialogCameraLabel');
+      case PermissionType.MICROPHONE:
+        return loadTimeData.getString(
+            'pluginVmPermissionDialogMicrophoneLabel');
+      default:
+        assertNotReached();
+    }
+  },
+});
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.js
index 5a8a15d6..e832283 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/toggle_row.js
@@ -35,6 +35,13 @@
   },
 
   /**
+   * @param {boolean} value What to set the toggle to.
+   */
+  setToggle(value) {
+    this.$.toggle.checked = value;
+  },
+
+  /**
    * @param {MouseEvent} event
    * @private
    */
diff --git a/chrome/browser/resources/settings/icons.html b/chrome/browser/resources/settings/icons.html
index 495c68b..5074dd4 100644
--- a/chrome/browser/resources/settings/icons.html
+++ b/chrome/browser/resources/settings/icons.html
@@ -115,7 +115,6 @@
       <g id="save-original"><path d="M11 17H6V4h5v4h4v2h2V7l-5-5H6c-1.1 0-2 .9-2 2v13c0 1.1.9 2 2 2h5v-2zm9 5h-8v-2h8v2zm0-7l-4 4-4-4h3v-3h2v3h3z"></path></g>
       <g id="sensors"><path d="M10 8.5c-0.8 0-1.5 0.7-1.5 1.5s0.7 1.5 1.5 1.5s1.5-0.7 1.5-1.5S10.8 8.5 10 8.5z M7.6 5.8 C6.2 6.7 5.2 8.2 5.2 10c0 1.8 1 3.4 2.4 4.2l0.8-1.4c-1-0.6-1.6-1.6-1.6-2.8c0-1.2 0.6-2.2 1.6-2.8L7.6 5.8z M14.8 10 c0-1.8-1-3.4-2.4-4.2l-0.8 1.4c0.9 0.6 1.6 1.6 1.6 2.8c0 1.2-0.6 2.2-1.6 2.8l0.8 1.4C13.8 13.4 14.8 11.8 14.8 10z M6 3 c-2.4 1.4-4 4-4 7c0 3 1.6 5.6 4 7l0.8-1.4c-1.9-1.1-3.2-3.2-3.2-5.6c0-2.4 1.3-4.5 3.2-5.6L6 3z M13.2 4.4 c1.9 1.1 3.2 3.2 3.2 5.6c0 2.4-1.3 4.5-3.2 5.6L14 17c2.4-1.4 4-4 4-7c0-3-1.6-5.6-4-7L13.2 4.4z"></path></g>
       <g id="serial-port"><path d="M22 9V7h-2V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-2h2v-2h-2v-2h2v-2h-2V9h2zm-4 10H4V5h14v14zM6 13h5v4H6zm6-6h4v3h-4zM6 7h5v5H6zm6 4h4v6h-4z"></path></g>
-      <g id="supervisor-account"><path d="M16.5 12c1.38 0 2.49-1.12 2.49-2.5S17.88 7 16.5 7C15.12 7 14 8.12 14 9.5s1.12 2.5 2.5 2.5zM9 11c1.66 0 2.99-1.34 2.99-3S10.66 5 9 5C7.34 5 6 6.34 6 8s1.34 3 3 3zm7.5 3c-1.83 0-5.5.92-5.5 2.75V19h11v-2.25c0-1.83-3.67-2.75-5.5-2.75zM9 13c-2.33 0-7 1.17-7 3.5V19h7v-2.25c0-.85.33-2.34 2.37-3.47C10.5 13.1 9.66 13 9 13z"></path></g>
       <g id="sync-disabled"><path d="M10 6.35V4.26c-.8.21-1.55.54-2.23.96l1.46 1.46c.25-.12.5-.24.77-.33zm-7.14-.94l2.36 2.36C4.45 8.99 4 10.44 4 12c0 2.21.91 4.2 2.36 5.64L4 20h6v-6l-2.24 2.24C6.68 15.15 6 13.66 6 12c0-1 .25-1.94.68-2.77l8.08 8.08c-.25.13-.5.25-.77.34v2.09c.8-.21 1.55-.54 2.23-.96l2.36 2.36 1.27-1.27L4.14 4.14 2.86 5.41zM20 4h-6v6l2.24-2.24C17.32 8.85 18 10.34 18 12c0 1-.25 1.94-.68 2.77l1.46 1.46C19.55 15.01 20 13.56 20 12c0-2.21-.91-4.2-2.36-5.64L20 4z"></path></g>
       <g id="sync-problem"><path d="M3 12c0 2.21.91 4.2 2.36 5.64L3 20h6v-6l-2.24 2.24C5.68 15.15 5 13.66 5 12c0-2.61 1.67-4.83 4-5.65V4.26C5.55 5.15 3 8.27 3 12zm8 5h2v-2h-2v2zM21 4h-6v6l2.24-2.24C18.32 8.85 19 10.34 19 12c0 2.61-1.67 4.83-4 5.65v2.09c3.45-.89 6-4.01 6-7.74 0-2.21-.91-4.2-2.36-5.64L21 4zm-10 9h2V7h-2v6z"></path></g>
       <g id="usb"><path d="M15 7v4h1v2h-3V5h2l-3-4-3 4h2v8H8v-2.07c.7-.37 1.2-1.08 1.2-1.93 0-1.21-.99-2.2-2.2-2.2-1.21 0-2.2.99-2.2 2.2 0 .85.5 1.56 1.2 1.93V13c0 1.11.89 2 2 2h3v3.05c-.71.37-1.2 1.1-1.2 1.95 0 1.22.99 2.2 2.2 2.2 1.21 0 2.2-.98 2.2-2.2 0-.85-.49-1.58-1.2-1.95V15h3c1.11 0 2-.89 2-2v-2h1V7h-4z"></path></g>
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 03eb56a..3c93fcf 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -240,6 +240,12 @@
       <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PLUGIN_VM_BROWSER_PROXY_HTML"
                 file="chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_browser_proxy.html"
                  compress="false" type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PLUGIN_VM_PERMISSION_DIALOG_JS"
+                file="chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_permission_dialog.js"
+                 compress="false" type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_APP_MANAGEMENT_PLUGIN_VM_PERMISSION_DIALOG_HTML"
+                file="chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_permission_dialog.html"
+                 compress="false" type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_CAPTIONS_SUBPAGE_JS"
                  file="a11y_page/captions_subpage.js"
                  compress="false" type="chrome_html" />
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialog.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialog.java
index cd694d3..f8bd86a 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialog.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialog.java
@@ -27,7 +27,6 @@
  */
 public class QrCodeDialog extends DialogFragment {
     private ArrayList<QrCodeDialogTab> mTabs;
-    private Context mContext;
     private TabLayoutPageListener mTabLayoutPageListener;
 
     /**
@@ -38,20 +37,8 @@
     }
 
     @Override
-    public void onAttach(Context context) {
-        super.onAttach(context);
-        mContext = context;
-
-        QrCodeShareCoordinator shareCoordinator =
-                new QrCodeShareCoordinator(context, this::dismiss);
-        QrCodeScanCoordinator scanCoordinator = new QrCodeScanCoordinator(context, this::dismiss);
-
-        mTabs.add(shareCoordinator);
-        mTabs.add(scanCoordinator);
-    }
-
-    @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
+        initTabs();
         AlertDialog.Builder builder =
                 new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_Fullscreen);
         builder.setView(getDialogView());
@@ -72,7 +59,6 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
-        mContext = null;
         for (QrCodeDialogTab tab : mTabs) {
             tab.onDestroy();
         }
@@ -104,4 +90,15 @@
         tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager));
         return dialogView;
     }
+
+    private void initTabs() {
+        Context context = getActivity();
+
+        QrCodeShareCoordinator shareCoordinator =
+                new QrCodeShareCoordinator(context, this::dismiss);
+        QrCodeScanCoordinator scanCoordinator = new QrCodeScanCoordinator(context, this::dismiss);
+
+        mTabs.add(shareCoordinator);
+        mTabs.add(scanCoordinator);
+    }
 }
diff --git a/chrome/browser/themes/theme_helper_win.cc b/chrome/browser/themes/theme_helper_win.cc
index 6aa2b3dea..aabae0b 100644
--- a/chrome/browser/themes/theme_helper_win.cc
+++ b/chrome/browser/themes/theme_helper_win.cc
@@ -12,7 +12,6 @@
 #include "skia/ext/skia_utils_win.h"
 #include "ui/base/win/shell.h"
 #include "ui/gfx/color_utils.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/views_features.h"
 
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc b/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc
index 2c8c375..cd5fafe 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
+#include "chrome/common/chrome_features.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_update.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
@@ -132,14 +133,18 @@
     return;
   }
 
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
+
   // When Crostini generates shelf id as the app_id, which couldn't match to an
   // app, the default penguin icon should be loaded.
   if (crostini::IsUnmatchedCrostiniShelfAppId(app_id)) {
     apps::mojom::IconKeyPtr icon_key = apps::mojom::IconKey::New();
     proxy->LoadIconFromIconKey(
         apps::mojom::AppType::kCrostini, std::string(), std::move(icon_key),
-        apps::mojom::IconCompression::kUncompressed, icon_size_in_dip(),
-        allow_placeholder_icon,
+        icon_type, icon_size_in_dip(), allow_placeholder_icon,
         base::BindOnce(&AppServiceAppIconLoader::OnLoadIcon,
                        weak_ptr_factory_.GetWeakPtr(), app_id));
     return;
@@ -150,16 +155,19 @@
     return;
   }
 
-  proxy->LoadIcon(app_type, app_id, apps::mojom::IconCompression::kUncompressed,
-                  icon_size_in_dip(), allow_placeholder_icon,
+  proxy->LoadIcon(app_type, app_id, icon_type, icon_size_in_dip(),
+                  allow_placeholder_icon,
                   base::BindOnce(&AppServiceAppIconLoader::OnLoadIcon,
                                  weak_ptr_factory_.GetWeakPtr(), app_id));
 }
 
 void AppServiceAppIconLoader::OnLoadIcon(const std::string& app_id,
                                          apps::mojom::IconValuePtr icon_value) {
-  if (icon_value->icon_compression !=
-      apps::mojom::IconCompression::kUncompressed) {
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
+  if (icon_value->icon_type != icon_type) {
     return;
   }
 
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
index 0bd8a1db..395b701 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
@@ -204,8 +204,11 @@
   apps::AppServiceProxy* proxy =
       apps::AppServiceProxyFactory::GetForProfile(profile());
   if (proxy) {
-    proxy->LoadIcon(app_type_, id(),
-                    apps::mojom::IconCompression::kUncompressed,
+    auto icon_type =
+        (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+            ? apps::mojom::IconType::kStandard
+            : apps::mojom::IconType::kUncompressed;
+    proxy->LoadIcon(app_type_, id(), icon_type,
                     ash::AppListConfig::instance().grid_icon_dimension(),
                     allow_placeholder_icon,
                     base::BindOnce(&AppServiceAppItem::OnLoadIcon,
@@ -214,8 +217,11 @@
 }
 
 void AppServiceAppItem::OnLoadIcon(apps::mojom::IconValuePtr icon_value) {
-  if (icon_value->icon_compression !=
-      apps::mojom::IconCompression::kUncompressed) {
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
+  if (icon_value->icon_type != icon_type) {
     return;
   }
   SetIcon(icon_value->uncompressed);
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
index 691a662..173240e5 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
@@ -745,6 +745,13 @@
   void TearDown() override { ResetBuilder(); }
 
  protected:
+  // Required to ensure that the Plugin VM manager can be accessed in order to
+  // retrieve permissions.
+  struct ScopedDBusThreadManager {
+    ScopedDBusThreadManager() { chromeos::DBusThreadManager::Initialize(); }
+    ~ScopedDBusThreadManager() { chromeos::DBusThreadManager::Shutdown(); }
+  } dbus_thread_manager_;
+
   // Destroys any existing builder in the correct order.
   void ResetBuilder() {
     builder_.reset();
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index c5c7cb34..b37f38ae 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -2241,7 +2241,7 @@
 
   apps::ArcIconOnceLoader once_loader(profile());
   once_loader.LoadIcon(
-      app_id, icon_size, apps::mojom::IconCompression::kCompressed,
+      app_id, icon_size, apps::mojom::IconType::kCompressed,
       base::BindLambdaForTesting([&](ArcAppIcon* icon) {
         const std::map<ui::ScaleFactor, std::string>& compressed_images =
             icon->compressed_images();
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc
index bdf0a3f..c5dc904 100644
--- a/chrome/browser/ui/app_list/search/app_service_app_result.cc
+++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -194,8 +194,12 @@
     // If |icon_loader_releaser_| is non-null, assigning to it will signal to
     // |icon_loader_| that the previous icon is no longer being used, as a hint
     // that it could be flushed from any caches.
+    auto icon_type =
+        (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+            ? apps::mojom::IconType::kStandard
+            : apps::mojom::IconType::kUncompressed;
     icon_loader_releaser_ = icon_loader_->LoadIcon(
-        app_type_, app_id(), apps::mojom::IconCompression::kUncompressed,
+        app_type_, app_id(), icon_type,
         chip ? ash::AppListConfig::instance().suggestion_chip_icon_dimension()
              : ash::AppListConfig::instance().GetPreferredIconDimension(
                    display_type()),
@@ -207,8 +211,11 @@
 
 void AppServiceAppResult::OnLoadIcon(bool chip,
                                      apps::mojom::IconValuePtr icon_value) {
-  if (icon_value->icon_compression !=
-      apps::mojom::IconCompression::kUncompressed) {
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
+  if (icon_value->icon_type != icon_type) {
     return;
   }
 
diff --git a/chrome/browser/ui/app_list/search/os_settings_provider.cc b/chrome/browser/ui/app_list/search/os_settings_provider.cc
index e8e2584..45857683 100644
--- a/chrome/browser/ui/app_list/search/os_settings_provider.cc
+++ b/chrome/browser/ui/app_list/search/os_settings_provider.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_manager.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.h"
 #include "chrome/browser/ui/webui/settings/chromeos/search/search_handler.h"
+#include "chrome/common/chrome_features.h"
 #include "ui/gfx/image/image_skia.h"
 #include "url/gurl.h"
 
@@ -188,10 +189,13 @@
   app_service_proxy_ = apps::AppServiceProxyFactory::GetForProfile(profile_);
   if (app_service_proxy_) {
     Observe(&app_service_proxy_->AppRegistryCache());
+    auto icon_type =
+        (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+            ? apps::mojom::IconType::kStandard
+            : apps::mojom::IconType::kUncompressed;
     app_service_proxy_->LoadIcon(
         apps::mojom::AppType::kWeb,
-        chromeos::default_web_apps::kOsSettingsAppId,
-        apps::mojom::IconCompression::kUncompressed,
+        chromeos::default_web_apps::kOsSettingsAppId, icon_type,
         ash::AppListConfig::instance().search_list_icon_dimension(),
         /*allow_placeholder_icon=*/false,
         base::BindOnce(&OsSettingsProvider::OnLoadIcon,
@@ -278,10 +282,13 @@
       update.AppId() == chromeos::default_web_apps::kOsSettingsAppId &&
       update.ReadinessChanged() &&
       update.Readiness() == apps::mojom::Readiness::kReady) {
+    auto icon_type =
+        (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+            ? apps::mojom::IconType::kStandard
+            : apps::mojom::IconType::kUncompressed;
     app_service_proxy_->LoadIcon(
         apps::mojom::AppType::kWeb,
-        chromeos::default_web_apps::kOsSettingsAppId,
-        apps::mojom::IconCompression::kUncompressed,
+        chromeos::default_web_apps::kOsSettingsAppId, icon_type,
         ash::AppListConfig::instance().search_list_icon_dimension(),
         /*allow_placeholder_icon=*/false,
         base::BindOnce(&OsSettingsProvider::OnLoadIcon,
@@ -364,8 +371,11 @@
 }
 
 void OsSettingsProvider::OnLoadIcon(apps::mojom::IconValuePtr icon_value) {
-  if (icon_value->icon_compression ==
-      apps::mojom::IconCompression::kUncompressed) {
+  auto icon_type =
+      (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon))
+          ? apps::mojom::IconType::kStandard
+          : apps::mojom::IconType::kUncompressed;
+  if (icon_value->icon_type == icon_type) {
     icon_ = icon_value->uncompressed;
   }
 }
diff --git a/chrome/browser/ui/intent_picker_tab_helper.cc b/chrome/browser/ui/intent_picker_tab_helper.cc
index 762b4d6..0cfe26db 100644
--- a/chrome/browser/ui/intent_picker_tab_helper.cc
+++ b/chrome/browser/ui/intent_picker_tab_helper.cc
@@ -108,7 +108,7 @@
   }
 
   constexpr bool allow_placeholder_icon = false;
-  proxy->LoadIcon(app_type, app_id, apps::mojom::IconCompression::kUncompressed,
+  proxy->LoadIcon(app_type, app_id, apps::mojom::IconType::kUncompressed,
                   gfx::kFaviconSize, allow_placeholder_icon,
                   base::BindOnce(&IntentPickerTabHelper::OnAppIconLoaded,
                                  weak_factory_.GetWeakPtr(), std::move(apps),
diff --git a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
index 15f8243..87a174f 100644
--- a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
@@ -25,7 +25,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/background.h"
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc
index b4896a2..d50bd013e 100644
--- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc
+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/browser_window_property_manager_win.h"
 #include "chrome/browser/ui/views/frame/system_menu_insertion_delegate_win.h"
+#include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/win/app_icon.h"
 #include "chrome/browser/win/titlebar_config.h"
@@ -348,8 +349,8 @@
   } else {
     // The glass should extend to the bottom of the tabstrip.
     HWND hwnd = GetHWND();
-    gfx::Rect tabstrip_region_bounds(
-        browser_frame_->GetBoundsForTabStripRegion(browser_view_->tabstrip()));
+    gfx::Rect tabstrip_region_bounds(browser_frame_->GetBoundsForTabStripRegion(
+        browser_view_->tab_strip_region_view()->GetMinimumSize()));
     tabstrip_region_bounds =
         display::win::ScreenWin::DIPToClientRect(hwnd, tabstrip_region_bounds);
 
diff --git a/chrome/browser/ui/views/frame/browser_frame.cc b/chrome/browser/ui/views/frame/browser_frame.cc
index ad048f7..385520bb 100644
--- a/chrome/browser/ui/views/frame/browser_frame.cc
+++ b/chrome/browser/ui/views/frame/browser_frame.cc
@@ -111,11 +111,11 @@
 }
 
 gfx::Rect BrowserFrame::GetBoundsForTabStripRegion(
-    const views::View* tabstrip) const {
+    const gfx::Size& tabstrip_minimum_size) const {
   // This can be invoked before |browser_frame_view_| has been set.
-  return browser_frame_view_
-             ? browser_frame_view_->GetBoundsForTabStripRegion(tabstrip)
-             : gfx::Rect();
+  return browser_frame_view_ ? browser_frame_view_->GetBoundsForTabStripRegion(
+                                   tabstrip_minimum_size)
+                             : gfx::Rect();
 }
 
 int BrowserFrame::GetTopInset() const {
diff --git a/chrome/browser/ui/views/frame/browser_frame.h b/chrome/browser/ui/views/frame/browser_frame.h
index 76ce740..24a27201 100644
--- a/chrome/browser/ui/views/frame/browser_frame.h
+++ b/chrome/browser/ui/views/frame/browser_frame.h
@@ -64,8 +64,9 @@
   int GetMinimizeButtonOffset() const;
 
   // Retrieves the bounds in non-client view coordinates for the
-  // TabStripRegionView that contains the specified TabStrip view.
-  gfx::Rect GetBoundsForTabStripRegion(const views::View* tabstrip) const;
+  // TabStripRegionView that contains the TabStrip view.
+  gfx::Rect GetBoundsForTabStripRegion(
+      const gfx::Size& tabstrip_minimum_size) const;
 
   // Returns the inset of the topmost view in the client view from the top of
   // the non-client view. The topmost view depends on the window type. The
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index dbc93621..1cba62c 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/tabs/tab_types.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
 #include "chrome/browser/ui/views/web_apps/web_app_frame_toolbar_view.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/common/chrome_features.h"
@@ -380,7 +381,9 @@
 int BrowserNonClientFrameView::GetSystemMenuY() const {
   if (!browser_view()->IsTabStripVisible())
     return GetTopInset(false);
-  return GetBoundsForTabStripRegion(browser_view()->tabstrip()).bottom() -
+  return GetBoundsForTabStripRegion(
+             browser_view()->tab_strip_region_view()->GetMinimumSize())
+             .bottom() -
          GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP);
 }
 #endif
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
index ee3edf4..9868393 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
@@ -59,7 +59,7 @@
   // Retrieves the bounds in non-client view coordinates within which the
   // TabStrip should be laid out.
   virtual gfx::Rect GetBoundsForTabStripRegion(
-      const views::View* tabstrip) const = 0;
+      const gfx::Size& tabstrip_minimum_size) const = 0;
 
   // Returns the inset of the topmost view in the client view from the top of
   // the non-client view. The topmost view depends on the window type. The
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index e1bfb1cb..0aef8552 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -175,15 +175,12 @@
 // BrowserNonClientFrameView:
 
 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForTabStripRegion(
-    const views::View* tabstrip) const {
-  if (!tabstrip)
-    return gfx::Rect();
-
+    const gfx::Size& tabstrip_minimum_size) const {
   const int left_inset = GetTabStripLeftInset();
   const bool restored = !frame()->IsMaximized() && !frame()->IsFullscreen();
   return gfx::Rect(left_inset, GetTopInset(restored),
                    std::max(0, width() - left_inset - GetTabStripRightInset()),
-                   tabstrip->GetPreferredSize().height());
+                   tabstrip_minimum_size.height());
 }
 
 int BrowserNonClientFrameViewAsh::GetTopInset(bool restored) const {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
index 8937981..63631f6 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -52,7 +52,7 @@
 
   // BrowserNonClientFrameView:
   gfx::Rect GetBoundsForTabStripRegion(
-      const views::View* tabstrip) const override;
+      const gfx::Size& tabstrip_minimum_size) const override;
   int GetTopInset(bool restored) const override;
   int GetThemeBackgroundXInset() const override;
   void UpdateFrameColor() override;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index b70db96..9d4b276 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -54,6 +54,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
+#include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
 #include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_host.h"
 #include "chrome/browser/ui/views/location_bar/content_setting_image_view.h"
 #include "chrome/browser/ui/views/location_bar/custom_tab_bar_view.h"
@@ -514,7 +515,9 @@
 
   // Frame paints by default.
   EXPECT_TRUE(frame_view->ShouldPaint());
-  EXPECT_LT(0, frame_view->GetBoundsForTabStripRegion(browser_view->tabstrip())
+  EXPECT_LT(0, frame_view
+                   ->GetBoundsForTabStripRegion(
+                       browser_view->tab_strip_region_view()->GetMinimumSize())
                    .bottom());
 
   // Enter both browser fullscreen and tab fullscreen. Entering browser
@@ -535,7 +538,9 @@
   revealed_lock.reset();
   EXPECT_FALSE(immersive_mode_controller->IsRevealed());
   EXPECT_FALSE(frame_view->ShouldPaint());
-  EXPECT_EQ(0, frame_view->GetBoundsForTabStripRegion(browser_view->tabstrip())
+  EXPECT_EQ(0, frame_view
+                   ->GetBoundsForTabStripRegion(
+                       browser_view->tab_strip_region_view()->GetMinimumSize())
                    .bottom());
 
   // Repeat test but without tab fullscreen.
@@ -546,14 +551,18 @@
       ImmersiveModeController::ANIMATE_REVEAL_NO));
   EXPECT_TRUE(immersive_mode_controller->IsRevealed());
   EXPECT_TRUE(frame_view->ShouldPaint());
-  EXPECT_LT(0, frame_view->GetBoundsForTabStripRegion(browser_view->tabstrip())
+  EXPECT_LT(0, frame_view
+                   ->GetBoundsForTabStripRegion(
+                       browser_view->tab_strip_region_view()->GetMinimumSize())
                    .bottom());
 
   // Ending the reveal. Immersive browser should have the same behavior as full
   // screen, i.e., having an origin of (0,0).
   revealed_lock.reset();
   EXPECT_FALSE(frame_view->ShouldPaint());
-  EXPECT_EQ(0, frame_view->GetBoundsForTabStripRegion(browser_view->tabstrip())
+  EXPECT_EQ(0, frame_view
+                   ->GetBoundsForTabStripRegion(
+                       browser_view->tab_strip_region_view()->GetMinimumSize())
                    .bottom());
 
   // Exiting immersive fullscreen should make the caption buttons and the frame
@@ -565,7 +574,9 @@
   }
   EXPECT_FALSE(immersive_mode_controller->IsEnabled());
   EXPECT_TRUE(frame_view->ShouldPaint());
-  EXPECT_LT(0, frame_view->GetBoundsForTabStripRegion(browser_view->tabstrip())
+  EXPECT_LT(0, frame_view
+                   ->GetBoundsForTabStripRegion(
+                       browser_view->tab_strip_region_view()->GetMinimumSize())
                    .bottom());
 }
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
index a3f614c..7ce02c2 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
@@ -28,7 +28,7 @@
   void OnFullscreenStateChanged() override;
   bool CaptionButtonsOnLeadingEdge() const override;
   gfx::Rect GetBoundsForTabStripRegion(
-      const views::View* tabstrip) const override;
+      const gfx::Size& tabstrip_minimum_size) const override;
   int GetTopInset(bool restored) const override;
   int GetThemeBackgroundXInset() const override;
   void UpdateFullscreenTopUI() override;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
index 4c789d3..a5ce8e88 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
@@ -118,16 +118,14 @@
 }
 
 gfx::Rect BrowserNonClientFrameViewMac::GetBoundsForTabStripRegion(
-    const views::View* tabstrip) const {
+    const gfx::Size& tabstrip_minimum_size) const {
   // TODO(weili): In the future, we should hide the title bar, and show the
   // tab strip directly under the menu bar. For now, just lay our content
   // under the native title bar. Use the default title bar height to avoid
   // calling through private APIs.
-  DCHECK(tabstrip);
-
   const bool restored = !frame()->IsMaximized() && !frame()->IsFullscreen();
   gfx::Rect bounds(0, GetTopInset(restored), width(),
-                   tabstrip->GetPreferredSize().height());
+                   tabstrip_minimum_size.height());
 
   // Do not draw caption buttons on fullscreen.
   if (!frame()->IsFullscreen()) {
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index bb77313..43ec3ed2 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -433,8 +433,10 @@
   }
 
   gfx::Rect GetBoundsForTabStripRegionInBrowserView() const override {
+    const gfx::Size tabstrip_minimum_size =
+        browser_view_->tab_strip_region_view()->GetMinimumSize();
     gfx::RectF bounds_f(browser_view_->frame()->GetBoundsForTabStripRegion(
-        browser_view_->tabstrip()));
+        tabstrip_minimum_size));
     views::View::ConvertRectToTarget(browser_view_->parent(), browser_view_,
         &bounds_f);
     return gfx::ToEnclosingRect(bounds_f);
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 14f1f1f5..dd13e23b 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -178,6 +178,10 @@
     return GetBrowserViewLayout()->contents_border_widget();
   }
 
+  TabStripRegionView* tab_strip_region_view() const {
+    return tab_strip_region_view_;
+  }
+
   // Accessor for the TabStrip.
   TabStrip* tabstrip() { return tabstrip_; }
   const TabStrip* tabstrip() const { return tabstrip_; }
diff --git a/chrome/browser/ui/views/frame/browser_view_unittest.cc b/chrome/browser/ui/views/frame/browser_view_unittest.cc
index b1f61cc..9a5a608f 100644
--- a/chrome/browser/ui/views/frame/browser_view_unittest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_unittest.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
 #include "chrome/browser/ui/views/frame/browser_view_layout.h"
+#include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
 #include "chrome/browser/ui/views/frame/test_with_browser_view.h"
 #include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "chrome/browser/ui/views/infobars/infobar_container_view.h"
@@ -37,7 +38,7 @@
 // Tab strip bounds depend on the window frame sizes.
 gfx::Point ExpectedTabStripRegionOrigin(BrowserView* browser_view) {
   gfx::Rect tabstrip_bounds(browser_view->frame()->GetBoundsForTabStripRegion(
-      browser_view->tabstrip()));
+      browser_view->tab_strip_region_view()->GetMinimumSize()));
   gfx::Point tabstrip_region_origin(tabstrip_bounds.origin());
   views::View::ConvertPointToTarget(browser_view->parent(), browser_view,
                                     &tabstrip_region_origin);
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index 0bb252ae..4a87bd7 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/glass_browser_caption_button_container.h"
+#include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
 #include "chrome/browser/ui/views/frame/webui_tab_strip_container_view.h"
 #include "chrome/browser/ui/views/tabs/new_tab_button.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
@@ -148,7 +149,7 @@
 }
 
 gfx::Rect GlassBrowserFrameView::GetBoundsForTabStripRegion(
-    const views::View* tabstrip) const {
+    const gfx::Size& tabstrip_minimum_size) const {
   const int x = CaptionButtonsOnLeadingEdge()
                     ? (width() - frame()->GetMinimizeButtonOffset())
                     : 0;
@@ -156,7 +157,7 @@
   if (!CaptionButtonsOnLeadingEdge())
     end_x = std::min(MinimizeButtonX(), end_x);
   return gfx::Rect(x, TopAreaHeight(false), std::max(0, end_x - x),
-                   tabstrip->GetPreferredSize().height());
+                   tabstrip_minimum_size.height());
 }
 
 int GlassBrowserFrameView::GetTopInset(bool restored) const {
@@ -604,7 +605,9 @@
 
   const int titlebar_height =
       browser_view()->IsTabStripVisible()
-          ? GetBoundsForTabStripRegion(browser_view()->tabstrip()).bottom()
+          ? GetBoundsForTabStripRegion(
+                browser_view()->tab_strip_region_view()->GetMinimumSize())
+                .bottom()
           : TitlebarHeight(false);
   const gfx::Rect titlebar_rect = gfx::ToEnclosingRect(
       gfx::RectF(0, y, width() * scale, titlebar_height * scale - y));
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.h b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
index 61aede8d..3bbbb9b3 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
@@ -36,7 +36,7 @@
   // BrowserNonClientFrameView:
   bool CaptionButtonsOnLeadingEdge() const override;
   gfx::Rect GetBoundsForTabStripRegion(
-      const views::View* tabstrip) const override;
+      const gfx::Size& tabstrip_minimum_size) const override;
   int GetTopInset(bool restored) const override;
   int GetThemeBackgroundXInset() const override;
   bool HasVisibleBackgroundTabShapes(
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index a8924164..ec4a52b 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h"
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h"
+#include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
 #include "chrome/browser/ui/views/tab_icon_view.h"
 #include "chrome/browser/ui/views/tabs/new_tab_button.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
@@ -196,12 +197,8 @@
 // OpaqueBrowserFrameView, BrowserNonClientFrameView implementation:
 
 gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStripRegion(
-    const views::View* tabstrip) const {
-  if (!tabstrip)
-    return gfx::Rect();
-
-  return layout_->GetBoundsForTabStripRegion(tabstrip->GetMinimumSize(),
-                                             width());
+    const gfx::Size& tabstrip_minimum_size) const {
+  return layout_->GetBoundsForTabStripRegion(tabstrip_minimum_size, width());
 }
 
 int OpaqueBrowserFrameView::GetTopInset(bool restored) const {
@@ -450,7 +447,7 @@
 }
 
 gfx::Size OpaqueBrowserFrameView::GetTabstripMinimumSize() const {
-  return browser_view()->tabstrip()->GetMinimumSize();
+  return browser_view()->tab_strip_region_view()->GetMinimumSize();
 }
 
 int OpaqueBrowserFrameView::GetTopAreaHeight() const {
@@ -459,7 +456,7 @@
     return non_client_top_height;
   return std::max(
       non_client_top_height,
-      GetBoundsForTabStripRegion(browser_view()->tabstrip()).bottom() -
+      GetBoundsForTabStripRegion(GetTabstripMinimumSize()).bottom() -
           GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP));
 }
 
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
index 60f5556e5..a26a2ea 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
@@ -57,7 +57,7 @@
 
   // BrowserNonClientFrameView:
   gfx::Rect GetBoundsForTabStripRegion(
-      const views::View* tabstrip) const override;
+      const gfx::Size& tabstrip_minimum_size) const override;
   int GetTopInset(bool restored) const override;
   int GetThemeBackgroundXInset() const override;
   void UpdateThrobber(bool running) override;
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
index 4a8c0ca1..65c07e6 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
@@ -25,6 +25,7 @@
 TabStripRegionView::~TabStripRegionView() = default;
 
 TabStrip* TabStripRegionView::AddTabStrip(std::unique_ptr<TabStrip> tab_strip) {
+  tab_strip_ = tab_strip.get();
   tab_strip->SetAvailableWidthCallback(
       base::BindRepeating(&TabStripRegionView::CalculateTabStripAvailableWidth,
                           base::Unretained(this)));
@@ -51,6 +52,16 @@
   PreferredSizeChanged();
 }
 
+gfx::Size TabStripRegionView::GetMinimumSize() const {
+  gfx::Size tab_strip_min_size = tab_strip_->GetMinimumSize();
+  // Cap the tabstrip minimum width to a reasonable value so browser windows
+  // aren't forced to grow arbitrarily wide.
+  const int max_min_width = 520;
+  tab_strip_min_size.set_width(
+      std::min(max_min_width, tab_strip_min_size.width()));
+  return tab_strip_min_size;
+}
+
 int TabStripRegionView::CalculateTabStripAvailableWidth() {
   Layout();
   base::Optional<int> available_width =
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.h b/chrome/browser/ui/views/frame/tab_strip_region_view.h
index 1950343..406d5926 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.h
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.h
@@ -21,6 +21,7 @@
   // views::View overrides:
   const char* GetClassName() const override;
   void ChildPreferredSizeChanged(views::View* child) override;
+  gfx::Size GetMinimumSize() const override;
 
   // TODO(958173): Override OnBoundsChanged to cancel tabstrip animations.
 
@@ -30,6 +31,7 @@
   int CalculateTabStripAvailableWidth();
 
   views::View* tab_strip_container_;
+  views::View* tab_strip_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_FRAME_TAB_STRIP_REGION_VIEW_H_
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index 6e88b81..43cb0a8 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -39,6 +39,7 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/native_browser_frame_factory.h"
+#include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
 #include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
@@ -2581,7 +2582,7 @@
       BrowserView::GetBrowserViewForBrowser(browser2);
   const gfx::Rect tabstrip_region2_bounds =
       browser_view2->frame()->GetBoundsForTabStripRegion(
-          browser_view2->tabstrip());
+          browser_view2->tab_strip_region_view()->GetMinimumSize());
   gfx::Rect bounds = initial_bounds;
   bounds.Offset(0, tabstrip_region2_bounds.bottom());
   browser()->window()->SetBounds(bounds);
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 8519cac..21269ed2 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -2285,13 +2285,7 @@
           ? GetStackableTabWidth() + (2 * kStackedPadding * kMaxStackedCount)
           : layout_helper_->CalculateMinimumWidth();
 
-  // Cap the tabstrip minimum width to a reasonable value so browser windows
-  // aren't forced to grow arbitrarily wide.
-  const int max_min_width = 520;
-  const int capped_minimum_width =
-      std::min(minimum_tab_area_width, max_min_width);
-
-  return gfx::Size(capped_minimum_width + GetRightSideReservedWidth(),
+  return gfx::Size(minimum_tab_area_width + GetRightSideReservedWidth(),
                    GetLayoutConstant(TAB_HEIGHT));
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip_browsertest.cc b/chrome/browser/ui/views/tabs/tab_strip_browsertest.cc
index 37297b79..b207e9b1 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_browsertest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_browsertest.cc
@@ -735,3 +735,83 @@
   // No change expected.
   EXPECT_EQ(contentses, GetWebContentses());
 }
+
+IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
+                       CollapseGroup_WithActiveTabInGroup_SelectsNext) {
+  AppendTab();
+
+  tab_groups::TabGroupId group = AddTabToNewGroup(0);
+  ui::MouseEvent dummy_event =
+      ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::PointF(), gfx::PointF(),
+                     base::TimeTicks::Now(), 0, 0);
+  tab_strip()->SelectTab(tab_strip()->tab_at(0), dummy_event);
+  ASSERT_EQ(0, tab_strip()->controller()->GetActiveIndex());
+  ASSERT_FALSE(tab_strip()->controller()->IsGroupCollapsed(group));
+  tab_strip()->controller()->ToggleTabGroupCollapsedState(group);
+
+  EXPECT_TRUE(tab_strip()->controller()->IsGroupCollapsed(group));
+  EXPECT_EQ(1, tab_strip()->controller()->GetActiveIndex());
+}
+
+IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
+                       CollapseGroup_WithActiveTabInGroup_SelectsPrevious) {
+  AppendTab();
+
+  tab_groups::TabGroupId group = AddTabToNewGroup(1);
+  ui::MouseEvent dummy_event =
+      ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::PointF(), gfx::PointF(),
+                     base::TimeTicks::Now(), 0, 0);
+  tab_strip()->SelectTab(tab_strip()->tab_at(1), dummy_event);
+  ASSERT_EQ(1, tab_strip()->controller()->GetActiveIndex());
+  ASSERT_FALSE(tab_strip()->controller()->IsGroupCollapsed(group));
+  tab_strip()->controller()->ToggleTabGroupCollapsedState(group);
+
+  EXPECT_TRUE(tab_strip()->controller()->IsGroupCollapsed(group));
+  EXPECT_EQ(0, tab_strip()->controller()->GetActiveIndex());
+}
+
+IN_PROC_BROWSER_TEST_F(
+    TabStripBrowsertest,
+    CollapseGroup_WithActiveTabOutsideGroup_DoesNotChangeActiveTab) {
+  AppendTab();
+
+  tab_groups::TabGroupId group = AddTabToNewGroup(0);
+  ui::MouseEvent dummy_event =
+      ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::PointF(), gfx::PointF(),
+                     base::TimeTicks::Now(), 0, 0);
+  tab_strip()->SelectTab(tab_strip()->tab_at(1), dummy_event);
+  ASSERT_EQ(1, tab_strip()->controller()->GetActiveIndex());
+  ASSERT_FALSE(tab_strip()->controller()->IsGroupCollapsed(group));
+  tab_strip()->controller()->ToggleTabGroupCollapsedState(group);
+
+  EXPECT_TRUE(tab_strip()->controller()->IsGroupCollapsed(group));
+  EXPECT_EQ(1, tab_strip()->controller()->GetActiveIndex());
+}
+
+IN_PROC_BROWSER_TEST_F(TabStripBrowsertest, CollapseGroup_Fails) {
+  AppendTab();
+
+  tab_groups::TabGroupId group = AddTabToNewGroup(0);
+  tab_strip_model()->AddToExistingGroup({1}, group);
+  ASSERT_FALSE(tab_strip()->controller()->IsGroupCollapsed(group));
+  tab_strip()->controller()->ToggleTabGroupCollapsedState(group);
+
+  EXPECT_FALSE(tab_strip()->controller()->IsGroupCollapsed(group));
+}
+
+IN_PROC_BROWSER_TEST_F(TabStripBrowsertest,
+                       ActivateTabInCollapsedGroup_ExpandsCollapsedGroup) {
+  AppendTab();
+
+  tab_groups::TabGroupId group = AddTabToNewGroup(0);
+  ASSERT_FALSE(tab_strip()->controller()->IsGroupCollapsed(group));
+  tab_strip()->controller()->ToggleTabGroupCollapsedState(group);
+  ASSERT_TRUE(tab_strip()->controller()->IsGroupCollapsed(group));
+  ASSERT_EQ(1, tab_strip()->controller()->GetActiveIndex());
+
+  ui::MouseEvent dummy_event =
+      ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::PointF(), gfx::PointF(),
+                     base::TimeTicks::Now(), 0, 0);
+  tab_strip()->SelectTab(tab_strip()->tab_at(0), dummy_event);
+  EXPECT_FALSE(tab_strip()->controller()->IsGroupCollapsed(group));
+}
diff --git a/chrome/browser/ui/webui/settings/chromeos/apps_section.cc b/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
index 0ba95ed..9f14dbb 100644
--- a/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/apps_section.cc
@@ -314,6 +314,10 @@
        IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING},
       {"pluginVmSharedPathsListEmptyMessage",
        IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE},
+      {"pluginVmPermissionDialogCameraLabel",
+       IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_CAMERA_LABEL},
+      {"pluginVmPermissionDialogMicrophoneLabel",
+       IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_MICROPHONE_LABEL},
   };
   AddLocalizedStringsBulk(html_source, kLocalizedStrings);
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc b/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc
index 715782b..0f531b8 100644
--- a/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc
@@ -12,6 +12,7 @@
 #include "base/bind_helpers.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
+#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -32,6 +33,15 @@
       "removePluginVmSharedPath",
       base::BindRepeating(&PluginVmHandler::HandleRemovePluginVmSharedPath,
                           weak_ptr_factory_.GetWeakPtr()));
+  web_ui()->RegisterMessageCallback(
+      "wouldPermissionChangeRequireRelaunch",
+      base::BindRepeating(
+          &PluginVmHandler::HandleWouldPermissionChangeRequireRelaunch,
+          weak_ptr_factory_.GetWeakPtr()));
+  web_ui()->RegisterMessageCallback(
+      "setPluginVmPermission",
+      base::BindRepeating(&PluginVmHandler::HandleSetPluginVmPermission,
+                          weak_ptr_factory_.GetWeakPtr()));
 }
 
 void PluginVmHandler::HandleGetPluginVmSharedPathsDisplayText(
@@ -68,5 +78,38 @@
           path));
 }
 
+void PluginVmHandler::HandleWouldPermissionChangeRequireRelaunch(
+    const base::ListValue* args) {
+  AllowJavascript();
+  CHECK_EQ(3U, args->GetSize());
+  std::string callback_id = args->GetList()[0].GetString();
+  plugin_vm::PermissionType permission_type =
+      static_cast<plugin_vm::PermissionType>(args->GetList()[1].GetInt());
+  DCHECK(permission_type == plugin_vm::PermissionType::kCamera ||
+         permission_type == plugin_vm::PermissionType::kMicrophone);
+  bool proposed_value = args->GetList()[2].GetBool();
+  bool current_value =
+      plugin_vm::PluginVmManagerFactory::GetForProfile(profile_)->GetPermission(
+          permission_type);
+  // This can probably incorrectly return false in a small window during
+  // startup.
+  bool requires_relaunch =
+      plugin_vm::IsPluginVmRunning(profile_) && proposed_value != current_value;
+
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            base::Value(requires_relaunch));
+}
+
+void PluginVmHandler::HandleSetPluginVmPermission(const base::ListValue* args) {
+  CHECK_EQ(2U, args->GetSize());
+  plugin_vm::PermissionType permission_type =
+      static_cast<plugin_vm::PermissionType>(args->GetList()[0].GetInt());
+  bool proposed_value = args->GetList()[1].GetBool();
+  DCHECK(permission_type == plugin_vm::PermissionType::kCamera ||
+         permission_type == plugin_vm::PermissionType::kMicrophone);
+  plugin_vm::PluginVmManagerFactory::GetForProfile(profile_)->SetPermission(
+      permission_type, proposed_value);
+}
+
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h b/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h
index d57b34da..36270cc 100644
--- a/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h
@@ -8,6 +8,8 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h"
+#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager_factory.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 
 class Profile;
@@ -32,8 +34,14 @@
   void HandleGetPluginVmSharedPathsDisplayText(const base::ListValue* args);
   // Remove a specified path from being shared.
   void HandleRemovePluginVmSharedPath(const base::ListValue* args);
+  // Checks if Plugin VM would need to be relaunched if the proposed changes are
+  // made.
+  void HandleWouldPermissionChangeRequireRelaunch(const base::ListValue* args);
+  // Sets the specified permission to the value proposed.
+  void HandleSetPluginVmPermission(const base::ListValue* args);
 
   Profile* profile_;
+
   // weak_ptr_factory_ should always be last member.
   base::WeakPtrFactory<PluginVmHandler> weak_ptr_factory_{this};
 
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 323b7480..f87b79d 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1593453566-5a3b62176029046ef3c78ea8aa3bef37e9aa6907.profdata
+chrome-mac-master-1593475028-63728bbaf9d72e013a403087ba2956d3e5f5a26b.profdata
diff --git a/chrome/common/extensions/api/developer_private.idl b/chrome/common/extensions/api/developer_private.idl
index 2a58e11..6f62839 100644
--- a/chrome/common/extensions/api/developer_private.idl
+++ b/chrome/common/extensions/api/developer_private.idl
@@ -161,10 +161,6 @@
     ViewType type;
   };
 
-  enum ControllerType {
-    POLICY
-  };
-
   enum HostAccess {
     ON_CLICK,
     ON_SPECIFIC_SITES,
@@ -172,7 +168,6 @@
   };
 
   dictionary ControlledInfo {
-    ControllerType type;
     DOMString text;
   };
 
diff --git a/chrome/renderer/extensions/app_hooks_delegate.h b/chrome/renderer/extensions/app_hooks_delegate.h
index da546fb..8c1c192d 100644
--- a/chrome/renderer/extensions/app_hooks_delegate.h
+++ b/chrome/renderer/extensions/app_hooks_delegate.h
@@ -26,9 +26,6 @@
 // The custom hooks for the chrome.app API.
 class AppHooksDelegate : public APIBindingHooksDelegate {
  public:
-  using GetterCallback =
-      base::Callback<void(const v8::PropertyCallbackInfo<v8::Value>& info)>;
-
   AppHooksDelegate(Dispatcher* dispatcher, APIRequestHandler* request_handler);
   ~AppHooksDelegate() override;
 
@@ -98,8 +95,6 @@
 
   IPCHelper ipc_helper_;
 
-  GetterCallback callback_;
-
   DISALLOW_COPY_AND_ASSIGN(AppHooksDelegate);
 };
 
diff --git a/chrome/renderer/media/chrome_speech_recognition_client.cc b/chrome/renderer/media/chrome_speech_recognition_client.cc
index 56ffc36..70da74b25 100644
--- a/chrome/renderer/media/chrome_speech_recognition_client.cc
+++ b/chrome/renderer/media/chrome_speech_recognition_client.cc
@@ -10,6 +10,7 @@
 #include "content/public/renderer/render_frame.h"
 #include "media/base/audio_bus.h"
 #include "media/base/audio_parameters.h"
+#include "media/base/bind_to_current_loop.h"
 #include "media/base/channel_mixer.h"
 #include "media/base/media_switches.h"
 #include "media/mojo/mojom/media_types.mojom.h"
@@ -46,6 +47,10 @@
       caption_host_.BindNewPipeAndPassReceiver());
   is_website_blocked_ = IsUrlBlocked(
       render_frame->GetWebFrame()->GetSecurityOrigin().ToString().Utf8());
+
+  send_audio_callback_ = media::BindToCurrentLoop(base::BindRepeating(
+      &ChromeSpeechRecognitionClient::SendAudioToSpeechRecognitionService,
+      weak_factory_.GetWeakPtr()));
 }
 
 void ChromeSpeechRecognitionClient::OnRecognizerBound(
@@ -62,10 +67,7 @@
 void ChromeSpeechRecognitionClient::AddAudio(
     scoped_refptr<media::AudioBuffer> buffer) {
   DCHECK(buffer);
-  if (IsSpeechRecognitionAvailable()) {
-    speech_recognition_recognizer_->SendAudioToSpeechRecognitionService(
-        ConvertToAudioDataS16(std::move(buffer)));
-  }
+  send_audio_callback_.Run(ConvertToAudioDataS16(std::move(buffer)));
 }
 
 void ChromeSpeechRecognitionClient::AddAudio(
@@ -73,17 +75,14 @@
     int sample_rate,
     media::ChannelLayout channel_layout) {
   DCHECK(audio_bus);
-  if (IsSpeechRecognitionAvailable()) {
-    speech_recognition_recognizer_->SendAudioToSpeechRecognitionService(
-        ConvertToAudioDataS16(std::move(audio_bus), sample_rate,
-                              channel_layout));
-  }
+  send_audio_callback_.Run(
+      ConvertToAudioDataS16(std::move(audio_bus), sample_rate, channel_layout));
 }
 
 bool ChromeSpeechRecognitionClient::IsSpeechRecognitionAvailable() {
   // TODO(evliu): Check if SODA is available.
   return !is_website_blocked_ && is_browser_requesting_transcription_ &&
-         is_recognizer_bound_ && speech_recognition_recognizer_.is_connected();
+         is_recognizer_bound_;
 }
 
 // The OnReadyCallback is set by the owner of |this| and is executed when speech
@@ -140,6 +139,15 @@
   }
 }
 
+void ChromeSpeechRecognitionClient::SendAudioToSpeechRecognitionService(
+    media::mojom::AudioDataS16Ptr audio_data) {
+  DCHECK(audio_data);
+  if (IsSpeechRecognitionAvailable()) {
+    speech_recognition_recognizer_->SendAudioToSpeechRecognitionService(
+        std::move(audio_data));
+  }
+}
+
 media::mojom::AudioDataS16Ptr
 ChromeSpeechRecognitionClient::ConvertToAudioDataS16(
     scoped_refptr<media::AudioBuffer> buffer) {
diff --git a/chrome/renderer/media/chrome_speech_recognition_client.h b/chrome/renderer/media/chrome_speech_recognition_client.h
index 19b31c5d..df0e0574 100644
--- a/chrome/renderer/media/chrome_speech_recognition_client.h
+++ b/chrome/renderer/media/chrome_speech_recognition_client.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/containers/flat_set.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/common/caption.mojom.h"
 #include "media/base/audio_buffer.h"
 #include "media/base/speech_recognition_client.h"
@@ -29,6 +30,9 @@
     : public media::SpeechRecognitionClient,
       public media::mojom::SpeechRecognitionRecognizerClient {
  public:
+  using SendAudioToSpeechRecognitionServiceCallback =
+      base::RepeatingCallback<void(media::mojom::AudioDataS16Ptr audio_data)>;
+
   explicit ChromeSpeechRecognitionClient(
       content::RenderFrame* render_frame,
       media::SpeechRecognitionClient::OnReadyCallback callback);
@@ -55,6 +59,9 @@
       media::mojom::SpeechRecognitionResultPtr result) override;
 
  private:
+  void SendAudioToSpeechRecognitionService(
+      media::mojom::AudioDataS16Ptr audio_data);
+
   media::mojom::AudioDataS16Ptr ConvertToAudioDataS16(
       scoped_refptr<media::AudioBuffer> buffer);
 
@@ -78,6 +85,9 @@
 
   media::SpeechRecognitionClient::OnReadyCallback on_ready_callback_;
 
+  // Sends audio to the speech recognition thread on the renderer thread.
+  SendAudioToSpeechRecognitionServiceCallback send_audio_callback_;
+
   mojo::Remote<media::mojom::SpeechRecognitionContext>
       speech_recognition_context_;
   mojo::Remote<media::mojom::SpeechRecognitionRecognizer>
@@ -111,6 +121,8 @@
   // A flag indicating whether the speech recognition service supports
   // multichannel audio.
   bool is_multichannel_supported_ = false;
+
+  base::WeakPtrFactory<ChromeSpeechRecognitionClient> weak_factory_{this};
 };
 
 #endif  // CHROME_RENDERER_MEDIA_CHROME_SPEECH_RECOGNITION_CLIENT_H_
diff --git a/chrome/test/data/pdf/basic_test.js b/chrome/test/data/pdf/basic_test.js
index ca0c24f..52667efb 100644
--- a/chrome/test/data/pdf/basic_test.js
+++ b/chrome/test/data/pdf/basic_test.js
@@ -49,7 +49,7 @@
     // Test that the traversal through the shadow DOM works correctly.
     const viewer = document.body.querySelector('pdf-viewer');
     const toolbar = viewer.shadowRoot.querySelector('#toolbar');
-    toolbar.$.pageselector.pageSelector.inputElement.focus();
+    toolbar.$.pageselector.pageSelector.focus();
     chrome.test.assertTrue(shouldIgnoreKeyEvents(toolbar));
 
     // Test case where the active element has a shadow root of its own.
diff --git a/chrome/test/data/pdf/material_elements_test.js b/chrome/test/data/pdf/material_elements_test.js
index 23c38e7..cea6208d 100644
--- a/chrome/test/data/pdf/material_elements_test.js
+++ b/chrome/test/data/pdf/material_elements_test.js
@@ -50,6 +50,7 @@
     // Simulate entering text into `input` and pressing enter.
     function changeInput(newValue) {
       input.value = newValue;
+      input.dispatchEvent(new CustomEvent('input'));
       input.dispatchEvent(new CustomEvent('change'));
     }
 
diff --git a/chrome/test/data/pdf/two_up_view_feature_test.js b/chrome/test/data/pdf/two_up_view_feature_test.js
deleted file mode 100644
index 2393e5b8..0000000
--- a/chrome/test/data/pdf/two_up_view_feature_test.js
+++ /dev/null
@@ -1,40 +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.
-
-import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-
-chrome.test.runTests([
-  function testTwoUpViewFeatureDisabled() {
-    const toolbar = viewer.shadowRoot.querySelector('#zoom-toolbar');
-    loadTimeData.overrideValues({'pdfTwoUpViewEnabled': false});
-    toolbar.strings = Object.assign({}, toolbar.strings);
-
-    const twoUpButton = toolbar.shadowRoot.querySelector('#two-up-view-button');
-    chrome.test.assertTrue(!!twoUpButton);
-    chrome.test.assertTrue(twoUpButton.hidden);
-
-    const fitButton = toolbar.shadowRoot.querySelector('#fit-button');
-    chrome.test.assertTrue(!!fitButton);
-    chrome.test.assertEq(100, fitButton.delay);
-
-    chrome.test.succeed();
-  },
-
-
-  function testTwoUpViewFeatureEnabled() {
-    const toolbar = viewer.shadowRoot.querySelector('#zoom-toolbar');
-    loadTimeData.overrideValues({'pdfTwoUpViewEnabled': true});
-    toolbar.strings = Object.assign({}, toolbar.strings);
-
-    const twoUpButton = toolbar.shadowRoot.querySelector('#two-up-view-button');
-    chrome.test.assertTrue(!!twoUpButton);
-    chrome.test.assertFalse(twoUpButton.hidden);
-
-    const fitButton = toolbar.shadowRoot.querySelector('#fit-button');
-    chrome.test.assertTrue(!!fitButton);
-    chrome.test.assertEq(150, fitButton.delay);
-
-    chrome.test.succeed();
-  },
-]);
diff --git a/chrome/test/data/webui/extensions/detail_view_test.js b/chrome/test/data/webui/extensions/detail_view_test.js
index 9b7e575..85ccea78 100644
--- a/chrome/test/data/webui/extensions/detail_view_test.js
+++ b/chrome/test/data/webui/extensions/detail_view_test.js
@@ -356,7 +356,7 @@
   test(assert(extension_detail_view_tests.TestNames.Indicator), function() {
     const indicator = item.$$('cr-tooltip-icon');
     expectTrue(indicator.hidden);
-    item.set('data.controlledInfo', {type: 'POLICY', text: 'policy'});
+    item.set('data.controlledInfo', {text: 'policy'});
     flush();
     expectFalse(indicator.hidden);
   });
diff --git a/chrome/test/data/webui/extensions/item_test.js b/chrome/test/data/webui/extensions/item_test.js
index 5f4dcca2..901262c 100644
--- a/chrome/test/data/webui/extensions/item_test.js
+++ b/chrome/test/data/webui/extensions/item_test.js
@@ -317,7 +317,7 @@
     expectEquals('extensions-icons:input', icon.icon);
 
     item.set('data.location', 'FROM_STORE');
-    item.set('data.controlledInfo', {type: 'POLICY', text: 'policy'});
+    item.set('data.controlledInfo', {text: 'policy'});
     flush();
     expectTrue(isChildVisible(item, '#source-indicator'));
     expectEquals('extensions-icons:business', icon.icon);
diff --git a/chrome/test/data/webui/print_preview/BUILD.gn b/chrome/test/data/webui/print_preview/BUILD.gn
index ad6477c3..75bb493 100644
--- a/chrome/test/data/webui/print_preview/BUILD.gn
+++ b/chrome/test/data/webui/print_preview/BUILD.gn
@@ -22,9 +22,9 @@
     ":color_settings_test",
     ":copies_settings_test",
     ":custom_margins_test",
+    ":destination_dialog_interactive_test",
+    ":destination_dialog_test",
 
-    #":destination_dialog_interactive_test",
-    #":destination_dialog_test",
     #":destination_item_test",
     #":destination_list_test",
     #":destination_search_test_chromeos",
@@ -197,6 +197,32 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
+js_library("destination_dialog_test") {
+  deps = [
+    ":cloud_print_interface_stub",
+    ":native_layer_stub",
+    ":print_preview_test_utils",
+    "..:chai_assert",
+    "..:test_util.m",
+    "//chrome/browser/resources/print_preview:print_preview",
+    "//ui/webui/resources/js:assert.m",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
+
+js_library("destination_dialog_interactive_test") {
+  deps = [
+    ":cloud_print_interface_stub",
+    ":native_layer_stub",
+    ":print_preview_test_utils",
+    "..:chai_assert",
+    "..:test_util.m",
+    "//chrome/browser/resources/print_preview:print_preview",
+    "//ui/webui/resources/js:assert.m",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
+
 if (is_chromeos) {
   js_library("destination_dropdown_cros_test") {
     deps = [
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js
index 1a20a32..b157dec 100644
--- a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js
+++ b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js
@@ -2,15 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CloudPrintInterfaceImpl, Destination, makeRecentDestination, NativeLayer, NativeLayerImpl, State} from 'chrome://print/print_preview.js';
+import {CloudPrintInterfaceImpl, Destination, makeRecentDestination, NativeLayerImpl, State} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-import {CloudPrintInterfaceStub} from 'chrome://test/print_preview/cloud_print_interface_stub.js';
-import {NativeLayerStub} from 'chrome://test/print_preview/native_layer_stub.js';
-import {getDestinations, setupTestListenerElement} from 'chrome://test/print_preview/print_preview_test_utils.js';
-import {eventToPromise, fakeDataBind} from 'chrome://test/test_util.m.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../chai_assert.js';
+import {eventToPromise, fakeDataBind} from '../test_util.m.js';
+
+import {CloudPrintInterfaceStub} from './cloud_print_interface_stub.js';
+import {NativeLayerStub} from './native_layer_stub.js';
+import {getDestinations, setupTestListenerElement} from './print_preview_test_utils.js';
 
 window.destination_dialog_interactive_test = {};
+const destination_dialog_interactive_test =
+    window.destination_dialog_interactive_test;
 destination_dialog_interactive_test.suiteName =
     'DestinationDialogInteractiveTest';
 /** @enum {string} */
@@ -21,11 +26,11 @@
 };
 
 suite(destination_dialog_interactive_test.suiteName, function() {
-  /** @type {?PrintPreviewDestinationDialogElement} */
-  let dialog = null;
+  /** @type {!PrintPreviewDestinationDialogElement} */
+  let dialog;
 
-  /** @type {?NativeLayer} */
-  let nativeLayer = null;
+  /** @type {!NativeLayerStub} */
+  let nativeLayer;
 
   /** @override */
   suiteSetup(function() {
@@ -67,7 +72,8 @@
         [] /* userAccounts */, true /* syncAvailable */);
     return nativeLayer.whenCalled('getPrinterCapabilities').then(() => {
       // Retrieve a reference to dialog
-      dialog = destinationSettings.$.destinationDialog.get();
+      dialog = /** @type {!PrintPreviewDestinationDialogElement} */ (
+          destinationSettings.$$('#destinationDialog').get());
     });
   });
 
@@ -76,7 +82,9 @@
   test(
       assert(destination_dialog_interactive_test.TestNames.FocusSearchBox),
       function() {
-        const searchInput = dialog.$.searchBox.getSearchInput();
+        const searchInput = /** @type {!PrintPreviewSearchBoxElement} */ (
+                                dialog.$$('#searchBox'))
+                                .getSearchInput();
         assertTrue(!!searchInput);
         const whenFocusDone = eventToPromise('focus', searchInput);
         dialog.destinationStore.startLoadAllDestinations();
@@ -91,7 +99,9 @@
       assert(
           destination_dialog_interactive_test.TestNames.FocusSearchBoxOnSignIn),
       function() {
-        const searchInput = dialog.$.searchBox.getSearchInput();
+        const searchInput = /** @type {!PrintPreviewSearchBoxElement} */ (
+                                dialog.$$('#searchBox'))
+                                .getSearchInput();
         assertTrue(!!searchInput);
         const signInLink = dialog.$$('.sign-in');
         assertTrue(!!signInLink);
@@ -125,19 +135,21 @@
   test(
       assert(destination_dialog_interactive_test.TestNames.EscapeSearchBox),
       function() {
-        const searchInput = dialog.$.searchBox.getSearchInput();
+        const searchBox = /** @type {!PrintPreviewSearchBoxElement} */ (
+            dialog.$$('#searchBox'));
+        const searchInput = searchBox.getSearchInput();
         assertTrue(!!searchInput);
         const whenFocusDone = eventToPromise('focus', searchInput);
         dialog.destinationStore.startLoadAllDestinations();
         dialog.show();
         return whenFocusDone
             .then(() => {
-              assertTrue(dialog.$.dialog.open);
+              assertTrue(dialog.$$('#dialog').open);
 
               // Put something in the search box.
               const whenSearchChanged =
-                  eventToPromise('search-changed', dialog.$.searchBox);
-              dialog.$.searchBox.setValue('query');
+                  eventToPromise('search-changed', searchBox);
+              searchBox.setValue('query');
               return whenSearchChanged;
             })
             .then(() => {
@@ -150,12 +162,12 @@
             })
             .then(() => {
               // Dialog should still be open.
-              assertTrue(dialog.$.dialog.open);
+              assertTrue(dialog.$$('#dialog').open);
 
               // Clear the search box.
               const whenSearchChanged =
-                  eventToPromise('search-changed', dialog.$.searchBox);
-              dialog.$.searchBox.setValue('');
+                  eventToPromise('search-changed', searchBox);
+              searchBox.setValue('');
               return whenSearchChanged;
             })
             .then(() => {
@@ -168,7 +180,7 @@
             })
             .then(() => {
               // Dialog is closed.
-              assertFalse(dialog.$.dialog.open);
+              assertFalse(dialog.$$('#dialog').open);
             });
       });
 });
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_test.js b/chrome/test/data/webui/print_preview/destination_dialog_test.js
index c4fa7e60..5b0fafa7 100644
--- a/chrome/test/data/webui/print_preview/destination_dialog_test.js
+++ b/chrome/test/data/webui/print_preview/destination_dialog_test.js
@@ -2,17 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Destination, DestinationStore, InvitationStore, makeRecentDestination, NativeLayer, NativeLayerImpl, PluginProxy} from 'chrome://print/print_preview.js';
+import {Destination, DestinationStore, InvitationStore, LocalDestinationInfo, makeRecentDestination, NativeLayerImpl, PluginProxy, RecentDestination} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {keyEventOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {CloudPrintInterfaceStub} from 'chrome://test/print_preview/cloud_print_interface_stub.js';
-import {NativeLayerStub} from 'chrome://test/print_preview/native_layer_stub.js';
-import {PDFPluginStub} from 'chrome://test/print_preview/plugin_stub.js';
-import {createDestinationStore, getDestinations, getGoogleDriveDestination, setupTestListenerElement} from 'chrome://test/print_preview/print_preview_test_utils.js';
-import {eventToPromise} from 'chrome://test/test_util.m.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../chai_assert.js';
+import {eventToPromise} from '../test_util.m.js';
+
+import {CloudPrintInterfaceStub} from './cloud_print_interface_stub.js';
+import {NativeLayerStub} from './native_layer_stub.js';
+import {PDFPluginStub} from './plugin_stub.js';
+import {createDestinationStore, getDestinations, getGoogleDriveDestination, setupTestListenerElement} from './print_preview_test_utils.js';
 
 window.destination_dialog_test = {};
+const destination_dialog_test = window.destination_dialog_test;
 destination_dialog_test.suiteName = 'DestinationDialogTest';
 /** @enum {string} */
 destination_dialog_test.TestNames = {
@@ -22,17 +26,17 @@
 };
 
 suite(destination_dialog_test.suiteName, function() {
-  /** @type {?PrintPreviewDestinationDialogElement} */
-  let dialog = null;
+  /** @type {!PrintPreviewDestinationDialogElement} */
+  let dialog;
 
-  /** @type {?DestinationStore} */
-  let destinationStore = null;
+  /** @type {!DestinationStore} */
+  let destinationStore;
 
-  /** @type {?NativeLayer} */
-  let nativeLayer = null;
+  /** @type {!NativeLayerStub} */
+  let nativeLayer;
 
-  /** @type {?CloudPrintInterface} */
-  let cloudPrintInterface = null;
+  /** @type {!CloudPrintInterfaceStub} */
+  let cloudPrintInterface;
 
   /** @type {!Array<!Destination>} */
   let destinations = [];
@@ -65,7 +69,8 @@
         recentDestinations /* recentDestinations */);
 
     // Set up dialog
-    dialog = document.createElement('print-preview-destination-dialog');
+    dialog = /** @type {!PrintPreviewDestinationDialogElement} */ (
+        document.createElement('print-preview-destination-dialog'));
     dialog.activeUser = '';
     dialog.users = [];
     dialog.destinationStore = destinationStore;
@@ -132,7 +137,7 @@
         flush();
         provisionalDialog =
             dialog.$$('print-preview-provisional-destination-resolver');
-        assertFalse(provisionalDialog.$.dialog.open);
+        assertFalse(provisionalDialog.$$('#dialog').open);
         const list = dialog.$$('print-preview-destination-list');
         const printerItems = list.shadowRoot.querySelectorAll(
             'print-preview-destination-list-item');
@@ -147,7 +152,7 @@
         // Click the provisional destination to select it.
         provisionalItem.click();
         flush();
-        assertTrue(provisionalDialog.$.dialog.open);
+        assertTrue(provisionalDialog.$$('#dialog').open);
 
         // Send escape key on provisionalDialog. Destinations dialog should
         // not close.
@@ -156,8 +161,8 @@
         flush();
         await whenClosed;
 
-        assertFalse(provisionalDialog.$.dialog.open);
-        assertTrue(dialog.$.dialog.open);
+        assertFalse(provisionalDialog.$$('#dialog').open);
+        assertTrue(dialog.$$('#dialog').open);
       });
 
   /**
@@ -166,7 +171,7 @@
    */
   function assertSignedInState(account, numUsers) {
     const signedIn = account !== '';
-    assertEquals(signedIn, dialog.$.cloudprintPromo.hidden);
+    assertEquals(signedIn, dialog.$$('#cloudprintPromo').hidden);
     assertEquals(!signedIn, dialog.$$('.user-info').hidden);
 
     if (numUsers > 0) {
@@ -210,7 +215,7 @@
     // Check that both cloud print promo and dropdown are hidden when
     // cloud print is disabled.
     dialog.cloudPrintDisabled = true;
-    assertTrue(dialog.$.cloudprintPromo.hidden);
+    assertTrue(dialog.$$('#cloudprintPromo').hidden);
     assertTrue(dialog.$$('.user-info').hidden);
     userSelect = dialog.$$('.md-select');
 
@@ -227,7 +232,7 @@
 
     // Simulate signing in to an account.
     destinationStore.setActiveUser(user1);
-    dialog.$.cloudprintPromo.querySelector('[is=\'action-link\']').click();
+    dialog.$$('#cloudprintPromo').querySelector('[is=\'action-link\']').click();
     let addAccount = await nativeLayer.whenCalled('signIn');
 
     assertFalse(addAccount);
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 2a82860..82ac4b7 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,8 +5,17 @@
 'use strict';
 
 suite('<app-management-plugin-vm-detail-view>', function() {
+  /** @enum {number} */
+  const Permissions = {
+    CAMERA: 0,
+    MICROPHONE: 1,
+  };
+
   let pluginVmDetailView;
   let fakeHandler;
+  /** @type {?TestPluginVmBrowserProxy} */
+  let pluginVmBrowserProxy = null;
+  let appId;
 
   function getPermissionBoolByType(permissionType) {
     return app_management.util.getPermissionValueBool(
@@ -23,7 +32,60 @@
     return storeData.apps[storeData.selectedAppId];
   }
 
+  /**
+   * @param {boolean} expectedCameraState
+   * @param {!HTMLElement} cameraToggle
+   * @param {boolean} expectedMicrophoneState
+   * @param {!HTMLElement} microphoneToggle
+   */
+  function assertToggleStates(
+      expectedCameraState, cameraToggle, expectedMicrophoneState,
+      microphoneToggle) {
+    assertEquals(expectedCameraState, cameraToggle.checked);
+    assertEquals(expectedMicrophoneState, microphoneToggle.checked);
+  }
+
+  /**
+   * @param {boolean} expectedCameraState
+   * @param {boolean} expectedMicrophoneState
+   */
+  function assertPermissions(expectedCameraState, expectedMicrophoneState) {
+    assertEquals(
+        expectedCameraState,
+        pluginVmBrowserProxy.permissions[Permissions.CAMERA]);
+    assertEquals(
+        expectedMicrophoneState,
+        pluginVmBrowserProxy.permissions[Permissions.MICROPHONE]);
+  }
+
+  /**
+   * @param {boolean} booleanToChange
+   * @return {OptionalBool}
+   */
+  function booleanToOptionalBool(booleanToChange) {
+    return booleanToChange ? Bool.kTrue : Bool.kFalse;
+  }
+
+  // Syncs the permissions stored in pluginVmBrowserProxy with app management.
+  function syncPermissions() {
+    const app = app_management.Store.getInstance().data.apps[appId];
+    app.permissions[PluginVmPermissionType.CAMERA] =
+        app_management.util.createPermission(
+            PluginVmPermissionType.CAMERA, PermissionValueType.kBool,
+            booleanToOptionalBool(
+                pluginVmBrowserProxy.permissions[Permissions.CAMERA]),
+            /*is_managed=*/ false);
+    app.permissions[PluginVmPermissionType.MICROPHONE] =
+        app_management.util.createPermission(
+            PluginVmPermissionType.MICROPHONE, PermissionValueType.kBool,
+            booleanToOptionalBool(
+                pluginVmBrowserProxy.permissions[Permissions.MICROPHONE]),
+            /*is_managed=*/ false);
+  }
+
   setup(async function() {
+    pluginVmBrowserProxy = new TestPluginVmBrowserProxy();
+    settings.PluginVmBrowserProxyImpl.instance_ = pluginVmBrowserProxy;
     fakeHandler = setupFakeHandler();
     replaceStore();
 
@@ -44,18 +106,25 @@
           false /*is_managed*/);
     }
 
+    pluginVmBrowserProxy.pluginVmRunning = false;
+    pluginVmBrowserProxy.permissions =
+        [/*isCameraEnabled=*/ true, /*isMicrophoneEnabled=*/ true];
+
     // Add an app, and make it the currently selected app.
     const options = {
       type: apps.mojom.AppType.kPluginVm,
       permissions: permissions
     };
     const app = await fakeHandler.addApp(null, options);
+    appId = app.id;
     app_management.Store.getInstance().dispatch(
-        app_management.actions.updateSelectedAppId(app.id));
+        app_management.actions.updateSelectedAppId(appId));
+    syncPermissions();
 
     pluginVmDetailView =
         document.createElement('app-management-plugin-vm-detail-view');
     replaceBody(pluginVmDetailView);
+    await fakeHandler.flushPipesForTesting();
   });
 
   test('App is rendered correctly', function() {
@@ -64,7 +133,7 @@
         pluginVmDetailView.app_.id);
   });
 
-  test('Toggle permissions', async function() {
+  test('Toggle permissions handled by app settings', async function() {
     const checkToggle = async (permissionType) => {
       assertTrue(getPermissionBoolByType(permissionType));
       assertTrue(getPermissionCrToggleByType(pluginVmDetailView, permissionType)
@@ -85,8 +154,6 @@
     };
 
     await checkToggle('PRINTING');
-    await checkToggle('CAMERA');
-    await checkToggle('MICROPHONE');
   });
 
   test('Pin to shelf toggle', async function() {
@@ -113,4 +180,256 @@
         app_management.util.convertOptionalBoolToBool(
             getSelectedAppFromStore().isPinned));
   });
+
+  test('Toggle permissions handled by Plugin Vm', async function() {
+    const camera_toggle = pluginVmDetailView.$$('#camera-permission')
+                              .$$('#toggle-row')
+                              .$$('#toggle');
+    const microphone_toggle = pluginVmDetailView.$$('#microphone-permission')
+                                  .$$('#toggle-row')
+                                  .$$('#toggle');
+    assertToggleStates(
+        /*expectedCameraState=*/ true, camera_toggle,
+        /*expectedMicrophoneState=*/ true, microphone_toggle);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    assertEquals(
+        0,
+        pluginVmBrowserProxy.getCallCount(
+            'wouldPermissionChangeRequireRelaunch'));
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+
+    camera_toggle.click();
+    await fakeHandler.flushPipesForTesting();
+    assertToggleStates(
+        /*expectedCameraState=*/ false, camera_toggle,
+        /*expectedMicrophoneState=*/ true, microphone_toggle);
+    assertPermissions(
+        /*expectedCameraState=*/ false, /*expectedMicrophoneState=*/ true);
+    syncPermissions();
+
+    camera_toggle.click();
+    await fakeHandler.flushPipesForTesting();
+    assertToggleStates(
+        /*expectedCameraState=*/ true, camera_toggle,
+        /*expectedMicrophoneState=*/ true, microphone_toggle);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    syncPermissions();
+
+    microphone_toggle.click();
+    await fakeHandler.flushPipesForTesting();
+    assertToggleStates(
+        /*expectedCameraState=*/ true, camera_toggle,
+        /*expectedMicrophoneState=*/ false, microphone_toggle);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ false);
+    syncPermissions();
+
+    microphone_toggle.click();
+    await fakeHandler.flushPipesForTesting();
+    assertToggleStates(
+        /*expectedCameraState=*/ true, camera_toggle,
+        /*expectedMicrophoneState=*/ true, microphone_toggle);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+
+    assertEquals(
+        4,
+        pluginVmBrowserProxy.getCallCount(
+            'wouldPermissionChangeRequireRelaunch'));
+    assertEquals(4, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+  });
+
+  test('Cancel camera permission dialog', async function() {
+    const camera_toggle = pluginVmDetailView.$$('#camera-permission')
+                              .$$('#toggle-row')
+                              .$$('#toggle');
+    assertFalse(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+    assertTrue(camera_toggle.checked);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    assertEquals(
+        0,
+        pluginVmBrowserProxy.getCallCount(
+            'wouldPermissionChangeRequireRelaunch'));
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    pluginVmBrowserProxy.pluginVmRunning = true;
+
+    camera_toggle.click();
+    await fakeHandler.flushPipesForTesting();
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    assertTrue(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+    const dialog =
+        pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog');
+    assertEquals(
+        dialog.$$('#dialog-text').innerText,
+        loadTimeData.getString('pluginVmPermissionDialogCameraLabel'));
+    assertFalse(camera_toggle.checked);
+    // Assert that the permission did not change.
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    const dialogClosedPromise = test_util.eventToPromise('close', dialog);
+    dialog.$$('#dialogCancelButton').click();
+    await Promise.all(
+        [dialogClosedPromise, fakeHandler.flushPipesForTesting()]);
+    syncPermissions();
+
+    assertEquals(
+        1,
+        pluginVmBrowserProxy.getCallCount(
+            'wouldPermissionChangeRequireRelaunch'));
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    assertTrue(camera_toggle.checked);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    assertFalse(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+  });
+
+  test('Cancel microphone permission dialog', async function() {
+    const microphone_toggle = pluginVmDetailView.$$('#microphone-permission')
+                                  .$$('#toggle-row')
+                                  .$$('#toggle');
+    assertFalse(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+    assertTrue(microphone_toggle.checked);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    assertEquals(
+        0,
+        pluginVmBrowserProxy.getCallCount(
+            'wouldPermissionChangeRequireRelaunch'));
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    pluginVmBrowserProxy.pluginVmRunning = true;
+
+    microphone_toggle.click();
+    await fakeHandler.flushPipesForTesting();
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    assertTrue(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+    const dialog =
+        pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog');
+    assertEquals(
+        loadTimeData.getString('pluginVmPermissionDialogMicrophoneLabel'),
+        dialog.$$('#dialog-text').innerText);
+    assertFalse(microphone_toggle.checked);
+    // Assert that the permission did not change.
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    const dialogClosedPromise = test_util.eventToPromise('close', dialog);
+    dialog.$$('#dialogCancelButton').click();
+    await Promise.all(
+        [dialogClosedPromise, fakeHandler.flushPipesForTesting()]);
+    syncPermissions();
+
+    assertEquals(
+        1,
+        pluginVmBrowserProxy.getCallCount(
+            'wouldPermissionChangeRequireRelaunch'));
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    assertTrue(microphone_toggle.checked);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    assertFalse(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+  });
+
+  test('ok camera permission dialog', async function() {
+    const camera_toggle = pluginVmDetailView.$$('#camera-permission')
+                              .$$('#toggle-row')
+                              .$$('#toggle');
+    assertFalse(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+    assertTrue(camera_toggle.checked);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    assertEquals(
+        0,
+        pluginVmBrowserProxy.getCallCount(
+            'wouldPermissionChangeRequireRelaunch'));
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    pluginVmBrowserProxy.pluginVmRunning = true;
+
+    camera_toggle.click();
+    await fakeHandler.flushPipesForTesting();
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    assertTrue(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+    const dialog =
+        pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog');
+    assertEquals(
+        dialog.$$('#dialog-text').innerText,
+        loadTimeData.getString('pluginVmPermissionDialogCameraLabel'));
+    assertFalse(camera_toggle.checked);
+    // Assert that the permission did not change.
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    const dialogClosedPromise = test_util.eventToPromise('close', dialog);
+    dialog.$$('#dialogOkButton').click();
+    await Promise.all(
+        [dialogClosedPromise, fakeHandler.flushPipesForTesting()]);
+    syncPermissions();
+
+    assertEquals(
+        1,
+        pluginVmBrowserProxy.getCallCount(
+            'wouldPermissionChangeRequireRelaunch'));
+    assertEquals(1, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    assertFalse(camera_toggle.checked);
+    assertPermissions(
+        /*expectedCameraState=*/ false, /*expectedMicrophoneState=*/ true);
+    assertFalse(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+  });
+
+  test('ok microphone permission dialog', async function() {
+    const microphone_toggle = pluginVmDetailView.$$('#microphone-permission')
+                                  .$$('#toggle-row')
+                                  .$$('#toggle');
+    assertFalse(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+    assertTrue(microphone_toggle.checked);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    assertEquals(
+        0,
+        pluginVmBrowserProxy.getCallCount(
+            'wouldPermissionChangeRequireRelaunch'));
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    pluginVmBrowserProxy.pluginVmRunning = true;
+
+    microphone_toggle.click();
+    await fakeHandler.flushPipesForTesting();
+    assertEquals(0, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    assertTrue(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+    const dialog =
+        pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog');
+    assertEquals(
+        loadTimeData.getString('pluginVmPermissionDialogMicrophoneLabel'),
+        dialog.$$('#dialog-text').innerText);
+    assertFalse(microphone_toggle.checked);
+    // Assert that the permission did not change.
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ true);
+    const dialogClosedPromise = test_util.eventToPromise('close', dialog);
+    dialog.$$('#dialogOkButton').click();
+    await Promise.all(
+        [dialogClosedPromise, fakeHandler.flushPipesForTesting()]);
+    syncPermissions();
+
+    assertEquals(
+        1,
+        pluginVmBrowserProxy.getCallCount(
+            'wouldPermissionChangeRequireRelaunch'));
+    assertEquals(1, pluginVmBrowserProxy.getCallCount('setPluginVmPermission'));
+    assertFalse(microphone_toggle.checked);
+    assertPermissions(
+        /*expectedCameraState=*/ true, /*expectedMicrophoneState=*/ false);
+    assertFalse(
+        !!pluginVmDetailView.$$('app-management-plugin-vm-permission-dialog'));
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/app_management/test_plugin_vm_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/app_management/test_plugin_vm_browser_proxy.js
new file mode 100644
index 0000000..c0d7dea
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/app_management/test_plugin_vm_browser_proxy.js
@@ -0,0 +1,45 @@
+// 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.
+
+/** @implements {settings.PluginVmBrowserProxy} */
+class TestPluginVmBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    super([
+      'getPluginVmSharedPathsDisplayText',
+      'removePluginVmSharedPath',
+      'wouldPermissionChangeRequireRelaunch',
+      'setPluginVmPermission',
+    ]);
+    this.pluginVmRunning = false;
+    this.permissions = [true, true];  // [0]Camera, [1]Microphone
+  }
+
+  /** @override */
+  getPluginVmSharedPathsDisplayText(paths) {
+    this.methodCalled('getPluginVmSharedPathsDisplayText', paths);
+    return Promise.resolve(true);
+  }
+
+  /** @override */
+  removePluginVmSharedPath(vmName, path) {
+    this.methodCalled('removePluginVmSharedPath', vmName, path);
+  }
+
+  /** @override */
+  wouldPermissionChangeRequireRelaunch(permissionSetting) {
+    this.methodCalled(
+        'wouldPermissionChangeRequireRelaunch', permissionSetting);
+    return Promise.resolve(
+        permissionSetting.proposedValue !==
+            this.permissions[permissionSetting.permissionType] &&
+        this.pluginVmRunning);
+  }
+
+  /** @override */
+  setPluginVmPermission(permissionSetting) {
+    this.methodCalled('setPluginVmPermission', permissionSetting);
+    this.permissions[permissionSetting.permissionType] =
+        permissionSetting.proposedValue;
+  }
+}
diff --git a/chrome/test/data/webui/settings/chromeos/crostini_page_test.js b/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
index ef51417..ec9107c9 100644
--- a/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
@@ -105,24 +105,17 @@
         showCrostiniDiskResize: true,
       });
 
-      const eventPromise = new Promise((resolve) => {
-                             const v = cr.addWebUIListener(
-                                 'crostini-installer-status-changed', () => {
-                                   resolve(v);
-                                 });
-                           }).then((v) => {
-        assertTrue(cr.removeWebUIListener(v));
-      });
-
+      console.log('Navigating');
       settings.Router.getInstance().navigateTo(settings.routes.CROSTINI);
+      console.log('Clicking');
       crostiniPage.$$('#crostini').click();
 
-      const pageLoadPromise = test_util.flushTasks().then(() => {
-        subpage = crostiniPage.$$('settings-crostini-subpage');
-        assertTrue(!!subpage);
-      });
+      console.log('Flushing page load');
+      await test_util.flushTasks();
+      subpage = crostiniPage.$$('settings-crostini-subpage');
+      assertTrue(!!subpage);
+      console.log('pageLoadPromise done');
 
-      await Promise.all([pageLoadPromise, eventPromise]);
     });
 
     suite('SubPageDefault', function() {
@@ -187,6 +180,14 @@
         assertFalse(button.disabled);
       });
 
+      test('InstallerStatusQueriedOnAttach', async function() {
+        // We navigated the page during setup, so this request should've been
+        // triggered by here.
+        assertTrue(
+            crostiniBrowserProxy.getCallCount(
+                'requestCrostiniInstallerStatus') >= 1);
+      });
+
       test('Export', async function() {
         assertTrue(!!subpage.$$('#crostini-export-import'));
         subpage.$$('#crostini-export-import').click();
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index c89a20f..5cc375d 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -403,6 +403,8 @@
   /** @override */
   get extraLibraries() {
     return super.extraLibraries.concat([
+      BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
+      'app_management/test_plugin_vm_browser_proxy.js',
       'app_management/plugin_vm_detail_view_test.js',
     ]);
   }
diff --git a/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js
index d0520c46..7d39aa2 100644
--- a/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js
@@ -31,6 +31,7 @@
       'shutdownCrostini',
       'setCrostiniMicSharingEnabled',
       'getCrostiniMicSharingEnabled',
+      'requestCrostiniInstallerStatus',
     ]);
     this.sharedUsbDevices = [];
     this.removeSharedPathResult = true;
@@ -124,6 +125,7 @@
 
   /** @override */
   requestCrostiniInstallerStatus() {
+    this.methodCalled('requestCrostiniInstallerStatus');
     cr.webUIListenerCallback('crostini-installer-status-changed', false);
   }
 
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn
index cff64f0..2f4784cd 100644
--- a/chrome/updater/mac/BUILD.gn
+++ b/chrome/updater/mac/BUILD.gn
@@ -8,7 +8,7 @@
 
 group("mac") {
   deps = [
-    ":install_app",
+    ":app_install",
     ":updater_bundle",
     ":updater_install_script",
     "//chrome/updater/mac/signing",
@@ -53,7 +53,7 @@
 
   sources = [ "main.cc" ]
   deps = [
-    ":install_app",
+    ":app_install",
     ":network_fetcher_sources",
     ":updater_setup_sources",
     "//chrome/updater:lib",
@@ -102,12 +102,12 @@
   ]
 }
 
-source_set("install_app") {
+source_set("app_install") {
   sources = [
+    "setup/app_install.cc",
+    "setup/app_install.h",
     "setup/app_swap.cc",
     "setup/app_swap.h",
-    "setup/install_app.cc",
-    "setup/install_app.h",
   ]
 
   deps = [
@@ -158,7 +158,7 @@
   data = [ "//chrome/test/data/updater/updater_setup_test_dmg.dmg" ]
 
   deps = [
-    ":install_app",
+    ":app_install",
     ":installer_sources",
     ":updater_setup_sources",
     "//base",
diff --git a/chrome/updater/mac/setup/install_app.cc b/chrome/updater/mac/setup/app_install.cc
similarity index 96%
rename from chrome/updater/mac/setup/install_app.cc
rename to chrome/updater/mac/setup/app_install.cc
index 3663ddd..6e8d1f66 100644
--- a/chrome/updater/mac/setup/install_app.cc
+++ b/chrome/updater/mac/setup/app_install.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/updater/mac/setup/install_app.h"
+#include "chrome/updater/mac/setup/app_install.h"
 
 #include "base/bind.h"
 #include "base/memory/ref_counted.h"
diff --git a/chrome/updater/mac/setup/install_app.h b/chrome/updater/mac/setup/app_install.h
similarity index 67%
rename from chrome/updater/mac/setup/install_app.h
rename to chrome/updater/mac/setup/app_install.h
index 88a03de1..8bfb65e 100644
--- a/chrome/updater/mac/setup/install_app.h
+++ b/chrome/updater/mac/setup/app_install.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_UPDATER_MAC_SETUP_INSTALL_APP_H_
-#define CHROME_UPDATER_MAC_SETUP_INSTALL_APP_H_
+#ifndef CHROME_UPDATER_MAC_SETUP_APP_INSTALL_H_
+#define CHROME_UPDATER_MAC_SETUP_APP_INSTALL_H_
 
 #include "base/memory/scoped_refptr.h"
 
@@ -15,4 +15,4 @@
 
 }  // namespace updater
 
-#endif  // CHROME_UPDATER_MAC_SETUP_INSTALL_APP_H_
+#endif  // CHROME_UPDATER_MAC_SETUP_APP_INSTALL_H_
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index c4f28d4..fa35d09 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -23,13 +23,13 @@
 #if defined(OS_WIN)
 #include "chrome/updater/app/server/win/server.h"
 #include "chrome/updater/app/server/win/service_main.h"
-#include "chrome/updater/win/install_app.h"
+#include "chrome/updater/win/app_install.h"
 #endif
 
 #if defined(OS_MACOSX)
 #include "chrome/updater/app/server/mac/server.h"
+#include "chrome/updater/mac/setup/app_install.h"
 #include "chrome/updater/mac/setup/app_swap.h"
-#include "chrome/updater/mac/setup/install_app.h"
 #endif
 
 // Instructions For Windows.
diff --git a/chrome/updater/win/BUILD.gn b/chrome/updater/win/BUILD.gn
index 9d94ee1..c27c5c4 100644
--- a/chrome/updater/win/BUILD.gn
+++ b/chrome/updater/win/BUILD.gn
@@ -24,7 +24,7 @@
   configs += [ "//build/config/win:windowed" ]
 
   deps = [
-    ":install_app",
+    ":app_install",
     ":lib",
     ":version_resources",
     "//build/win:default_exe_manifest",
@@ -131,10 +131,10 @@
 # dependency of ATL in the UI so ATL headers are not visible in the
 # compilation units outside the UI itself.
 # TODO(sorin): https://crbug.com/1014311
-source_set("install_app") {
+source_set("app_install") {
   sources = [
-    "install_app.cc",
-    "install_app.h",
+    "app_install.cc",
+    "app_install.h",
   ]
 
   deps = [
@@ -164,8 +164,8 @@
   ]
 
   deps = [
+    ":app_install",
     ":constants",
-    ":install_app",
     ":lib",
     "//base/test:test_support",
     "//testing/gtest",
@@ -189,7 +189,7 @@
   ]
 
   deps = [
-    ":install_app",
+    ":app_install",
     ":lib",
     "//base",
     "//base/test:test_support",
diff --git a/chrome/updater/win/install_app.cc b/chrome/updater/win/app_install.cc
similarity index 94%
rename from chrome/updater/win/install_app.cc
rename to chrome/updater/win/app_install.cc
index fb66e24..e19b4a8d 100644
--- a/chrome/updater/win/install_app.cc
+++ b/chrome/updater/win/app_install.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/updater/win/install_app.h"
+#include "chrome/updater/win/app_install.h"
 
 #include <memory>
 #include <string>
@@ -60,7 +60,7 @@
 // https://crbug.com/1014298
 constexpr base::char16 kAppNameChrome[] = L"Google Chrome";
 
-class InstallAppController;
+class AppInstallController;
 
 scoped_refptr<UpdateService> CreateUpdateService(
     scoped_refptr<update_client::Configurator> config) {
@@ -395,23 +395,23 @@
 // posts a reply to the main thread, which makes the main thread exit its run
 // loop, and then the main thread returns to the destructor of this class,
 // and destructs its class members.
-class InstallAppController
-    : public base::RefCountedThreadSafe<InstallAppController>,
+class AppInstallController
+    : public base::RefCountedThreadSafe<AppInstallController>,
       public ui::ProgressWndEvents,
       public WTL::CMessageFilter {
  public:
-  explicit InstallAppController(scoped_refptr<Configurator> configurator);
+  explicit AppInstallController(scoped_refptr<Configurator> configurator);
 
-  InstallAppController(const InstallAppController&) = delete;
-  InstallAppController& operator=(const InstallAppController&) = delete;
+  AppInstallController(const AppInstallController&) = delete;
+  AppInstallController& operator=(const AppInstallController&) = delete;
 
   void InstallApp(const std::string& app_id,
                   base::OnceCallback<void(int)> callback);
 
  private:
-  friend class base::RefCountedThreadSafe<InstallAppController>;
+  friend class base::RefCountedThreadSafe<AppInstallController>;
 
-  ~InstallAppController() override;
+  ~AppInstallController() override;
 
   // Overrides for OmahaWndEvents. These functions are called on the UI thread.
   void DoClose() override {}
@@ -483,7 +483,7 @@
 
 // TODO(sorin): fix the hardcoding of the application name.
 // https:crbug.com/1014298
-InstallAppController::InstallAppController(
+AppInstallController::AppInstallController(
     scoped_refptr<Configurator> configurator)
     : main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
       ui_task_runner_(base::ThreadPool::CreateSingleThreadTaskRunner(
@@ -494,9 +494,9 @@
       config_(configurator),
       persisted_data_(
           base::MakeRefCounted<PersistedData>(config_->GetPrefService())) {}
-InstallAppController::~InstallAppController() = default;
+AppInstallController::~AppInstallController() = default;
 
-void InstallAppController::InstallApp(const std::string& app_id,
+void AppInstallController::InstallApp(const std::string& app_id,
                                       base::OnceCallback<void(int)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(base::ThreadTaskRunnerHandle::IsSet());
@@ -505,11 +505,11 @@
   callback_ = std::move(callback);
 
   ui_task_runner_->PostTaskAndReply(
-      FROM_HERE, base::BindOnce(&InstallAppController::InitializeUI, this),
-      base::BindOnce(&InstallAppController::DoInstallApp, this));
+      FROM_HERE, base::BindOnce(&AppInstallController::InitializeUI, this),
+      base::BindOnce(&AppInstallController::DoInstallApp, this));
 }
 
-void InstallAppController::DoInstallApp() {
+void AppInstallController::DoInstallApp() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // At this point, the UI has been initialized, which means the UI can be
@@ -517,7 +517,7 @@
   // below runs the UI message loop for the UI until it exits, because
   // a WM_QUIT message has been posted to it.
   ui_task_runner_->PostTask(FROM_HERE,
-                            base::BindOnce(&InstallAppController::RunUI, this));
+                            base::BindOnce(&AppInstallController::RunUI, this));
 
   update_service_ = CreateUpdateService(config_);
 
@@ -526,17 +526,17 @@
 
   update_service_->Update(
       app_id_, UpdateService::Priority::kForeground,
-      base::BindRepeating(&InstallAppController::StateChange, this),
-      base::BindOnce(&InstallAppController::InstallComplete, this));
+      base::BindRepeating(&AppInstallController::StateChange, this),
+      base::BindOnce(&AppInstallController::InstallComplete, this));
 }
 
-void InstallAppController::InstallComplete(UpdateService::Result result) {
+void AppInstallController::InstallComplete(UpdateService::Result result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   PrefsCommit();
   update_service_ = nullptr;
 }
 
-void InstallAppController::StateChange(
+void AppInstallController::StateChange(
     UpdateService::UpdateState update_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(install_progress_observer_ipc_);
@@ -587,7 +587,7 @@
   }
 }
 
-void InstallAppController::HandleInstallResult(
+void AppInstallController::HandleInstallResult(
     const UpdateService::UpdateState& update_state) {
   CompletionCodes completion_code = CompletionCodes::COMPLETION_CODE_ERROR;
   base::string16 completion_text;
@@ -625,7 +625,7 @@
 
 // Creates and shows the progress window. The window has thread affinity. It
 // must be created, process its messages, and be destroyed on the same thread.
-void InstallAppController::InitializeUI() {
+void AppInstallController::InitializeUI() {
   DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
 
   base::ScopedDisallowBlocking no_blocking_allowed_on_ui_thread;
@@ -639,7 +639,7 @@
   progress_wnd_->Show();
 }
 
-void InstallAppController::RunUI() {
+void AppInstallController::RunUI() {
   DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
   DCHECK_EQ(GetUIThreadID(), GetCurrentThreadId());
 
@@ -653,12 +653,12 @@
                               base::BindOnce(std::move(callback_), 0));
 }
 
-void InstallAppController::DoExit() {
+void AppInstallController::DoExit() {
   DCHECK_EQ(GetUIThreadID(), GetCurrentThreadId());
   PostThreadMessage(GetCurrentThreadId(), WM_QUIT, 0, 0);
 }
 
-BOOL InstallAppController::PreTranslateMessage(MSG* msg) {
+BOOL AppInstallController::PreTranslateMessage(MSG* msg) {
   DCHECK_EQ(GetUIThreadID(), GetCurrentThreadId());
   if (msg->message == InstallProgressObserverIPC::WM_PROGRESS_WINDOW_IPC) {
     install_progress_observer_ipc_->Invoke(msg->wParam, msg->lParam);
@@ -667,12 +667,12 @@
   return false;
 }
 
-void InstallAppController::PrefsCommit() {
+void AppInstallController::PrefsCommit() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   config_->GetPrefService()->CommitPendingWrite();
 }
 
-DWORD InstallAppController::GetUIThreadID() const {
+DWORD AppInstallController::GetUIThreadID() const {
   DCHECK(progress_wnd_);
   return ::GetWindowThreadProcessId(progress_wnd_->m_hWnd, nullptr);
 }
@@ -694,7 +694,7 @@
   void SetupDone(int result);
 
   scoped_refptr<Configurator> config_;
-  scoped_refptr<InstallAppController> app_install_controller_;
+  scoped_refptr<AppInstallController> app_install_controller_;
 
   // The splash screen has a fading effect. That means that the splash screen
   // needs to be alive for a while, until the fading effect is over.
@@ -747,7 +747,7 @@
     return;
   }
 
-  app_install_controller_ = base::MakeRefCounted<InstallAppController>(config_);
+  app_install_controller_ = base::MakeRefCounted<AppInstallController>(config_);
   app_install_controller_->InstallApp(
       app_id, base::BindOnce(&AppInstall::Shutdown, this));
 }
diff --git a/chrome/updater/win/install_app.h b/chrome/updater/win/app_install.h
similarity index 71%
rename from chrome/updater/win/install_app.h
rename to chrome/updater/win/app_install.h
index 581261f..061f008 100644
--- a/chrome/updater/win/install_app.h
+++ b/chrome/updater/win/app_install.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_UPDATER_WIN_INSTALL_APP_H_
-#define CHROME_UPDATER_WIN_INSTALL_APP_H_
+#ifndef CHROME_UPDATER_WIN_APP_INSTALL_H_
+#define CHROME_UPDATER_WIN_APP_INSTALL_H_
 
 #include <string>
 
@@ -17,4 +17,4 @@
 
 }  // namespace updater
 
-#endif  // CHROME_UPDATER_WIN_INSTALL_APP_H_
+#endif  // CHROME_UPDATER_WIN_APP_INSTALL_H_
diff --git a/chrome/updater/win/test/BUILD.gn b/chrome/updater/win/test/BUILD.gn
index 1c06760..dd4215c 100644
--- a/chrome/updater/win/test/BUILD.gn
+++ b/chrome/updater/win/test/BUILD.gn
@@ -26,7 +26,7 @@
   deps = [
     "//base",
     "//chrome/updater:base",
-    "//chrome/updater/win:install_app",
+    "//chrome/updater/win:app_install",
     "//chrome/updater/win:lib",
   ]
 }
diff --git a/chromeos/components/camera_app_ui/camera_app_helper.mojom b/chromeos/components/camera_app_ui/camera_app_helper.mojom
index 7e6004c..73605b72f 100644
--- a/chromeos/components/camera_app_ui/camera_app_helper.mojom
+++ b/chromeos/components/camera_app_ui/camera_app_helper.mojom
@@ -14,6 +14,22 @@
   Update(bool is_tablet_mode);
 };
 
+// Screen backlight state.
+// Reference from ash/public/cpp/screen_backlight_type.h
+enum ScreenState {
+    ON,
+    OFF,
+    OFF_AUTO,
+};
+
+// Interface for monitoring screen state of device. The state is detected from
+// Chrome browser process and is notified to Chrome Camera App in renderer
+// process.
+interface ScreenStateMonitor {
+  // Updates with the latest changed screen state.
+  Update(ScreenState state);
+};
+
 // Interface for communication between Chrome Camera App (Remote) and Chrome
 // (Receiver).
 interface CameraAppHelper {
@@ -35,9 +51,14 @@
   // Triggers the end of event tracing for given |event|.
   StopPerfEventTrace(string event);
 
-  // Registers an TabletModeMonitor instance and returns the tablet mode
+  // Registers a TabletModeMonitor instance and returns the tablet mode
   // initial state. Calling the Update() whenever the tablet mode state
   // changes.
   SetTabletMonitor(pending_remote<TabletModeMonitor> monitor)
       => (bool is_tablet_mode);
+
+  // Registers a ScreenStateMonitor instance and returns the initial screen
+  // state. Calling the Update() whenever the screen state changes.
+  SetScreenStateMonitor(pending_remote<ScreenStateMonitor> monitor)
+      => (ScreenState initial_state);
 };
diff --git a/chromeos/components/camera_app_ui/camera_app_helper_impl.cc b/chromeos/components/camera_app_ui/camera_app_helper_impl.cc
index 80d3814..912506c 100644
--- a/chromeos/components/camera_app_ui/camera_app_helper_impl.cc
+++ b/chromeos/components/camera_app_ui/camera_app_helper_impl.cc
@@ -12,6 +12,22 @@
 #include "ui/aura/window.h"
 
 namespace chromeos_camera {
+namespace {
+
+mojom::ScreenState ToMojoScreenState(ash::ScreenState s) {
+  switch (s) {
+    case ash::ScreenState::ON:
+      return mojom::ScreenState::ON;
+    case ash::ScreenState::OFF:
+      return mojom::ScreenState::OFF;
+    case ash::ScreenState::OFF_AUTO:
+      return mojom::ScreenState::OFF_AUTO;
+    default:
+      NOTREACHED();
+  }
+}
+
+}  // namespace
 
 CameraAppHelperImpl::CameraAppHelperImpl(
     CameraResultCallback camera_result_callback,
@@ -20,10 +36,12 @@
   DCHECK(window);
   window->SetProperty(ash::kCanConsumeSystemKeysKey, true);
   ash::TabletMode::Get()->AddObserver(this);
+  ash::ScreenBacklight::Get()->AddObserver(this);
 }
 
 CameraAppHelperImpl::~CameraAppHelperImpl() {
   ash::TabletMode::Get()->RemoveObserver(this);
+  ash::ScreenBacklight::Get()->RemoveObserver(this);
 }
 
 void CameraAppHelperImpl::HandleCameraResult(
@@ -49,18 +67,32 @@
 void CameraAppHelperImpl::SetTabletMonitor(
     mojo::PendingRemote<TabletModeMonitor> monitor,
     SetTabletMonitorCallback callback) {
-  monitor_ = mojo::Remote<TabletModeMonitor>(std::move(monitor));
+  tablet_monitor_ = mojo::Remote<TabletModeMonitor>(std::move(monitor));
   std::move(callback).Run(ash::TabletMode::Get()->InTabletMode());
 }
 
+void CameraAppHelperImpl::SetScreenStateMonitor(
+    mojo::PendingRemote<ScreenStateMonitor> monitor,
+    SetScreenStateMonitorCallback callback) {
+  screen_state_monitor_ = mojo::Remote<ScreenStateMonitor>(std::move(monitor));
+  auto&& mojo_state =
+      ToMojoScreenState(ash::ScreenBacklight::Get()->GetScreenState());
+  std::move(callback).Run(mojo_state);
+}
+
 void CameraAppHelperImpl::OnTabletModeStarted() {
-  if (monitor_.is_bound())
-    monitor_->Update(true);
+  if (tablet_monitor_.is_bound())
+    tablet_monitor_->Update(true);
 }
 
 void CameraAppHelperImpl::OnTabletModeEnded() {
-  if (monitor_.is_bound())
-    monitor_->Update(false);
+  if (tablet_monitor_.is_bound())
+    tablet_monitor_->Update(false);
+}
+
+void CameraAppHelperImpl::OnScreenStateChanged(ash::ScreenState screen_state) {
+  if (screen_state_monitor_.is_bound())
+    screen_state_monitor_->Update(ToMojoScreenState(screen_state));
 }
 
 }  // namespace chromeos_camera
diff --git a/chromeos/components/camera_app_ui/camera_app_helper_impl.h b/chromeos/components/camera_app_ui/camera_app_helper_impl.h
index b5d6eab..e4c5588 100644
--- a/chromeos/components/camera_app_ui/camera_app_helper_impl.h
+++ b/chromeos/components/camera_app_ui/camera_app_helper_impl.h
@@ -7,6 +7,7 @@
 
 #include <vector>
 
+#include "ash/public/cpp/screen_backlight.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
 #include "base/macros.h"
 #include "chromeos/components/camera_app_ui/camera_app_helper.mojom.h"
@@ -19,6 +20,7 @@
 namespace chromeos_camera {
 
 class CameraAppHelperImpl : public ash::TabletModeObserver,
+                            public ash::ScreenBacklightObserver,
                             public mojom::CameraAppHelper {
  public:
   using CameraResultCallback =
@@ -27,6 +29,7 @@
                                    const std::vector<uint8_t>&,
                                    HandleCameraResultCallback)>;
   using TabletModeMonitor = mojom::TabletModeMonitor;
+  using ScreenStateMonitor = mojom::ScreenStateMonitor;
 
   CameraAppHelperImpl(CameraResultCallback camera_result_callback,
                       aura::Window* window);
@@ -42,15 +45,21 @@
   void StopPerfEventTrace(const std::string& event) override;
   void SetTabletMonitor(mojo::PendingRemote<TabletModeMonitor> monitor,
                         SetTabletMonitorCallback callback) override;
+  void SetScreenStateMonitor(mojo::PendingRemote<ScreenStateMonitor> monitor,
+                             SetScreenStateMonitorCallback callback) override;
 
  private:
   // ash::TabletModeObserver overrides;
   void OnTabletModeStarted() override;
   void OnTabletModeEnded() override;
 
+  // ash::ScreenBacklightObserver overrides;
+  void OnScreenStateChanged(ash::ScreenState screen_state) override;
+
   CameraResultCallback camera_result_callback_;
 
-  mojo::Remote<TabletModeMonitor> monitor_;
+  mojo::Remote<TabletModeMonitor> tablet_monitor_;
+  mojo::Remote<ScreenStateMonitor> screen_state_monitor_;
 
   DISALLOW_COPY_AND_ASSIGN(CameraAppHelperImpl);
 };
diff --git a/chromeos/components/camera_app_ui/resources/src/js/mojo/chrome_helper.js b/chromeos/components/camera_app_ui/resources/src/js/mojo/chrome_helper.js
index 7e28f4b..853fd83 100644
--- a/chromeos/components/camera_app_ui/resources/src/js/mojo/chrome_helper.js
+++ b/chromeos/components/camera_app_ui/resources/src/js/mojo/chrome_helper.js
@@ -43,6 +43,24 @@
   }
 
   /**
+   * Starts monitor monitoring system screen state of device.
+   * @param {function(chromeosCamera.mojom.ScreenState)} onChange Callback
+   *     called each time when device screen state changes with parameter of
+   *     newly changed value.
+   * @return {!Promise<chromeosCamera.mojom.ScreenState>} Resolved to initial
+   *     system screen state.
+   */
+  async initScreenStateMonitor(onChange) {
+    const monitorCallbackRouter =
+        new chromeosCamera.mojom.ScreenStateMonitorCallbackRouter();
+    monitorCallbackRouter.update.addListener(onChange);
+
+    return (await this.remote_.setScreenStateMonitor(
+                monitorCallbackRouter.$.bindNewPipeAndPassRemote()))
+        .initialState;
+  }
+
+  /**
    * Checks if the device is under tablet mode currently.
    * @return {!Promise<boolean>}
    */
diff --git a/chromeos/components/camera_app_ui/resources/src/js/state.js b/chromeos/components/camera_app_ui/resources/src/js/state.js
index 2724680..aa38124 100644
--- a/chromeos/components/camera_app_ui/resources/src/js/state.js
+++ b/chromeos/components/camera_app_ui/resources/src/js/state.js
@@ -44,6 +44,7 @@
   SHOULD_HANDLE_INTENT_RESULT: 'should-handle-intent-result',
   SHOW_METADATA: 'show-metadata',
   SQUARE_PREVIEW: 'square-preview',
+  SCREEN_OFF_AUTO: 'screen-off-auto',
   STREAMING: 'streaming',
   SUSPEND: 'suspend',
   TABLET: 'tablet',
diff --git a/chromeos/components/camera_app_ui/resources/src/js/views/camera.js b/chromeos/components/camera_app_ui/resources/src/js/views/camera.js
index 95064c9..fc41406 100644
--- a/chromeos/components/camera_app_ui/resources/src/js/views/camera.js
+++ b/chromeos/components/camera_app_ui/resources/src/js/views/camera.js
@@ -256,6 +256,8 @@
       }
     });
 
+    state.addObserver(state.State.SCREEN_OFF_AUTO, () => this.start());
+
     this.configuring_ = null;
   }
 
@@ -264,10 +266,18 @@
    * @return {!Promise}
    */
   async initialize() {
+    const helper = await ChromeHelper.getInstance();
+
     const setTablet = (isTablet) => state.set(state.State.TABLET, isTablet);
-    const isTablet =
-        await ChromeHelper.getInstance().initTabletModeMonitor(setTablet);
+    const isTablet = await helper.initTabletModeMonitor(setTablet);
     setTablet(isTablet);
+
+    const setScreenOffAuto = (s) => {
+      const offAuto = s === chromeosCamera.mojom.ScreenState.OFF_AUTO;
+      state.set(state.State.SCREEN_OFF_AUTO, offAuto);
+    };
+    const screenState = await helper.initScreenStateMonitor(setScreenOffAuto);
+    setScreenOffAuto(screenState);
   }
 
   /**
@@ -293,7 +303,8 @@
    */
   isSuspended() {
     return this.locked_ || chrome.app.window.current().isMinimized() ||
-        state.get(state.State.SUSPEND) || this.isTabletBackground_();
+        state.get(state.State.SUSPEND) ||
+        state.get(state.State.SCREEN_OFF_AUTO) || this.isTabletBackground_();
   }
 
   /**
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 298f858..5a7883aa 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -290,6 +290,11 @@
 const base::Feature kNewOsSettingsSearch{"NewOsSettingsSearch",
                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables a unique URL for each path in CrOS settings.
+// This allows deep linking to individual settings, i.e. in settings search.
+const base::Feature kOsSettingsDeepLinking{"OsSettingsDeepLinking",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether to enable the Parental Controls section of settings.
 const base::Feature kParentalControlsSettings{
     "ChromeOSParentalControlsSettings", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index f4ab2fa..7069649 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -129,6 +129,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kOobeScreensPriority;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kOsSettingsDeepLinking;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kParentalControlsSettings;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kPluginVmShowCameraPermissions;
diff --git a/chromeos/lacros/browser/lacros_chrome_service_impl.cc b/chromeos/lacros/browser/lacros_chrome_service_impl.cc
index 18cb027..479720b 100644
--- a/chromeos/lacros/browser/lacros_chrome_service_impl.cc
+++ b/chromeos/lacros/browser/lacros_chrome_service_impl.cc
@@ -23,7 +23,8 @@
 LacrosChromeServiceImpl::LacrosChromeServiceImpl()
     : pending_ash_chrome_service_receiver_(
           ash_chrome_service_.BindNewPipeAndPassReceiver()) {
-  // Bind remote interfaces in ash-chrome.
+  // Bind remote interfaces in ash-chrome. These remote interfaces can be used
+  // immediately. Outgoing calls will be queued.
   ash_chrome_service_->BindSelectFile(
       select_file_remote_.BindNewPipeAndPassReceiver());
 
@@ -36,6 +37,11 @@
   g_instance = nullptr;
 }
 
+void LacrosChromeServiceImpl::BindReceiver(
+    mojo::PendingReceiver<lacros::mojom::LacrosChromeService> receiver) {
+  receiver_.Bind(std::move(receiver));
+}
+
 void LacrosChromeServiceImpl::RequestAshChromeServiceReceiver(
     RequestAshChromeServiceReceiverCallback callback) {
   // TODO(hidehiko): Remove non-error logging from here.
diff --git a/chromeos/lacros/browser/lacros_chrome_service_impl.h b/chromeos/lacros/browser/lacros_chrome_service_impl.h
index f7d2309c..39d088f 100644
--- a/chromeos/lacros/browser/lacros_chrome_service_impl.h
+++ b/chromeos/lacros/browser/lacros_chrome_service_impl.h
@@ -8,6 +8,8 @@
 #include "base/component_export.h"
 #include "chromeos/lacros/mojom/lacros.mojom.h"
 #include "chromeos/lacros/mojom/select_file.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
@@ -22,6 +24,9 @@
   LacrosChromeServiceImpl();
   ~LacrosChromeServiceImpl() override;
 
+  void BindReceiver(
+      mojo::PendingReceiver<lacros::mojom::LacrosChromeService> receiver);
+
   mojo::Remote<lacros::mojom::SelectFile>& select_file_remote() {
     return select_file_remote_;
   }
@@ -31,6 +36,8 @@
       RequestAshChromeServiceReceiverCallback callback) override;
 
  private:
+  mojo::Receiver<lacros::mojom::LacrosChromeService> receiver_{this};
+
   // Proxy to AshChromeService in ash-chrome.
   mojo::Remote<lacros::mojom::AshChromeService> ash_chrome_service_;
 
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 21add9fa..1de41cc 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -290,6 +290,7 @@
       "//components/ukm/content:unit_tests",
       "//components/visitedlink/test:unit_tests",
       "//components/web_cache/browser:unit_tests",
+      "//components/web_package:unit_tests",
       "//components/webcrypto:unit_tests",
     ]
 
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
index 2a487d3..e3686b9 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
@@ -5,6 +5,8 @@
 package org.chromium.net.urlconnection;
 
 import android.annotation.SuppressLint;
+import android.net.TrafficStats;
+import android.os.Build;
 import android.util.Log;
 import android.util.Pair;
 
@@ -297,10 +299,10 @@
         }
         // Set HTTP method.
         requestBuilder.setHttpMethod(method);
-        if (mTrafficStatsTagSet) {
+        if (checkTrafficStatsTag()) {
             requestBuilder.setTrafficStatsTag(mTrafficStatsTag);
         }
-        if (mTrafficStatsUidSet) {
+        if (checkTrafficStatsUid()) {
             requestBuilder.setTrafficStatsUid(mTrafficStatsUid);
         }
 
@@ -310,6 +312,39 @@
         connected = true;
     }
 
+    private boolean checkTrafficStatsTag() {
+        if (mTrafficStatsTagSet) {
+            return true;
+        }
+
+        int tag = TrafficStats.getThreadStatsTag();
+        if (tag != -1) {
+            mTrafficStatsTag = tag;
+            mTrafficStatsTagSet = true;
+        }
+
+        return mTrafficStatsTagSet;
+    }
+
+    private boolean checkTrafficStatsUid() {
+        if (mTrafficStatsUidSet) {
+            return true;
+        }
+
+        // TrafficStats#getThreadStatsUid() is available on API level 28+.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
+            return false;
+        }
+
+        int uid = TrafficStats.getThreadStatsUid();
+        if (uid != -1) {
+            mTrafficStatsUid = uid;
+            mTrafficStatsUidSet = true;
+        }
+
+        return mTrafficStatsUidSet;
+    }
+
     /**
      * Returns an input stream from the server in the case of an error such as
      * the requested file has not been found on the remote server.
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java
index 4e43825..24db14d 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetHttpURLConnectionTest.java
@@ -12,6 +12,7 @@
 
 import static org.chromium.net.CronetTestRule.getContext;
 
+import android.net.TrafficStats;
 import android.os.Build;
 import android.os.Process;
 import android.support.test.runner.AndroidJUnit4;
@@ -1397,6 +1398,15 @@
         urlConnection.disconnect();
         assertTrue(CronetTestUtil.nativeGetTaggedBytes(tag) > priorBytes);
 
+        // Test tagging with TrafficStats.
+        tag = 0x12348765;
+        priorBytes = CronetTestUtil.nativeGetTaggedBytes(tag);
+        urlConnection = (CronetHttpURLConnection) url.openConnection();
+        TrafficStats.setThreadStatsTag(tag);
+        assertEquals(200, urlConnection.getResponseCode());
+        urlConnection.disconnect();
+        assertTrue(CronetTestUtil.nativeGetTaggedBytes(tag) > priorBytes);
+
         // Test tagging with our UID.
         // NOTE(pauljensen): Explicitly setting the UID to the current UID isn't a particularly
         // thorough test of this API but at least provides coverage of the underlying code, and
@@ -1413,6 +1423,20 @@
         assertEquals(200, urlConnection.getResponseCode());
         urlConnection.disconnect();
         assertTrue(CronetTestUtil.nativeGetTaggedBytes(tag) > priorBytes);
+
+        // TrafficStats.getThreadStatsUid() which is required for this feature is added in API level
+        // 28.
+        // Note, currently this part won't run as CronetTestUtil.nativeCanGetTaggedBytes() will
+        // return false on P+.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            tag = 0;
+            priorBytes = CronetTestUtil.nativeGetTaggedBytes(tag);
+            urlConnection = (CronetHttpURLConnection) url.openConnection();
+            TrafficStats.setThreadStatsUid(Process.myUid());
+            assertEquals(200, urlConnection.getResponseCode());
+            urlConnection.disconnect();
+            assertTrue(CronetTestUtil.nativeGetTaggedBytes(tag) > priorBytes);
+        }
     }
 
     @Test
diff --git a/components/cronet/cronet_url_request_context.cc b/components/cronet/cronet_url_request_context.cc
index 38bfa48..e5eef67 100644
--- a/components/cronet/cronet_url_request_context.cc
+++ b/components/cronet/cronet_url_request_context.cc
@@ -676,7 +676,7 @@
   if (!net_log_file_observer_)
     return;
   net_log_file_observer_->StopObserving(
-      GetNetLogInfo(),
+      base::Value::ToUniquePtrValue(GetNetLogInfo()),
       base::BindOnce(
           &CronetURLRequestContext::NetworkTasks::StopNetLogCompleted,
           base::Unretained(this)));
@@ -688,13 +688,12 @@
   callback_->OnStopNetLogCompleted();
 }
 
-std::unique_ptr<base::DictionaryValue>
-CronetURLRequestContext::NetworkTasks::GetNetLogInfo() const {
-  std::unique_ptr<base::DictionaryValue> net_info =
+base::Value CronetURLRequestContext::NetworkTasks::GetNetLogInfo() const {
+  base::Value net_info =
       net::GetNetInfo(context_.get(), net::NET_INFO_ALL_SOURCES);
   if (effective_experimental_options_) {
-    net_info->Set("cronetExperimentalParams",
-                  effective_experimental_options_->CreateDeepCopy());
+    net_info.SetKey("cronetExperimentalParams",
+                    effective_experimental_options_->Clone());
   }
   return net_info;
 }
diff --git a/components/cronet/cronet_url_request_context.h b/components/cronet/cronet_url_request_context.h
index eab25b5..f277b96 100644
--- a/components/cronet/cronet_url_request_context.h
+++ b/components/cronet/cronet_url_request_context.h
@@ -242,7 +242,7 @@
 
    private:
     friend class TestUtil;
-    std::unique_ptr<base::DictionaryValue> GetNetLogInfo() const;
+    base::Value GetNetLogInfo() const;
 
     std::unique_ptr<net::FileNetLogObserver> net_log_file_observer_;
 
diff --git a/components/cronet/ios/cronet_environment.h b/components/cronet/ios/cronet_environment.h
index 3717a4ff..0951567 100644
--- a/components/cronet/ios/cronet_environment.h
+++ b/components/cronet/ios/cronet_environment.h
@@ -156,7 +156,7 @@
   void StartNetLogOnNetworkThread(const base::FilePath&, bool log_bytes);
   void StopNetLogOnNetworkThread(base::WaitableEvent* log_stopped_event);
 
-  std::unique_ptr<base::DictionaryValue> GetNetLogInfo() const;
+  base::Value GetNetLogInfo() const;
 
   // Returns the HttpNetworkSession object from the passed in
   // URLRequestContext or NULL if none exists.
diff --git a/components/cronet/ios/cronet_environment.mm b/components/cronet/ios/cronet_environment.mm
index 6229ece..0d28157 100644
--- a/components/cronet/ios/cronet_environment.mm
+++ b/components/cronet/ios/cronet_environment.mm
@@ -201,20 +201,20 @@
   if (file_net_log_observer_) {
     DLOG(WARNING) << "Stopped NetLog.";
     file_net_log_observer_->StopObserving(
-        GetNetLogInfo(), base::BindOnce(&SignalEvent, log_stopped_event));
+        base::Value::ToUniquePtrValue(GetNetLogInfo()),
+        base::BindOnce(&SignalEvent, log_stopped_event));
     file_net_log_observer_.reset();
   } else {
     log_stopped_event->Signal();
   }
 }
 
-std::unique_ptr<base::DictionaryValue> CronetEnvironment::GetNetLogInfo()
-    const {
-  std::unique_ptr<base::DictionaryValue> net_info =
+base::Value CronetEnvironment::GetNetLogInfo() const {
+  base::Value net_info =
       net::GetNetInfo(main_context_.get(), net::NET_INFO_ALL_SOURCES);
   if (effective_experimental_options_) {
-    net_info->Set("cronetExperimentalParams",
-                  effective_experimental_options_->CreateDeepCopy());
+    net_info.SetKey("cronetExperimentalParams",
+                    effective_experimental_options_->Clone());
   }
   return net_info;
 }
diff --git a/components/printing/common/print.mojom b/components/printing/common/print.mojom
index fc601e1..033bd41 100644
--- a/components/printing/common/print.mojom
+++ b/components/printing/common/print.mojom
@@ -65,6 +65,16 @@
   int32 document_cookie;
 };
 
+// Parameters to describe the final rendered preview document.
+struct DidPreviewDocumentParams {
+  // Document's content including metafile data and subframe info.
+  DidPrintContentParams content;
+  // Cookie for the document to ensure correctness.
+  int32 document_cookie;
+  // Store the expected pages count.
+  uint32 expected_pages_count;
+};
+
 // Interface implemented by a class that desires to render print documents for
 // Chrome print preview.
 interface PrintRenderer {
diff --git a/components/printing/common/print_messages.h b/components/printing/common/print_messages.h
index 16a6c23..4677f37 100644
--- a/components/printing/common/print_messages.h
+++ b/components/printing/common/print_messages.h
@@ -276,16 +276,16 @@
 IPC_STRUCT_TRAITS_END()
 
 // Parameters to describe the final rendered preview document.
-IPC_STRUCT_BEGIN(PrintHostMsg_DidPreviewDocument_Params)
+IPC_STRUCT_TRAITS_BEGIN(printing::mojom::DidPreviewDocumentParams)
   // Document's content including metafile data and subframe info.
-  IPC_STRUCT_MEMBER(printing::mojom::DidPrintContentParams, content)
+  IPC_STRUCT_TRAITS_MEMBER(content)
 
   // Cookie for the document to ensure correctness.
-  IPC_STRUCT_MEMBER(int, document_cookie)
+  IPC_STRUCT_TRAITS_MEMBER(document_cookie)
 
   // Store the expected pages count.
-  IPC_STRUCT_MEMBER(int, expected_pages_count)
-IPC_STRUCT_END()
+  IPC_STRUCT_TRAITS_MEMBER(expected_pages_count)
+IPC_STRUCT_TRAITS_END()
 #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
 
 // Parameters to describe a rendered page.
@@ -415,7 +415,7 @@
 // used for printing) that was requested by a PrintMsg_PrintPreview message.
 // The memory handle in this message is already valid in the browser process.
 IPC_MESSAGE_ROUTED2(PrintHostMsg_MetafileReadyForPrinting,
-                    PrintHostMsg_DidPreviewDocument_Params /* params */,
+                    printing::mojom::DidPreviewDocumentParams /* params */,
                     PrintHostMsg_PreviewIds /* ids */)
 #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
 
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index a726ad2..2a97938d 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -1641,7 +1641,8 @@
   DCHECK(!is_print_ready_metafile_sent_);
   print_preview_context_.FinalizePrintReadyDocument();
 
-  PrintHostMsg_DidPreviewDocument_Params preview_params;
+  mojom::DidPreviewDocumentParams preview_params;
+  preview_params.content = mojom::DidPrintContentParams::New();
 
   // Modifiable content of MSKP type is collected into a document during
   // individual page preview generation, so only need to share a separate
@@ -1650,7 +1651,7 @@
   MetafileSkia* metafile = print_preview_context_.metafile();
   if (metafile) {
     if (!CopyMetafileDataToReadOnlySharedMem(*metafile,
-                                             &preview_params.content)) {
+                                             preview_params.content.get())) {
       LOG(ERROR) << "CopyMetafileDataToReadOnlySharedMem failed";
       print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
       return false;
@@ -2727,7 +2728,8 @@
   return pages_to_render_;
 }
 
-int PrintRenderFrameHelper::PrintPreviewContext::pages_rendered_count() const {
+size_t PrintRenderFrameHelper::PrintPreviewContext::pages_rendered_count()
+    const {
   DCHECK_EQ(DONE, state_);
   return pages_to_render_.size();
 }
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
index ed6c601..dbf78f8 100644
--- a/components/printing/renderer/print_render_frame_helper.h
+++ b/components/printing/renderer/print_render_frame_helper.h
@@ -534,7 +534,7 @@
 
     int total_page_count() const;
     const std::vector<int>& pages_to_render() const;
-    int pages_rendered_count() const;
+    size_t pages_rendered_count() const;
     MetafileSkia* metafile();
     ContentProxySet* typeface_content_info();
     int last_error() const;
diff --git a/components/printing/test/print_render_frame_helper_browsertest.cc b/components/printing/test/print_render_frame_helper_browsertest.cc
index 5a4bb44f..24c38ef 100644
--- a/components/printing/test/print_render_frame_helper_browsertest.cc
+++ b/components/printing/test/print_render_frame_helper_browsertest.cc
@@ -743,8 +743,8 @@
       PrintHostMsg_MetafileReadyForPrinting::Read(preview_msg, &preview_param);
       const auto& param = std::get<0>(preview_param);
       EXPECT_NE(0, param.document_cookie);
-      EXPECT_NE(0, param.expected_pages_count);
-      EXPECT_NE(0U, param.content.metafile_data_region.GetSize());
+      EXPECT_NE(0U, param.expected_pages_count);
+      EXPECT_NE(0U, param.content->metafile_data_region.GetSize());
     }
   }
 
diff --git a/components/safe_browsing/content/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
index c04f24f..50fa7311 100644
--- a/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
+++ b/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
@@ -1277,6 +1277,7 @@
   request_dict.SetKey("population",
                       SerializeChromeUserPopulation(request.population()));
   request_dict.SetKey("scoped_oauth_token", base::Value(ping.token));
+  request_dict.SetKey("dm_token", base::Value(request.dm_token()));
 
   std::string lookupType;
   switch (request.lookup_type()) {
diff --git a/components/safe_browsing/core/proto/realtimeapi.proto b/components/safe_browsing/core/proto/realtimeapi.proto
index 32ca4e2c..6b595d61 100644
--- a/components/safe_browsing/core/proto/realtimeapi.proto
+++ b/components/safe_browsing/core/proto/realtimeapi.proto
@@ -32,6 +32,9 @@
 
   // Deprecated
   optional string DEPRECATED_scoped_oauth_token = 4 [deprecated = true];
+
+  // The DM Token for Enterprise-enrolled devices (go/uss-dmtoken).
+  optional string dm_token = 5;
 }
 
 message RTLookupResponse {
diff --git a/components/services/app_service/README.md b/components/services/app_service/README.md
index 7e97994..9aee4e7 100644
--- a/components/services/app_service/README.md
+++ b/components/services/app_service/README.md
@@ -274,7 +274,7 @@
           AppType app_type,
           string app_id,
           IconKey icon_key,
-          IconCompression icon_compression,
+          IconType icon_type,
           int32 size_hint_in_dip,
           bool allow_placeholder_icon) => (IconValue icon_value);
 
@@ -286,7 +286,7 @@
       LoadIcon(
           string app_id,
           IconKey icon_key,
-          IconCompression icon_compression,
+          IconType icon_type,
           int32 size_hint_in_dip,
           bool allow_placeholder_icon) => (IconValue icon_value);
 
@@ -318,14 +318,15 @@
       uint32 icon_effects;
     };
 
-    enum IconCompression {
+    enum IconType {
       kUnknown,
       kUncompressed,
       kCompressed,
+      kStandard,
     };
 
     struct IconValue {
-      IconCompression icon_compression;
+      IconType icon_type;
       gfx.mojom.ImageSkia? uncompressed;
       array<uint8>? compressed;
       bool is_placeholder_icon;
diff --git a/components/services/app_service/app_service_impl.cc b/components/services/app_service/app_service_impl.cc
index a4c4467f..ef0f6ef 100644
--- a/components/services/app_service/app_service_impl.cc
+++ b/components/services/app_service/app_service_impl.cc
@@ -174,7 +174,7 @@
 void AppServiceImpl::LoadIcon(apps::mojom::AppType app_type,
                               const std::string& app_id,
                               apps::mojom::IconKeyPtr icon_key,
-                              apps::mojom::IconCompression icon_compression,
+                              apps::mojom::IconType icon_type,
                               int32_t size_hint_in_dip,
                               bool allow_placeholder_icon,
                               LoadIconCallback callback) {
@@ -183,7 +183,7 @@
     std::move(callback).Run(apps::mojom::IconValue::New());
     return;
   }
-  iter->second->LoadIcon(app_id, std::move(icon_key), icon_compression,
+  iter->second->LoadIcon(app_id, std::move(icon_key), icon_type,
                          size_hint_in_dip, allow_placeholder_icon,
                          std::move(callback));
 }
diff --git a/components/services/app_service/app_service_impl.h b/components/services/app_service/app_service_impl.h
index 21c77827..e0458cd 100644
--- a/components/services/app_service/app_service_impl.h
+++ b/components/services/app_service/app_service_impl.h
@@ -53,7 +53,7 @@
   void LoadIcon(apps::mojom::AppType app_type,
                 const std::string& app_id,
                 apps::mojom::IconKeyPtr icon_key,
-                apps::mojom::IconCompression icon_compression,
+                apps::mojom::IconType icon_type,
                 int32_t size_hint_in_dip,
                 bool allow_placeholder_icon,
                 LoadIconCallback callback) override;
diff --git a/components/services/app_service/app_service_impl_unittest.cc b/components/services/app_service/app_service_impl_unittest.cc
index 532a94c..f41cc8f 100644
--- a/components/services/app_service/app_service_impl_unittest.cc
+++ b/components/services/app_service/app_service_impl_unittest.cc
@@ -72,7 +72,7 @@
 
   void LoadIcon(const std::string& app_id,
                 apps::mojom::IconKeyPtr icon_key,
-                apps::mojom::IconCompression icon_compression,
+                apps::mojom::IconType icon_type,
                 int32_t size_hint_in_dip,
                 bool allow_placeholder_icon,
                 LoadIconCallback callback) override {
@@ -249,7 +249,7 @@
     constexpr bool allow_placeholder_icon = false;
     impl.LoadIcon(
         app_type, "o", std::move(icon_key),
-        apps::mojom::IconCompression::kUncompressed, size_hint_in_dip,
+        apps::mojom::IconType::kUncompressed, size_hint_in_dip,
         allow_placeholder_icon,
         base::BindOnce(
             [](bool* ran, apps::mojom::IconValuePtr iv) { *ran = true; },
diff --git a/components/services/app_service/public/cpp/icon_cache.cc b/components/services/app_service/public/cpp/icon_cache.cc
index 922879b..876215f 100644
--- a/components/services/app_service/public/cpp/icon_cache.cc
+++ b/components/services/app_service/public/cpp/icon_cache.cc
@@ -13,9 +13,10 @@
 IconCache::Value::Value()
     : image_(), is_placeholder_icon_(false), ref_count_(0) {}
 
-apps::mojom::IconValuePtr IconCache::Value::AsIconValue() {
+apps::mojom::IconValuePtr IconCache::Value::AsIconValue(
+    apps::mojom::IconType icon_type) {
   auto icon_value = apps::mojom::IconValue::New();
-  icon_value->icon_compression = apps::mojom::IconCompression::kUncompressed;
+  icon_value->icon_type = icon_type;
   icon_value->uncompressed = image_;
   icon_value->is_placeholder_icon = is_placeholder_icon_;
   return icon_value;
@@ -37,13 +38,13 @@
     apps::mojom::AppType app_type,
     const std::string& app_id,
     apps::mojom::IconKeyPtr icon_key,
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int32_t size_hint_in_dip,
     bool allow_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   IconLoader::Key key(
-      app_type, app_id, icon_key, icon_compression, size_hint_in_dip,
+      app_type, app_id, icon_key, icon_type, size_hint_in_dip,
       // We pass false instead of allow_placeholder_icon, as the Value
       // already records placeholder-ness. If the allow_placeholder_icon
       // arg to this function is true, we can re-use a cache hit regardless
@@ -54,7 +55,8 @@
   Value* cache_hit = nullptr;
   bool ref_count_incremented = false;
 
-  if (icon_compression == apps::mojom::IconCompression::kUncompressed) {
+  if (icon_type == apps::mojom::IconType::kUncompressed ||
+      icon_type == apps::mojom::IconType::kStandard) {
     auto iter = map_.find(key);
     if (iter == map_.end()) {
       iter = map_.insert(std::make_pair(key, Value())).first;
@@ -69,11 +71,11 @@
 
   std::unique_ptr<IconLoader::Releaser> releaser(nullptr);
   if (cache_hit) {
-    std::move(callback).Run(cache_hit->AsIconValue());
+    std::move(callback).Run(cache_hit->AsIconValue(icon_type));
   } else if (wrapped_loader_) {
     releaser = wrapped_loader_->LoadIconFromIconKey(
-        app_type, app_id, std::move(icon_key), icon_compression,
-        size_hint_in_dip, allow_placeholder_icon,
+        app_type, app_id, std::move(icon_key), icon_type, size_hint_in_dip,
+        allow_placeholder_icon,
         base::BindOnce(&IconCache::OnLoadIcon, weak_ptr_factory_.GetWeakPtr(),
                        key, std::move(callback)));
   } else {
@@ -108,8 +110,8 @@
 
 void IconCache::Update(const IconLoader::Key& key,
                        const apps::mojom::IconValue& icon_value) {
-  if (icon_value.icon_compression !=
-      apps::mojom::IconCompression::kUncompressed) {
+  if (icon_value.icon_type != apps::mojom::IconType::kUncompressed &&
+      icon_value.icon_type != apps::mojom::IconType::kStandard) {
     return;
   }
 
diff --git a/components/services/app_service/public/cpp/icon_cache.h b/components/services/app_service/public/cpp/icon_cache.h
index 8c0430c..86c4b61 100644
--- a/components/services/app_service/public/cpp/icon_cache.h
+++ b/components/services/app_service/public/cpp/icon_cache.h
@@ -20,7 +20,7 @@
 
 namespace apps {
 
-// An IconLoader that caches the apps::mojom::IconCompression::kUncompressed
+// An IconLoader that caches the apps::mojom::IconType::kUncompressed
 // results of another (wrapped) IconLoader.
 class IconCache : public IconLoader {
  public:
@@ -75,7 +75,7 @@
       apps::mojom::AppType app_type,
       const std::string& app_id,
       apps::mojom::IconKeyPtr icon_key,
-      apps::mojom::IconCompression icon_compression,
+      apps::mojom::IconType icon_type,
       int32_t size_hint_in_dip,
       bool allow_placeholder_icon,
       apps::mojom::Publisher::LoadIconCallback callback) override;
@@ -93,7 +93,7 @@
 
     Value();
 
-    apps::mojom::IconValuePtr AsIconValue();
+    apps::mojom::IconValuePtr AsIconValue(apps::mojom::IconType icon_type);
   };
 
   void Update(const IconLoader::Key&, const apps::mojom::IconValue&);
diff --git a/components/services/app_service/public/cpp/icon_cache_unittest.cc b/components/services/app_service/public/cpp/icon_cache_unittest.cc
index 9b6e64f..f306d47 100644
--- a/components/services/app_service/public/cpp/icon_cache_unittest.cc
+++ b/components/services/app_service/public/cpp/icon_cache_unittest.cc
@@ -37,15 +37,15 @@
         apps::mojom::AppType app_type,
         const std::string& app_id,
         apps::mojom::IconKeyPtr icon_key,
-        apps::mojom::IconCompression icon_compression,
+        apps::mojom::IconType icon_Type,
         int32_t size_hint_in_dip,
         bool allow_placeholder_icon,
         apps::mojom::Publisher::LoadIconCallback callback) override {
       num_load_calls_++;
 
       auto iv = apps::mojom::IconValue::New();
-      if (icon_compression == apps::mojom::IconCompression::kUncompressed) {
-        iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
+      if (icon_Type == apps::mojom::IconType::kUncompressed) {
+        iv->icon_type = apps::mojom::IconType::kUncompressed;
         iv->uncompressed =
             gfx::ImageSkia(gfx::ImageSkiaRep(gfx::Size(1, 1), 1.0f));
         iv->is_placeholder_icon = return_placeholder_icons_;
@@ -65,14 +65,13 @@
                           HitOrMiss expect_hom,
                           bool allow_placeholder_icon = false) {
     static constexpr auto app_type = apps::mojom::AppType::kWeb;
-    static constexpr auto icon_compression =
-        apps::mojom::IconCompression::kUncompressed;
+    static constexpr auto icon_type = apps::mojom::IconType::kUncompressed;
     static constexpr int32_t size_hint_in_dip = 1;
 
     int before = fake->NumLoadIconFromIconKeyCalls();
 
     UniqueReleaser releaser =
-        loader->LoadIcon(app_type, app_id, icon_compression, size_hint_in_dip,
+        loader->LoadIcon(app_type, app_id, icon_type, size_hint_in_dip,
                          allow_placeholder_icon, base::DoNothing());
 
     int after = fake->NumLoadIconFromIconKeyCalls();
diff --git a/components/services/app_service/public/cpp/icon_coalescer.cc b/components/services/app_service/public/cpp/icon_coalescer.cc
index 007bbce..a4121c6 100644
--- a/components/services/app_service/public/cpp/icon_coalescer.cc
+++ b/components/services/app_service/public/cpp/icon_coalescer.cc
@@ -51,7 +51,7 @@
     apps::mojom::AppType app_type,
     const std::string& app_id,
     apps::mojom::IconKeyPtr icon_key,
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int32_t size_hint_in_dip,
     bool allow_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback) {
@@ -61,15 +61,16 @@
     return nullptr;
   }
 
-  if (icon_compression != apps::mojom::IconCompression::kUncompressed) {
+  if (icon_type != apps::mojom::IconType::kUncompressed &&
+      icon_type != apps::mojom::IconType::kStandard) {
     return wrapped_loader_->LoadIconFromIconKey(
-        app_type, app_id, std::move(icon_key), icon_compression,
-        size_hint_in_dip, allow_placeholder_icon, std::move(callback));
+        app_type, app_id, std::move(icon_key), icon_type, size_hint_in_dip,
+        allow_placeholder_icon, std::move(callback));
   }
 
   scoped_refptr<RefCountedReleaser> shared_releaser;
-  IconLoader::Key key(app_type, app_id, icon_key, icon_compression,
-                      size_hint_in_dip, allow_placeholder_icon);
+  IconLoader::Key key(app_type, app_id, icon_key, icon_type, size_hint_in_dip,
+                      allow_placeholder_icon);
 
   auto iter = non_immediate_requests_.find(key);
   if (iter != non_immediate_requests_.end()) {
@@ -114,8 +115,8 @@
 
     std::unique_ptr<IconLoader::Releaser> unique_releaser =
         wrapped_loader_->LoadIconFromIconKey(
-            app_type, app_id, std::move(icon_key), icon_compression,
-            size_hint_in_dip, allow_placeholder_icon,
+            app_type, app_id, std::move(icon_key), icon_type, size_hint_in_dip,
+            allow_placeholder_icon,
             base::BindOnce(&IconCoalescer::OnLoadIcon,
                            weak_ptr_factory_.GetWeakPtr(), key, seq_num));
 
@@ -201,7 +202,7 @@
       iv = std::move(icon_value);
     } else {
       iv = apps::mojom::IconValue::New();
-      iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
+      iv->icon_type = icon_value->icon_type;
       iv->uncompressed = icon_value->uncompressed;
       iv->is_placeholder_icon = icon_value->is_placeholder_icon;
     }
diff --git a/components/services/app_service/public/cpp/icon_coalescer.h b/components/services/app_service/public/cpp/icon_coalescer.h
index c7dfebd..9d81dd5 100644
--- a/components/services/app_service/public/cpp/icon_coalescer.h
+++ b/components/services/app_service/public/cpp/icon_coalescer.h
@@ -20,7 +20,7 @@
 
 namespace apps {
 
-// An IconLoader that coalesces the apps::mojom::IconCompression::kUncompressed
+// An IconLoader that coalesces the apps::mojom::IconType::kUncompressed
 // results of another (wrapped) IconLoader.
 //
 // This is similar to, but different from, an IconCache. Both types are related
@@ -54,7 +54,7 @@
       apps::mojom::AppType app_type,
       const std::string& app_id,
       apps::mojom::IconKeyPtr icon_key,
-      apps::mojom::IconCompression icon_compression,
+      apps::mojom::IconType icon_type,
       int32_t size_hint_in_dip,
       bool allow_placeholder_icon,
       apps::mojom::Publisher::LoadIconCallback callback) override;
diff --git a/components/services/app_service/public/cpp/icon_coalescer_unittest.cc b/components/services/app_service/public/cpp/icon_coalescer_unittest.cc
index 76e4ddd..32650c7 100644
--- a/components/services/app_service/public/cpp/icon_coalescer_unittest.cc
+++ b/components/services/app_service/public/cpp/icon_coalescer_unittest.cc
@@ -57,7 +57,7 @@
         apps::mojom::AppType app_type,
         const std::string& app_id,
         apps::mojom::IconKeyPtr icon_key,
-        apps::mojom::IconCompression icon_compression,
+        apps::mojom::IconType icon_type,
         int32_t size_hint_in_dip,
         bool allow_placeholder_icon,
         apps::mojom::Publisher::LoadIconCallback callback) override {
@@ -76,7 +76,7 @@
 
     apps::mojom::IconValuePtr NewIconValuePtr() {
       auto iv = apps::mojom::IconValue::New();
-      iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
+      iv->icon_type = apps::mojom::IconType::kUncompressed;
       iv->uncompressed =
           gfx::ImageSkia(gfx::ImageSkiaRep(gfx::Size(1, 1), 1.0f));
       iv->is_placeholder_icon = false;
@@ -100,14 +100,12 @@
                           int* counter,
                           int delta) {
     static constexpr auto app_type = apps::mojom::AppType::kWeb;
-    static constexpr auto icon_compression =
-        apps::mojom::IconCompression::kUncompressed;
+    static constexpr auto icon_type = apps::mojom::IconType::kUncompressed;
     static constexpr int32_t size_hint_in_dip = 1;
     static constexpr bool allow_placeholder_icon = false;
 
     return loader->LoadIcon(
-        app_type, app_id, icon_compression, size_hint_in_dip,
-        allow_placeholder_icon,
+        app_type, app_id, icon_type, size_hint_in_dip, allow_placeholder_icon,
         base::BindOnce(&AppsIconCoalescerTest::Increment, counter, delta));
   }
 };
diff --git a/components/services/app_service/public/cpp/icon_loader.cc b/components/services/app_service/public/cpp/icon_loader.cc
index 25c57587..8b493c10 100644
--- a/components/services/app_service/public/cpp/icon_loader.cc
+++ b/components/services/app_service/public/cpp/icon_loader.cc
@@ -21,7 +21,7 @@
 IconLoader::Key::Key(apps::mojom::AppType app_type,
                      const std::string& app_id,
                      const apps::mojom::IconKeyPtr& icon_key,
-                     apps::mojom::IconCompression icon_compression,
+                     apps::mojom::IconType icon_type,
                      int32_t size_hint_in_dip,
                      bool allow_placeholder_icon)
     : app_type_(app_type),
@@ -29,7 +29,7 @@
       timeline_(icon_key ? icon_key->timeline : 0),
       resource_id_(icon_key ? icon_key->resource_id : 0),
       icon_effects_(icon_key ? icon_key->icon_effects : 0),
-      icon_compression_(icon_compression),
+      icon_type_(icon_type),
       size_hint_in_dip_(size_hint_in_dip),
       allow_placeholder_icon_(allow_placeholder_icon) {}
 
@@ -48,8 +48,8 @@
   if (this->icon_effects_ != that.icon_effects_) {
     return this->icon_effects_ < that.icon_effects_;
   }
-  if (this->icon_compression_ != that.icon_compression_) {
-    return this->icon_compression_ < that.icon_compression_;
+  if (this->icon_type_ != that.icon_type_) {
+    return this->icon_type_ < that.icon_type_;
   }
   if (this->size_hint_in_dip_ != that.size_hint_in_dip_) {
     return this->size_hint_in_dip_ < that.size_hint_in_dip_;
@@ -67,13 +67,13 @@
 std::unique_ptr<IconLoader::Releaser> IconLoader::LoadIcon(
     apps::mojom::AppType app_type,
     const std::string& app_id,
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int32_t size_hint_in_dip,
     bool allow_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback) {
-  return LoadIconFromIconKey(app_type, app_id, GetIconKey(app_id),
-                             icon_compression, size_hint_in_dip,
-                             allow_placeholder_icon, std::move(callback));
+  return LoadIconFromIconKey(app_type, app_id, GetIconKey(app_id), icon_type,
+                             size_hint_in_dip, allow_placeholder_icon,
+                             std::move(callback));
 }
 
 }  // namespace apps
diff --git a/components/services/app_service/public/cpp/icon_loader.h b/components/services/app_service/public/cpp/icon_loader.h
index 7470d2fc..b705799a 100644
--- a/components/services/app_service/public/cpp/icon_loader.h
+++ b/components/services/app_service/public/cpp/icon_loader.h
@@ -57,7 +57,7 @@
       apps::mojom::AppType app_type,
       const std::string& app_id,
       apps::mojom::IconKeyPtr icon_key,
-      apps::mojom::IconCompression icon_compression,
+      apps::mojom::IconType icon_type,
       int32_t size_hint_in_dip,
       bool allow_placeholder_icon,
       apps::mojom::Publisher::LoadIconCallback callback) = 0;
@@ -67,7 +67,7 @@
   std::unique_ptr<Releaser> LoadIcon(
       apps::mojom::AppType app_type,
       const std::string& app_id,
-      apps::mojom::IconCompression icon_compression,
+      apps::mojom::IconType icon_type,
       int32_t size_hint_in_dip,
       bool allow_placeholder_icon,
       apps::mojom::Publisher::LoadIconCallback callback);
@@ -89,14 +89,14 @@
     int32_t resource_id_;
     uint32_t icon_effects_;
     // Other fields.
-    apps::mojom::IconCompression icon_compression_;
+    apps::mojom::IconType icon_type_;
     int32_t size_hint_in_dip_;
     bool allow_placeholder_icon_;
 
     Key(apps::mojom::AppType app_type,
         const std::string& app_id,
         const apps::mojom::IconKeyPtr& icon_key,
-        apps::mojom::IconCompression icon_compression,
+        apps::mojom::IconType icon_type,
         int32_t size_hint_in_dip,
         bool allow_placeholder_icon);
 
diff --git a/components/services/app_service/public/cpp/intent_util.cc b/components/services/app_service/public/cpp/intent_util.cc
index 021f78f..0a91937 100644
--- a/components/services/app_service/public/cpp/intent_util.cc
+++ b/components/services/app_service/public/cpp/intent_util.cc
@@ -19,11 +19,17 @@
     case apps::mojom::ConditionType::kAction:
       return intent->action;
     case apps::mojom::ConditionType::kScheme:
-      return intent->scheme;
+      return intent->url.has_value()
+                 ? base::Optional<std::string>(intent->url->scheme())
+                 : base::nullopt;
     case apps::mojom::ConditionType::kHost:
-      return intent->host;
+      return intent->url.has_value()
+                 ? base::Optional<std::string>(intent->url->host())
+                 : base::nullopt;
     case apps::mojom::ConditionType::kPattern:
-      return intent->path;
+      return intent->url.has_value()
+                 ? base::Optional<std::string>(intent->url->path())
+                 : base::nullopt;
     case apps::mojom::ConditionType::kMimeType:
       return intent->mime_type;
   }
@@ -49,12 +55,18 @@
       base::SplitString(mime_type2, kMimeTypeSeparator, base::TRIM_WHITESPACE,
                         base::SPLIT_WANT_NONEMPTY);
 
-  const size_t kMimeTypeComponentSize = 2;
-  if (components1.size() != kMimeTypeComponentSize ||
-      components2.size() != kMimeTypeComponentSize) {
+  constexpr size_t kMimeTypeComponentSize = 2;
+  if (components1.size() > kMimeTypeComponentSize ||
+      components2.size() > kMimeTypeComponentSize || components1.size() == 0 ||
+      components2.size() == 0) {
     return false;
   }
 
+  // For strings only contain the main mime type (i.e. no "/").
+  if (components1.size() == 1 || components2.size() == 1) {
+    return ComponentMatched(components1[0], components2[0]);
+  }
+
   // Both intent and intent filter can use wildcard for mime type.
   for (size_t i = 0; i < kMimeTypeComponentSize; i++) {
     if (!ComponentMatched(components1[i], components2[i])) {
@@ -75,10 +87,7 @@
 apps::mojom::IntentPtr CreateIntentFromUrl(const GURL& url) {
   auto intent = apps::mojom::Intent::New();
   intent->action = kIntentActionView;
-  intent->scheme = url.scheme();
-  intent->host = url.host();
-  intent->port = url.port();
-  intent->path = url.path();
+  intent->url = url;
   return intent;
 }
 
diff --git a/components/services/app_service/public/cpp/intent_util_unittest.cc b/components/services/app_service/public/cpp/intent_util_unittest.cc
index 4e2afbe..9b9e512 100644
--- a/components/services/app_service/public/cpp/intent_util_unittest.cc
+++ b/components/services/app_service/public/cpp/intent_util_unittest.cc
@@ -226,11 +226,15 @@
   std::string mime_type2 = "image/jpeg";
   std::string mime_type_sub_wildcard = "text/*";
   std::string mime_type_all_wildcard = "*/*";
+  std::string mime_type_only_main_type = "text";
+  std::string mime_type_only_star = "*";
 
   auto intent1 = CreateShareIntent(mime_type1);
   auto intent2 = CreateShareIntent(mime_type2);
   auto intent_sub_wildcard = CreateShareIntent(mime_type_sub_wildcard);
   auto intent_all_wildcard = CreateShareIntent(mime_type_all_wildcard);
+  auto intent_only_main_type = CreateShareIntent(mime_type_only_main_type);
+  auto intent_only_star = CreateShareIntent(mime_type_only_star);
 
   auto filter1 = CreateIntentFilterForShareTarget(mime_type1);
 
@@ -238,6 +242,8 @@
   EXPECT_FALSE(apps_util::IntentMatchesFilter(intent2, filter1));
   EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_sub_wildcard, filter1));
   EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_all_wildcard, filter1));
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_only_main_type, filter1));
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_only_star, filter1));
 
   auto filter2 = CreateIntentFilterForShareTarget(mime_type2);
 
@@ -245,6 +251,8 @@
   EXPECT_TRUE(apps_util::IntentMatchesFilter(intent2, filter2));
   EXPECT_FALSE(apps_util::IntentMatchesFilter(intent_sub_wildcard, filter2));
   EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_all_wildcard, filter2));
+  EXPECT_FALSE(apps_util::IntentMatchesFilter(intent_only_main_type, filter2));
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_only_star, filter2));
 
   auto filter_sub_wildcard =
       CreateIntentFilterForShareTarget(mime_type_sub_wildcard);
@@ -255,6 +263,10 @@
       apps_util::IntentMatchesFilter(intent_sub_wildcard, filter_sub_wildcard));
   EXPECT_TRUE(
       apps_util::IntentMatchesFilter(intent_all_wildcard, filter_sub_wildcard));
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_only_main_type,
+                                             filter_sub_wildcard));
+  EXPECT_TRUE(
+      apps_util::IntentMatchesFilter(intent_only_star, filter_sub_wildcard));
 
   auto filter_all_wildcard =
       CreateIntentFilterForShareTarget(mime_type_all_wildcard);
@@ -265,4 +277,35 @@
       apps_util::IntentMatchesFilter(intent_sub_wildcard, filter_all_wildcard));
   EXPECT_TRUE(
       apps_util::IntentMatchesFilter(intent_all_wildcard, filter_all_wildcard));
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_only_main_type,
+                                             filter_all_wildcard));
+  EXPECT_TRUE(
+      apps_util::IntentMatchesFilter(intent_only_star, filter_all_wildcard));
+
+  auto filter_only_main_type =
+      CreateIntentFilterForShareTarget(mime_type_only_main_type);
+
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent1, filter_only_main_type));
+  EXPECT_FALSE(apps_util::IntentMatchesFilter(intent2, filter_only_main_type));
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_sub_wildcard,
+                                             filter_only_main_type));
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_all_wildcard,
+                                             filter_only_main_type));
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent_only_main_type,
+                                             filter_only_main_type));
+  EXPECT_TRUE(
+      apps_util::IntentMatchesFilter(intent_only_star, filter_only_main_type));
+
+  auto filter_only_star = CreateIntentFilterForShareTarget(mime_type_only_star);
+
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent1, filter_only_star));
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(intent2, filter_only_star));
+  EXPECT_TRUE(
+      apps_util::IntentMatchesFilter(intent_sub_wildcard, filter_only_star));
+  EXPECT_TRUE(
+      apps_util::IntentMatchesFilter(intent_all_wildcard, filter_only_star));
+  EXPECT_TRUE(
+      apps_util::IntentMatchesFilter(intent_only_main_type, filter_only_star));
+  EXPECT_TRUE(
+      apps_util::IntentMatchesFilter(intent_only_star, filter_only_star));
 }
diff --git a/components/services/app_service/public/cpp/stub_icon_loader.cc b/components/services/app_service/public/cpp/stub_icon_loader.cc
index 1df6c40..1613488 100644
--- a/components/services/app_service/public/cpp/stub_icon_loader.cc
+++ b/components/services/app_service/public/cpp/stub_icon_loader.cc
@@ -28,7 +28,7 @@
     apps::mojom::AppType app_type,
     const std::string& app_id,
     apps::mojom::IconKeyPtr icon_key,
-    apps::mojom::IconCompression icon_compression,
+    apps::mojom::IconType icon_type,
     int32_t size_hint_in_dip,
     bool allow_placeholder_icon,
     apps::mojom::Publisher::LoadIconCallback callback) {
@@ -36,7 +36,7 @@
   auto iter = timelines_by_app_id_.find(app_id);
   if (iter != timelines_by_app_id_.end()) {
     auto icon_value = apps::mojom::IconValue::New();
-    icon_value->icon_compression = apps::mojom::IconCompression::kUncompressed;
+    icon_value->icon_type = icon_type;
     icon_value->uncompressed =
         gfx::ImageSkia(gfx::ImageSkiaRep(gfx::Size(1, 1), 1.0f));
     std::move(callback).Run(std::move(icon_value));
diff --git a/components/services/app_service/public/cpp/stub_icon_loader.h b/components/services/app_service/public/cpp/stub_icon_loader.h
index 54fa69a7..8083d53 100644
--- a/components/services/app_service/public/cpp/stub_icon_loader.h
+++ b/components/services/app_service/public/cpp/stub_icon_loader.h
@@ -25,7 +25,7 @@
       apps::mojom::AppType app_type,
       const std::string& app_id,
       apps::mojom::IconKeyPtr icon_key,
-      apps::mojom::IconCompression icon_compression,
+      apps::mojom::IconType icon_type,
       int32_t size_hint_in_dip,
       bool allow_placeholder_icon,
       apps::mojom::Publisher::LoadIconCallback callback) override;
diff --git a/components/services/app_service/public/mojom/BUILD.gn b/components/services/app_service/public/mojom/BUILD.gn
index 0e61140..b2a043d 100644
--- a/components/services/app_service/public/mojom/BUILD.gn
+++ b/components/services/app_service/public/mojom/BUILD.gn
@@ -15,6 +15,7 @@
     "//ui/gfx/image/mojom",
     "//ui/gfx/mojom",
     "//ui/gfx/range/mojom",
+    "//url/mojom:url_mojom_gurl",
   ]
 }
 
diff --git a/components/services/app_service/public/mojom/app_service.mojom b/components/services/app_service/public/mojom/app_service.mojom
index f9d57c7..c10328e 100644
--- a/components/services/app_service/public/mojom/app_service.mojom
+++ b/components/services/app_service/public/mojom/app_service.mojom
@@ -27,7 +27,7 @@
       AppType app_type,
       string app_id,
       IconKey icon_key,
-      IconCompression icon_compression,
+      IconType icon_type,
       int32 size_hint_in_dip,
       bool allow_placeholder_icon) => (IconValue icon_value);
 
@@ -130,7 +130,7 @@
   LoadIcon(
       string app_id,
       IconKey icon_key,
-      IconCompression icon_compression,
+      IconType icon_type,
       int32 size_hint_in_dip,
       bool allow_placeholder_icon) => (IconValue icon_value);
 
diff --git a/components/services/app_service/public/mojom/types.mojom b/components/services/app_service/public/mojom/types.mojom
index d52470b..6713561 100644
--- a/components/services/app_service/public/mojom/types.mojom
+++ b/components/services/app_service/public/mojom/types.mojom
@@ -7,6 +7,7 @@
 import "mojo/public/mojom/base/file_path.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "ui/gfx/image/mojom/image.mojom";
+import "url/mojom/url.mojom";
 
 // Information about an app. See components/services/app_service/README.md.
 struct App {
@@ -159,7 +160,7 @@
   // components/services/app_service/public/cpp/icon_loader.*
 };
 
-enum IconCompression {
+enum IconType {
   // Sentinel value used in error cases.
   kUnknown,
   // Icon as an uncompressed gfx::ImageSkia with no standard Chrome OS mask.
@@ -172,7 +173,7 @@
 };
 
 struct IconValue {
-  IconCompression icon_compression;
+  IconType icon_type;
   gfx.mojom.ImageSkia? uncompressed;
   array<uint8>? compressed;
   bool is_placeholder_icon;
@@ -301,10 +302,7 @@
 // the moment, and will be extended to handle files in the future.
 struct Intent {
   string? action; // Intent action. e.g. view, send.
-  string? scheme; // URL scheme. e.g. https.
-  string? host;   // URL host. e.g. www.google.com.
-  string? port;   // URL host. e.g. 8080.
-  string? path;   // URL path. e.g. /abc.
+  url.mojom.Url? url; // The URL of the intent. e.g. https://www.google.com/.
   string? mime_type; // MIME type. e.g. text/plain, image/*.
 };
 
diff --git a/components/services/print_compositor/print_compositor_impl.cc b/components/services/print_compositor/print_compositor_impl.cc
index 8e87496..b731b25c 100644
--- a/components/services/print_compositor/print_compositor_impl.cc
+++ b/components/services/print_compositor/print_compositor_impl.cc
@@ -146,13 +146,9 @@
 
   // Add this frame and its serialized content.
   DCHECK(!base::Contains(frame_info_map_, frame_guid));
-  auto& frame_info =
-      frame_info_map_.emplace(frame_guid, std::make_unique<FrameInfo>())
-          .first->second;
-  frame_info->serialized_content = std::move(mapping);
-
-  // Copy the subframe content information.
-  frame_info->subframe_content_map = subframe_content_map;
+  frame_info_map_.emplace(frame_guid, std::make_unique<FrameInfo>(
+                                          mapping.GetMemoryAsSpan<uint8_t>(),
+                                          subframe_content_map));
 
   // If there is no request, we do nothing more.
   // Otherwise, we need to check whether any request actually waits on this
@@ -263,8 +259,8 @@
     }
 
     // Fulfill the request now.
-    FulfillRequest(std::move(request->serialized_content),
-                   request->subframe_content_map, std::move(request->callback));
+    FulfillRequest(request->serialized_content, request->subframe_content_map,
+                   std::move(request->callback));
 
     // Check for a collected print preview document that was waiting on
     // this page to finish.
@@ -330,7 +326,7 @@
     // requests have already been processed, otherwise this request could
     // fail by trying to use a typeface which hasn't been deserialized yet.
     if (requests_.empty()) {
-      FulfillRequest(std::move(mapping), subframe_content_map,
+      FulfillRequest(mapping.GetMemoryAsSpan<uint8_t>(), subframe_content_map,
                      std::move(callback));
       return;
     }
@@ -343,8 +339,8 @@
     frame_info_map_[frame_guid] = std::make_unique<FrameInfo>();
 
   requests_.push_back(std::make_unique<RequestInfo>(
-      std::move(mapping), subframe_content_map, std::move(pending_subframes),
-      std::move(callback)));
+      mapping.GetMemoryAsSpan<uint8_t>(), subframe_content_map,
+      std::move(pending_subframes), std::move(callback)));
 }
 
 void PrintCompositorImpl::HandleDocumentCompletionRequest() {
@@ -358,21 +354,16 @@
 }
 
 mojom::PrintCompositor::Status PrintCompositorImpl::CompositeToPdf(
-    base::ReadOnlySharedMemoryMapping shared_mem,
+    base::span<const uint8_t> serialized_content,
     const ContentToFrameMap& subframe_content_map,
     base::ReadOnlySharedMemoryRegion* region) {
   TRACE_EVENT0("print", "PrintCompositorImpl::CompositeToPdf");
 
-  if (!shared_mem.IsValid()) {
-    DLOG(ERROR) << "CompositeToPdf: Invalid input.";
-    return mojom::PrintCompositor::Status::kHandleMapError;
-  }
-
   PictureDeserializationContext subframes =
       GetPictureDeserializationContext(subframe_content_map);
 
   // Read in content and convert it into pdf.
-  SkMemoryStream stream(shared_mem.memory(), shared_mem.size());
+  SkMemoryStream stream(serialized_content.data(), serialized_content.size());
   int page_count = SkMultiPictureDocumentReadPageCount(&stream);
   if (!page_count) {
     DLOG(ERROR) << "CompositeToPdf: No page is read.";
@@ -432,7 +423,7 @@
       GetPictureDeserializationContext(frame_info->subframe_content_map);
 
   // Composite the entire frame.
-  SkMemoryStream stream(frame_info->serialized_content.memory(),
+  SkMemoryStream stream(frame_info->serialized_content.data(),
                         frame_info->serialized_content.size());
   SkDeserialProcs procs =
       DeserializationProcs(&subframes, &frame_info->typefaces);
@@ -459,12 +450,12 @@
 }
 
 void PrintCompositorImpl::FulfillRequest(
-    base::ReadOnlySharedMemoryMapping serialized_content,
+    base::span<const uint8_t> serialized_content,
     const ContentToFrameMap& subframe_content_map,
     CompositeToPdfCallback callback) {
   base::ReadOnlySharedMemoryRegion region;
-  auto status = CompositeToPdf(std::move(serialized_content),
-                               subframe_content_map, &region);
+  auto status =
+      CompositeToPdf(serialized_content, subframe_content_map, &region);
   std::move(callback).Run(status, std::move(region));
 }
 
@@ -492,28 +483,25 @@
 }
 
 PrintCompositorImpl::FrameContentInfo::FrameContentInfo(
-    base::ReadOnlySharedMemoryMapping content,
+    base::span<const uint8_t> content,
     const ContentToFrameMap& map)
-    : serialized_content(std::move(content)), subframe_content_map(map) {}
+    : serialized_content(content.begin(), content.end()),
+      subframe_content_map(map) {}
 
 PrintCompositorImpl::FrameContentInfo::FrameContentInfo() = default;
 
 PrintCompositorImpl::FrameContentInfo::~FrameContentInfo() = default;
 
-PrintCompositorImpl::FrameInfo::FrameInfo() = default;
-
-PrintCompositorImpl::FrameInfo::~FrameInfo() = default;
-
 PrintCompositorImpl::DocumentInfo::DocumentInfo() = default;
 
 PrintCompositorImpl::DocumentInfo::~DocumentInfo() = default;
 
 PrintCompositorImpl::RequestInfo::RequestInfo(
-    base::ReadOnlySharedMemoryMapping content,
+    base::span<const uint8_t> content,
     const ContentToFrameMap& content_info,
     const base::flat_set<uint64_t>& pending_subframes,
     mojom::PrintCompositor::CompositePageToPdfCallback callback)
-    : FrameContentInfo(std::move(content), content_info),
+    : FrameContentInfo(content, content_info),
       pending_subframes(pending_subframes),
       callback(std::move(callback)) {}
 
diff --git a/components/services/print_compositor/print_compositor_impl.h b/components/services/print_compositor/print_compositor_impl.h
index 01eb84cf..44a3d291 100644
--- a/components/services/print_compositor/print_compositor_impl.h
+++ b/components/services/print_compositor/print_compositor_impl.h
@@ -12,6 +12,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
+#include "base/containers/span.h"
 #include "base/macros.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/ref_counted_memory.h"
@@ -105,15 +106,14 @@
   // The core function for content composition and conversion to a pdf file.
   // Make this function virtual so tests can override it.
   virtual mojom::PrintCompositor::Status CompositeToPdf(
-      base::ReadOnlySharedMemoryMapping shared_mem,
+      base::span<const uint8_t> serialized_content,
       const ContentToFrameMap& subframe_content_map,
       base::ReadOnlySharedMemoryRegion* region);
 
   // Make these functions virtual so tests can override them.
-  virtual void FulfillRequest(
-      base::ReadOnlySharedMemoryMapping serialized_content,
-      const ContentToFrameMap& subframe_content_map,
-      CompositeToPdfCallback callback);
+  virtual void FulfillRequest(base::span<const uint8_t> serialized_content,
+                              const ContentToFrameMap& subframe_content_map,
+                              CompositeToPdfCallback callback);
   virtual void CompleteDocumentRequest(CompleteDocumentToPdfCallback callback);
 
  private:
@@ -132,13 +132,13 @@
   // Base structure to store a frame's content and its subframe
   // content information.
   struct FrameContentInfo {
-    FrameContentInfo(base::ReadOnlySharedMemoryMapping content,
+    FrameContentInfo(base::span<const uint8_t> content,
                      const ContentToFrameMap& map);
     FrameContentInfo();
     ~FrameContentInfo();
 
     // Serialized SkPicture content of this frame.
-    base::ReadOnlySharedMemoryMapping serialized_content;
+    std::vector<uint8_t> serialized_content;
 
     // Frame content after composition with subframe content.
     sk_sp<SkPicture> content;
@@ -152,8 +152,7 @@
 
   // Other than content, it also stores the status during frame composition.
   struct FrameInfo : public FrameContentInfo {
-    FrameInfo();
-    ~FrameInfo();
+    using FrameContentInfo::FrameContentInfo;
 
     // The following fields are used for storing composition status.
     // Set to true when this frame's |serialized_content| is composed with
@@ -167,7 +166,7 @@
 
   // Stores the page or document's request information.
   struct RequestInfo : public FrameContentInfo {
-    RequestInfo(base::ReadOnlySharedMemoryMapping content,
+    RequestInfo(base::span<const uint8_t> content,
                 const ContentToFrameMap& content_info,
                 const base::flat_set<uint64_t>& pending_subframes,
                 CompositeToPdfCallback callback);
diff --git a/components/services/print_compositor/print_compositor_impl_unittest.cc b/components/services/print_compositor/print_compositor_impl_unittest.cc
index 16584051..bc1c05e1 100644
--- a/components/services/print_compositor/print_compositor_impl_unittest.cc
+++ b/components/services/print_compositor/print_compositor_impl_unittest.cc
@@ -34,10 +34,11 @@
   MOCK_METHOD2(OnFulfillRequest, void(uint64_t, int));
 
  protected:
-  void FulfillRequest(base::ReadOnlySharedMemoryMapping serialized_content,
+  void FulfillRequest(base::span<const uint8_t> serialized_content,
                       const ContentToFrameMap& subframe_content_map,
                       CompositeToPdfCallback callback) override {
-    const auto* data = serialized_content.GetMemoryAs<const TestRequestData>();
+    const auto* data =
+        reinterpret_cast<const TestRequestData*>(serialized_content.data());
     OnFulfillRequest(data->frame_guid, data->page_num);
   }
 };
@@ -57,10 +58,11 @@
 
  protected:
   mojom::PrintCompositor::Status CompositeToPdf(
-      base::ReadOnlySharedMemoryMapping shared_mem,
+      base::span<const uint8_t> serialized_content,
       const ContentToFrameMap& subframe_content_map,
       base::ReadOnlySharedMemoryRegion* region) override {
-    const auto* data = shared_mem.GetMemoryAs<const TestRequestData>();
+    const auto* data =
+        reinterpret_cast<const TestRequestData*>(serialized_content.data());
     if (docinfo_)
       docinfo_->pages_written++;
     OnCompositeToPdf(data->frame_guid, data->page_num);
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 9e219c0..26a6fead 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -158,8 +158,6 @@
     "engine_impl/get_updates_delegate.h",
     "engine_impl/get_updates_processor.cc",
     "engine_impl/get_updates_processor.h",
-    "engine_impl/js_mutation_event_observer.cc",
-    "engine_impl/js_mutation_event_observer.h",
     "engine_impl/js_sync_encryption_handler_observer.cc",
     "engine_impl/js_sync_encryption_handler_observer.h",
     "engine_impl/js_sync_manager_observer.cc",
@@ -434,10 +432,6 @@
     "engine_impl/cycle/mock_debug_info_getter.h",
     "engine_impl/cycle/test_util.cc",
     "engine_impl/cycle/test_util.h",
-    "engine_impl/test_entry_factory.cc",
-    "engine_impl/test_entry_factory.h",
-    "syncable/test_user_share.cc",
-    "syncable/test_user_share.h",
     "test/engine/fake_model_worker.cc",
     "test/engine/fake_model_worker.h",
     "test/engine/fake_sync_scheduler.cc",
@@ -454,9 +448,6 @@
     "test/engine/mock_update_handler.h",
     "test/engine/single_type_mock_server.cc",
     "test/engine/single_type_mock_server.h",
-    "test/engine/test_directory_setter_upper.cc",
-    "test/engine/test_directory_setter_upper.h",
-    "test/engine/test_id_factory.h",
     "test/engine/test_syncable_utils.cc",
     "test/engine/test_syncable_utils.h",
     "test/fake_sync_encryption_handler.cc",
@@ -590,7 +581,6 @@
     "engine_impl/debug_info_event_listener_unittest.cc",
     "engine_impl/events/protocol_event_buffer_unittest.cc",
     "engine_impl/get_updates_processor_unittest.cc",
-    "engine_impl/js_mutation_event_observer_unittest.cc",
     "engine_impl/js_sync_encryption_handler_observer_unittest.cc",
     "engine_impl/js_sync_manager_observer_unittest.cc",
     "engine_impl/loopback_server/loopback_server_unittest.cc",
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc
index b028c4d..161a6d8 100644
--- a/components/sync/driver/glue/sync_engine_backend.cc
+++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -13,7 +13,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "base/trace_event/memory_dump_manager.h"
 #include "components/invalidation/public/invalidation_util.h"
 #include "components/invalidation/public/topic_invalidation_map.h"
 #include "components/sync/base/invalidation_adapter.h"
@@ -72,16 +71,6 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-bool SyncEngineBackend::OnMemoryDump(
-    const base::trace_event::MemoryDumpArgs& args,
-    base::trace_event::ProcessMemoryDump* pmd) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!sync_manager_)
-    return false;
-  sync_manager_->OnMemoryDump(pmd);
-  return true;
-}
-
 void SyncEngineBackend::OnSyncCycleCompleted(
     const SyncCycleSnapshot& snapshot) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -135,10 +124,6 @@
   SDVLOG(1) << "Control Types " << ModelTypeSetToString(new_control_types)
             << " added; calling ConfigureSyncer";
 
-  ModelTypeSet types_to_purge =
-      Difference(ModelTypeSet::All(), GetRoutingInfoTypes(routing_info));
-
-  sync_manager_->PurgeDisabledTypes(types_to_purge);
   sync_manager_->ConfigureSyncer(
       reason, new_control_types, SyncManager::SyncFeatureState::INITIALIZING,
       base::BindOnce(&SyncEngineBackend::DoInitialProcessControlTypes,
@@ -313,7 +298,6 @@
   registrar_->GetWorkers(&args.workers);
   args.encryption_observer_proxy = std::move(params.encryption_observer_proxy);
   args.extensions_activity = params.extensions_activity.get();
-  args.change_delegate = registrar_.get();  // as SyncManager::ChangeDelegate
   args.authenticated_account_id = params.authenticated_account_id;
   args.invalidator_client_id = params.invalidator_client_id;
   args.engine_components_factory = std::move(params.engine_components_factory);
@@ -325,9 +309,6 @@
   args.bag_of_chips = params.bag_of_chips;
   args.sync_status_observers.push_back(this);
   sync_manager_->Init(&args);
-  base::trace_event::MemoryDumpManager::GetInstance()
-      ->RegisterDumpProviderWithSequencedTaskRunner(
-          this, "SyncDirectory", base::SequencedTaskRunnerHandle::Get(), {});
 }
 
 void SyncEngineBackend::DoUpdateCredentials(
@@ -448,8 +429,7 @@
 
 void SyncEngineBackend::DoDestroySyncManager() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
-      this);
+
   if (sync_manager_) {
     DisableDirectoryTypeDebugInfoForwarding();
     sync_manager_->RemoveObserver(this);
@@ -460,7 +440,6 @@
 
 void SyncEngineBackend::DoPurgeDisabledTypes(const ModelTypeSet& to_purge) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  sync_manager_->PurgeDisabledTypes(to_purge);
   if (to_purge.Has(NIGORI)) {
     // We are using USS implementation of Nigori and someone asked us to purge
     // it's data. For regular datatypes it's controlled DataTypeManager, but
diff --git a/components/sync/driver/glue/sync_engine_backend.h b/components/sync/driver/glue/sync_engine_backend.h
index 0ddda13..f6b5bdc7 100644
--- a/components/sync/driver/glue/sync_engine_backend.h
+++ b/components/sync/driver/glue/sync_engine_backend.h
@@ -16,7 +16,6 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/sequence_checker.h"
-#include "base/trace_event/memory_dump_provider.h"
 #include "components/invalidation/impl/invalidation_switches.h"
 #include "components/invalidation/public/invalidation.h"
 #include "components/sync/base/cancelation_signal.h"
@@ -35,7 +34,6 @@
 class SyncEngineImpl;
 
 class SyncEngineBackend : public base::RefCountedThreadSafe<SyncEngineBackend>,
-                          public base::trace_event::MemoryDumpProvider,
                           public SyncManager::Observer,
                           public TypeDebugInfoObserver,
                           public SyncStatusObserver {
@@ -48,10 +46,6 @@
                     const base::FilePath& sync_data_folder,
                     const base::WeakPtr<SyncEngineImpl>& host);
 
-  // MemoryDumpProvider implementation.
-  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
-                    base::trace_event::ProcessMemoryDump* pmd) override;
-
   // SyncManager::Observer implementation.  The Core just acts like an air
   // traffic controller here, forwarding incoming messages to appropriate
   // landing threads.
diff --git a/components/sync/driver/glue/sync_engine_impl_unittest.cc b/components/sync/driver/glue/sync_engine_impl_unittest.cc
index 5d14877f..59c88172 100644
--- a/components/sync/driver/glue/sync_engine_impl_unittest.cc
+++ b/components/sync/driver/glue/sync_engine_impl_unittest.cc
@@ -310,9 +310,6 @@
   InitializeBackend(true);
   EXPECT_EQ(ControlTypes(), fake_manager_->GetAndResetDownloadedTypes());
   EXPECT_EQ(ControlTypes(), fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(ControlTypes())
-          .Empty());
 }
 
 // Test first time sync scenario. All types should be properly configured.
@@ -321,9 +318,6 @@
   InitializeBackend(true);
   EXPECT_EQ(ControlTypes(), fake_manager_->GetAndResetDownloadedTypes());
   EXPECT_EQ(ControlTypes(), fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(ControlTypes())
-          .Empty());
 
   ModelTypeSet ready_types = ConfigureDataTypes();
   // Nigori is always downloaded so won't be ready.
@@ -331,127 +325,35 @@
   EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().HasAll(
       Difference(enabled_types_, ControlTypes())));
   EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
 }
 
 // Test the restart after setting up sync scenario. No enabled types should be
-// downloaded or cleaned.
+// downloaded.
 TEST_F(SyncEngineImplTest, Restart) {
   sync_prefs_->SetFirstSetupComplete();
   fake_manager_factory_->set_progress_marker_types(enabled_types_);
   fake_manager_factory_->set_initial_sync_ended_types(enabled_types_);
   InitializeBackend(true);
   EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty());
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
-          .Empty());
   EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
 
   ModelTypeSet ready_types = ConfigureDataTypes();
   EXPECT_EQ(enabled_types_, ready_types);
   EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty());
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
-          .Empty());
   EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
-}
-
-// Test a sync restart scenario where some types had never finished configuring.
-// The partial types should be purged, then reconfigured properly.
-TEST_F(SyncEngineImplTest, PartialTypes) {
-  sync_prefs_->SetFirstSetupComplete();
-  // Set sync manager behavior before passing it down. All types have progress
-  // markers, but nigori and bookmarks are missing initial sync ended.
-  ModelTypeSet partial_types(NIGORI, BOOKMARKS);
-  ModelTypeSet full_types = Difference(enabled_types_, partial_types);
-  fake_manager_factory_->set_progress_marker_types(enabled_types_);
-  fake_manager_factory_->set_initial_sync_ended_types(full_types);
-
-  // Bringing up the backend should purge all partial types, then proceed to
-  // download the Nigori.
-  InitializeBackend(true);
-  EXPECT_EQ(ModelTypeSet(NIGORI), fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_TRUE(fake_manager_->GetAndResetPurgedTypes().HasAll(partial_types));
-  EXPECT_EQ(Union(full_types, ModelTypeSet(NIGORI)),
-            fake_manager_->InitialSyncEndedTypes());
-  EXPECT_EQ(
-      Difference(partial_types, ModelTypeSet(NIGORI)),
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_));
-
-  // Now do the actual configuration, which should download and apply bookmarks.
-  ModelTypeSet ready_types = ConfigureDataTypes();
-  EXPECT_EQ(full_types, ready_types);
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
-          .Empty());
-  EXPECT_EQ(partial_types, fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
-}
-
-// Test the behavior when we lose the sync db. Although we already have types
-// enabled, we should re-download all of them because we lost their data.
-TEST_F(SyncEngineImplTest, LostDB) {
-  sync_prefs_->SetFirstSetupComplete();
-  // Initialization should fetch the Nigori node.  Everything else should be
-  // left untouched.
-  InitializeBackend(true);
-  EXPECT_EQ(ModelTypeSet(ControlTypes()),
-            fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_EQ(ModelTypeSet(ControlTypes()),
-            fake_manager_->InitialSyncEndedTypes());
-  EXPECT_EQ(
-      Difference(enabled_types_, ControlTypes()),
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_));
-
-  // The database was empty, so any cleaning is entirely optional.  We want to
-  // reset this value before running the next part of the test, though.
-  fake_manager_->GetAndResetPurgedTypes();
-
-  // The actual configuration should redownload and apply all the enabled types.
-  ModelTypeSet ready_types = ConfigureDataTypes();
-  // Nigori is always downloaded so won't be ready.
-  EXPECT_EQ(Difference(ControlTypes(), ModelTypeSet(NIGORI)), ready_types);
-  EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().HasAll(
-      Difference(enabled_types_, ControlTypes())));
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
-          .Empty());
-  EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
 }
 
 TEST_F(SyncEngineImplTest, DisableTypes) {
   // Simulate first time sync.
   InitializeBackend(true);
-  fake_manager_->GetAndResetPurgedTypes();
   ModelTypeSet ready_types = ConfigureDataTypes();
   // Nigori is always downloaded so won't be ready.
   EXPECT_EQ(Difference(ControlTypes(), ModelTypeSet(NIGORI)), ready_types);
   EXPECT_EQ(enabled_types_, fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
-          .Empty());
   EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
 
   // Then disable two datatypes.
   ModelTypeSet disabled_types(BOOKMARKS, SEARCH_ENGINES);
-  ModelTypeSet old_types = enabled_types_;
   enabled_types_.RemoveAll(disabled_types);
   ready_types = ConfigureDataTypes();
 
@@ -459,29 +361,16 @@
   // downloaded.
   EXPECT_EQ(enabled_types_, ready_types);
   EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty());
-  EXPECT_EQ(disabled_types,
-            Intersection(fake_manager_->GetAndResetPurgedTypes(), old_types));
-  EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
 }
 
 TEST_F(SyncEngineImplTest, AddTypes) {
   // Simulate first time sync.
   InitializeBackend(true);
-  fake_manager_->GetAndResetPurgedTypes();
   ModelTypeSet ready_types = ConfigureDataTypes();
   // Nigori is always downloaded so won't be ready.
   EXPECT_EQ(Difference(ControlTypes(), ModelTypeSet(NIGORI)), ready_types);
   EXPECT_EQ(enabled_types_, fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
-          .Empty());
   EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
 
   // Then add two datatypes.
   ModelTypeSet new_types(EXTENSIONS, APPS);
@@ -493,34 +382,20 @@
   new_types.Put(NIGORI);
   EXPECT_EQ(Difference(enabled_types_, new_types), ready_types);
   EXPECT_EQ(new_types, fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
-          .Empty());
   EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
 }
 
 // And and disable in the same configuration.
 TEST_F(SyncEngineImplTest, AddDisableTypes) {
   // Simulate first time sync.
   InitializeBackend(true);
-  fake_manager_->GetAndResetPurgedTypes();
   ModelTypeSet ready_types = ConfigureDataTypes();
   // Nigori is always downloaded so won't be ready.
   EXPECT_EQ(Difference(ControlTypes(), ModelTypeSet(NIGORI)), ready_types);
   EXPECT_EQ(enabled_types_, fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
-          .Empty());
   EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
 
   // Then add two datatypes.
-  ModelTypeSet old_types = enabled_types_;
   ModelTypeSet disabled_types(BOOKMARKS, SEARCH_ENGINES);
   ModelTypeSet new_types(EXTENSIONS, APPS);
   enabled_types_.PutAll(new_types);
@@ -532,11 +407,6 @@
   new_types.Put(NIGORI);
   EXPECT_EQ(Difference(enabled_types_, new_types), ready_types);
   EXPECT_EQ(new_types, fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_EQ(disabled_types,
-            Intersection(fake_manager_->GetAndResetPurgedTypes(), old_types));
-  EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_EQ(disabled_types,
-            fake_manager_->GetTypesWithEmptyProgressMarkerToken(old_types));
 }
 
 // Test restarting the browser to newly supported datatypes. The new datatypes
@@ -554,11 +424,7 @@
   // Does nothing.
   InitializeBackend(true);
   EXPECT_TRUE(fake_manager_->GetAndResetDownloadedTypes().Empty());
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), old_types).Empty());
   EXPECT_EQ(old_types, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_EQ(new_types, fake_manager_->GetTypesWithEmptyProgressMarkerToken(
-                           enabled_types_));
 
   // Downloads and applies the new types (plus nigori).
   ModelTypeSet ready_types = ConfigureDataTypes();
@@ -566,54 +432,7 @@
   new_types.Put(NIGORI);
   EXPECT_EQ(Difference(old_types, ModelTypeSet(NIGORI)), ready_types);
   EXPECT_EQ(new_types, fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
-          .Empty());
   EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
-}
-
-// Test the newly supported types scenario, but with the presence of partial
-// types as well. Both partial and newly supported types should be downloaded
-// the configuration.
-TEST_F(SyncEngineImplTest, NewlySupportedTypesWithPartialTypes) {
-  sync_prefs_->SetFirstSetupComplete();
-  // Set sync manager behavior before passing it down. All types have progress
-  // markers and initial sync ended except the new types.
-  ModelTypeSet old_types = enabled_types_;
-  ModelTypeSet partial_types(NIGORI, BOOKMARKS);
-  ModelTypeSet full_types = Difference(enabled_types_, partial_types);
-  fake_manager_factory_->set_progress_marker_types(old_types);
-  fake_manager_factory_->set_initial_sync_ended_types(full_types);
-  ModelTypeSet new_types(APP_SETTINGS, EXTENSION_SETTINGS);
-  enabled_types_.PutAll(new_types);
-
-  // Purge the partial types.  The nigori will be among the purged types, but
-  // the syncer will re-download it by the time the initialization is complete.
-  InitializeBackend(true);
-  EXPECT_EQ(ModelTypeSet(NIGORI), fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_TRUE(fake_manager_->GetAndResetPurgedTypes().HasAll(partial_types));
-  EXPECT_EQ(Union(full_types, ModelTypeSet(NIGORI)),
-            fake_manager_->InitialSyncEndedTypes());
-  EXPECT_EQ(
-      Union(new_types, Difference(partial_types, ModelTypeSet(NIGORI))),
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_));
-
-  // Downloads and applies the new types and partial types (which includes
-  // nigori anyways).
-  ModelTypeSet ready_types = ConfigureDataTypes();
-  EXPECT_EQ(full_types, ready_types);
-  EXPECT_EQ(Union(new_types, partial_types),
-            fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_TRUE(
-      Intersection(fake_manager_->GetAndResetPurgedTypes(), enabled_types_)
-          .Empty());
-  EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
 }
 
 // Verify that downloading control types only downloads those types that do
@@ -632,12 +451,7 @@
   // any old types.
   InitializeBackend(true);
   EXPECT_EQ(new_types, fake_manager_->GetAndResetDownloadedTypes());
-  EXPECT_EQ(Difference(ModelTypeSet::All(), enabled_types_),
-            fake_manager_->GetAndResetPurgedTypes());
   EXPECT_EQ(enabled_types_, fake_manager_->InitialSyncEndedTypes());
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(enabled_types_)
-          .Empty());
 }
 
 // Fail to download control types.  It's believed that there is a server bug
@@ -702,15 +516,11 @@
   // Then mark the error types as unready (disables without purging).
   ready_types = ConfigureDataTypesWithUnready(error_types);
   EXPECT_EQ(Difference(enabled_types_, error_types), ready_types);
-  EXPECT_TRUE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(error_types).Empty());
 
   // Lastly explicitly disable the error types, which should result in a purge.
   enabled_types_.RemoveAll(error_types);
   ready_types = ConfigureDataTypes();
   EXPECT_EQ(Difference(enabled_types_, error_types), ready_types);
-  EXPECT_FALSE(
-      fake_manager_->GetTypesWithEmptyProgressMarkerToken(error_types).Empty());
 }
 
 // Tests that SyncEngineImpl retains ModelTypeConnector after call to
diff --git a/components/sync/driver/model_association_manager.cc b/components/sync/driver/model_association_manager.cc
index dcb978c..931ccb90 100644
--- a/components/sync/driver/model_association_manager.cc
+++ b/components/sync/driver/model_association_manager.cc
@@ -180,8 +180,7 @@
   delegate_->OnSingleDataTypeWillStop(dtc->type(), error);
 
   // Note: Depending on |shutdown_reason|, USS types might clear their metadata
-  // in response to Stop(). For directory types, the clearing happens in
-  // SyncManager::PurgeDisabledTypes() instead.
+  // in response to Stop().
   dtc->Stop(shutdown_reason, std::move(callback));
 }
 
diff --git a/components/sync/engine/DEPS b/components/sync/engine/DEPS
index 07bbd4d..d34bede 100644
--- a/components/sync/engine/DEPS
+++ b/components/sync/engine/DEPS
@@ -4,6 +4,5 @@
   "+components/sync/model",
   "+components/sync/nigori",
   "+components/sync/protocol",
-  "+components/sync/syncable",
   "+components/sync/test",
 ]
diff --git a/components/sync/engine/engine_components_factory.h b/components/sync/engine/engine_components_factory.h
index 49bea214..b72783ac 100644
--- a/components/sync/engine/engine_components_factory.h
+++ b/components/sync/engine/engine_components_factory.h
@@ -24,11 +24,6 @@
 class SyncEngineEventListener;
 class SyncScheduler;
 
-namespace syncable {
-class Directory;
-class DirectoryBackingStore;
-}
-
 // EngineComponentsFactory exists so that tests can override creation of
 // components used by the SyncManager and other things inside engine/.
 class EngineComponentsFactory {
@@ -58,18 +53,6 @@
     bool force_short_nudge_delay_for_test;
   };
 
-  // For selecting the types of storage to use to persist sync data when
-  // BuildDirectoryBackingStore() is called.
-  enum StorageOption {
-    // BuildDirectoryBackingStore should not use persistent on-disk storage.
-    STORAGE_IN_MEMORY,
-    // Use this if you want BuildDirectoryBackingStore to create/use a real
-    // on disk store.
-    STORAGE_ON_DISK,
-    // Use this to test the case where a directory fails to load.
-    STORAGE_INVALID
-  };
-
   virtual ~EngineComponentsFactory() {}
 
   virtual std::unique_ptr<SyncScheduler> BuildScheduler(
@@ -80,21 +63,15 @@
 
   virtual std::unique_ptr<SyncCycleContext> BuildContext(
       ServerConnectionManager* connection_manager,
-      syncable::Directory* directory,
       ExtensionsActivity* extensions_activity,
       const std::vector<SyncEngineEventListener*>& listeners,
       DebugInfoGetter* debug_info_getter,
       ModelTypeRegistry* model_type_registry,
       const std::string& invalidator_client_id,
+      const std::string& cache_guid,
       const std::string& store_birthday,
       const std::string& bag_of_chips,
       base::TimeDelta poll_interval) = 0;
-
-  virtual std::unique_ptr<syncable::DirectoryBackingStore>
-  BuildDirectoryBackingStore(StorageOption storage,
-                             const std::string& dir_name,
-                             const std::string& cache_guid,
-                             const base::FilePath& backing_filepath) = 0;
 };
 
 }  // namespace syncer
diff --git a/components/sync/engine/engine_components_factory_impl.cc b/components/sync/engine/engine_components_factory_impl.cc
index 01d9746..98be1f5 100644
--- a/components/sync/engine/engine_components_factory_impl.cc
+++ b/components/sync/engine/engine_components_factory_impl.cc
@@ -11,7 +11,6 @@
 #include "components/sync/engine_impl/cycle/sync_cycle_context.h"
 #include "components/sync/engine_impl/sync_scheduler_impl.h"
 #include "components/sync/engine_impl/syncer.h"
-#include "components/sync/syncable/on_disk_directory_backing_store.h"
 
 namespace syncer {
 
@@ -45,35 +44,19 @@
 
 std::unique_ptr<SyncCycleContext> EngineComponentsFactoryImpl::BuildContext(
     ServerConnectionManager* connection_manager,
-    syncable::Directory* directory,
     ExtensionsActivity* extensions_activity,
     const std::vector<SyncEngineEventListener*>& listeners,
     DebugInfoGetter* debug_info_getter,
     ModelTypeRegistry* model_type_registry,
     const std::string& invalidation_client_id,
+    const std::string& cache_guid,
     const std::string& store_birthday,
     const std::string& bag_of_chips,
     base::TimeDelta poll_interval) {
   return std::make_unique<SyncCycleContext>(
-      connection_manager, directory, extensions_activity, listeners,
-      debug_info_getter, model_type_registry, invalidation_client_id,
-      store_birthday, bag_of_chips, poll_interval);
-}
-
-std::unique_ptr<syncable::DirectoryBackingStore>
-EngineComponentsFactoryImpl::BuildDirectoryBackingStore(
-    StorageOption storage,
-    const std::string& dir_name,
-    const std::string& cache_guid,
-    const base::FilePath& backing_filepath) {
-  if (storage == STORAGE_ON_DISK) {
-    return std::unique_ptr<syncable::DirectoryBackingStore>(
-        new syncable::OnDiskDirectoryBackingStore(dir_name, cache_guid,
-                                                  backing_filepath));
-  } else {
-    NOTREACHED();
-    return std::unique_ptr<syncable::DirectoryBackingStore>();
-  }
+      connection_manager, extensions_activity, listeners, debug_info_getter,
+      model_type_registry, invalidation_client_id, cache_guid, store_birthday,
+      bag_of_chips, poll_interval);
 }
 
 }  // namespace syncer
diff --git a/components/sync/engine/engine_components_factory_impl.h b/components/sync/engine/engine_components_factory_impl.h
index c47148d..74e3b737 100644
--- a/components/sync/engine/engine_components_factory_impl.h
+++ b/components/sync/engine/engine_components_factory_impl.h
@@ -29,22 +29,16 @@
 
   std::unique_ptr<SyncCycleContext> BuildContext(
       ServerConnectionManager* connection_manager,
-      syncable::Directory* directory,
       ExtensionsActivity* extensions_activity,
       const std::vector<SyncEngineEventListener*>& listeners,
       DebugInfoGetter* debug_info_getter,
       ModelTypeRegistry* model_type_registry,
       const std::string& invalidator_client_id,
+      const std::string& cache_guid,
       const std::string& store_birthday,
       const std::string& bag_of_chips,
       base::TimeDelta poll_interval) override;
 
-  std::unique_ptr<syncable::DirectoryBackingStore> BuildDirectoryBackingStore(
-      StorageOption storage,
-      const std::string& dir_name,
-      const std::string& cache_guid,
-      const base::FilePath& backing_filepath) override;
-
  private:
   const Switches switches_;
   DISALLOW_COPY_AND_ASSIGN(EngineComponentsFactoryImpl);
diff --git a/components/sync/engine/fake_sync_manager.cc b/components/sync/engine/fake_sync_manager.cc
index fe605da..082653e6 100644
--- a/components/sync/engine/fake_sync_manager.cc
+++ b/components/sync/engine/fake_sync_manager.cc
@@ -36,12 +36,6 @@
 
 FakeSyncManager::~FakeSyncManager() {}
 
-ModelTypeSet FakeSyncManager::GetAndResetPurgedTypes() {
-  ModelTypeSet purged_types = purged_types_;
-  purged_types_.Clear();
-  return purged_types;
-}
-
 ModelTypeSet FakeSyncManager::GetAndResetDownloadedTypes() {
   ModelTypeSet downloaded_types = downloaded_types_;
   downloaded_types_.Clear();
@@ -70,7 +64,6 @@
 
 void FakeSyncManager::Init(InitArgs* args) {
   sync_task_runner_ = base::SequencedTaskRunnerHandle::Get();
-  PurgePartiallySyncedTypes();
 
   for (auto& observer : observers_) {
     observer.OnInitializationComplete(WeakHandle<JsBackend>(),
@@ -83,30 +76,6 @@
   return initial_sync_ended_types_;
 }
 
-ModelTypeSet FakeSyncManager::GetTypesWithEmptyProgressMarkerToken(
-    ModelTypeSet types) {
-  ModelTypeSet empty_types = types;
-  empty_types.RemoveAll(progress_marker_types_);
-  return empty_types;
-}
-
-void FakeSyncManager::PurgePartiallySyncedTypes() {
-  ModelTypeSet partial_types;
-  for (ModelType type : progress_marker_types_) {
-    if (!initial_sync_ended_types_.Has(type))
-      partial_types.Put(type);
-  }
-  progress_marker_types_.RemoveAll(partial_types);
-  purged_types_.PutAll(partial_types);
-}
-
-void FakeSyncManager::PurgeDisabledTypes(ModelTypeSet to_purge) {
-  // Simulate cleaning up disabled types.
-  purged_types_.PutAll(to_purge);
-  initial_sync_ended_types_.RemoveAll(to_purge);
-  progress_marker_types_.RemoveAll(to_purge);
-}
-
 void FakeSyncManager::UpdateCredentials(const SyncCredentials& credentials) {
   NOTIMPLEMENTED();
 }
@@ -225,10 +194,6 @@
 void FakeSyncManager::OnCookieJarChanged(bool account_mismatch,
                                          bool empty_jar) {}
 
-void FakeSyncManager::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd) {
-  NOTIMPLEMENTED();
-}
-
 void FakeSyncManager::UpdateInvalidationClientId(const std::string&) {
   NOTIMPLEMENTED();
 }
diff --git a/components/sync/engine/fake_sync_manager.h b/components/sync/engine/fake_sync_manager.h
index 781d457..e0a0d5e 100644
--- a/components/sync/engine/fake_sync_manager.h
+++ b/components/sync/engine/fake_sync_manager.h
@@ -45,10 +45,6 @@
                   bool should_fail_on_init);
   ~FakeSyncManager() override;
 
-  // Returns those types that have been purged from the directory since the last
-  // call to GetAndResetPurgedTypes(), or since startup if never called.
-  ModelTypeSet GetAndResetPurgedTypes();
-
   // Returns those types that have been downloaded since the last call to
   // GetAndResetDownloadedTypes(), or since startup if never called.
   ModelTypeSet GetAndResetDownloadedTypes();
@@ -71,10 +67,6 @@
   // loop for purposes of callbacks.
   void Init(InitArgs* args) override;
   ModelTypeSet InitialSyncEndedTypes() override;
-  ModelTypeSet GetTypesWithEmptyProgressMarkerToken(
-      ModelTypeSet types) override;
-  void PurgePartiallySyncedTypes() override;
-  void PurgeDisabledTypes(ModelTypeSet to_purge) override;
   void UpdateCredentials(const SyncCredentials& credentials) override;
   void InvalidateCredentials() override;
   void StartSyncingNormally(base::Time last_poll_time) override;
@@ -108,7 +100,6 @@
       TypeDebugInfoObserver* observer) override;
   void RequestEmitDebugInfo() override;
   void OnCookieJarChanged(bool account_mismatch, bool empty_jar) override;
-  void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd) override;
   void UpdateInvalidationClientId(const std::string&) override;
 
  private:
@@ -125,8 +116,7 @@
   // The types that should fail configuration attempts. These types will not
   // have their progress markers or initial_sync_ended bits set.
   ModelTypeSet configure_fail_types_;
-  // The set of types that have been purged.
-  ModelTypeSet purged_types_;
+
   // The set of types that have been downloaded.
   ModelTypeSet downloaded_types_;
 
diff --git a/components/sync/engine/sync_backend_registrar.cc b/components/sync/engine/sync_backend_registrar.cc
index ef377fc..9c1e66f6 100644
--- a/components/sync/engine/sync_backend_registrar.cc
+++ b/components/sync/engine/sync_backend_registrar.cc
@@ -9,8 +9,6 @@
 #include <utility>
 
 #include "base/logging.h"
-#include "components/sync/syncable/change_processor.h"
-#include "components/sync/syncable/user_share.h"
 
 namespace syncer {
 
@@ -120,33 +118,6 @@
   }
 }
 
-bool SyncBackendRegistrar::IsTypeActivatedForTest(ModelType type) const {
-  return GetProcessor(type) != nullptr;
-}
-
-void SyncBackendRegistrar::OnChangesApplied(
-    ModelType model_type,
-    int64_t model_version,
-    const BaseTransaction* trans,
-    const ImmutableChangeRecordList& changes) {
-  ChangeProcessor* processor = GetProcessor(model_type);
-  if (!processor)
-    return;
-
-  processor->ApplyChangesFromSyncModel(trans, model_version, changes);
-}
-
-void SyncBackendRegistrar::OnChangesComplete(ModelType model_type) {
-  ChangeProcessor* processor = GetProcessor(model_type);
-  if (!processor)
-    return;
-
-  // This call just notifies the processor that it can commit; it
-  // already buffered any changes it plans to makes so needs no
-  // further information.
-  processor->CommitChangesFromSyncModel();
-}
-
 void SyncBackendRegistrar::GetWorkers(
     std::vector<scoped_refptr<ModelSafeWorker>>* out) {
   base::AutoLock lock(lock_);
@@ -162,35 +133,6 @@
   out->swap(copy);
 }
 
-ChangeProcessor* SyncBackendRegistrar::GetProcessor(ModelType type) const {
-  base::AutoLock lock(lock_);
-  ChangeProcessor* processor = GetProcessorUnsafe(type);
-  if (!processor)
-    return nullptr;
-
-  // We can only check if |processor| exists, as otherwise the type is
-  // mapped to GROUP_PASSIVE.
-  DCHECK(IsCurrentThreadSafeForModel(type));
-  return processor;
-}
-
-ChangeProcessor* SyncBackendRegistrar::GetProcessorUnsafe(
-    ModelType type) const {
-  lock_.AssertAcquired();
-  auto it = processors_.find(type);
-
-  // Until model association happens for a datatype, it will not
-  // appear in the processors list.  During this time, it is OK to
-  // drop changes on the floor (since model association has not
-  // happened yet).  When the data type is activated, model
-  // association takes place then the change processor is added to the
-  // |processors_| list.
-  if (it == processors_.end())
-    return nullptr;
-
-  return it->second;
-}
-
 bool SyncBackendRegistrar::IsCurrentThreadSafeForModel(
     ModelType model_type) const {
   lock_.AssertAcquired();
@@ -207,8 +149,6 @@
 }
 
 SyncBackendRegistrar::~SyncBackendRegistrar() {
-  // All data types should have been deactivated by now.
-  DCHECK(processors_.empty());
 }
 
 void SyncBackendRegistrar::MaybeAddWorker(ModelSafeWorkerFactory worker_factory,
diff --git a/components/sync/engine/sync_backend_registrar.h b/components/sync/engine/sync_backend_registrar.h
index e4f80c5..fe81005 100644
--- a/components/sync/engine/sync_backend_registrar.h
+++ b/components/sync/engine/sync_backend_registrar.h
@@ -23,12 +23,9 @@
 
 namespace syncer {
 
-class ChangeProcessor;
-
-// A class that keep track of the workers, change processors, and
-// routing info for the enabled sync types, and also routes change
-// events to the right processors.
-class SyncBackendRegistrar : public SyncManager::ChangeDelegate {
+// A class that keep track of the workers and routing info for the enabled sync
+// types.
+class SyncBackendRegistrar {
  public:
   using ModelSafeWorkerFactory =
       base::RepeatingCallback<scoped_refptr<ModelSafeWorker>(ModelSafeGroup)>;
@@ -48,7 +45,7 @@
   //   4) Post a task to delete the SyncBackendRegistrar on the sync thread.
   //      When this task runs, there are no remaining pointers to the
   //      SyncBackendRegistrar.
-  ~SyncBackendRegistrar() override;
+  ~SyncBackendRegistrar();
 
   // Adds |type| to set of non-blocking types. These types are assigned to
   // GROUP_NON_BLOCKING model safe group and will be treated differently in
@@ -86,18 +83,6 @@
   // Must be called from the UI thread. (See destructor comment.)
   void RequestWorkerStopOnUIThread();
 
-  // Returns true only between calls to ActivateDataType(type, ...)
-  // and DeactivateDataType(type).  Used only by tests.
-  bool IsTypeActivatedForTest(ModelType type) const;
-
-  // SyncManager::ChangeDelegate implementation.  May be called from
-  // any thread.
-  void OnChangesApplied(ModelType model_type,
-                        int64_t model_version,
-                        const BaseTransaction* trans,
-                        const ImmutableChangeRecordList& changes) override;
-  void OnChangesComplete(ModelType model_type) override;
-
   void GetWorkers(std::vector<scoped_refptr<ModelSafeWorker>>* out);
   void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out);
 
@@ -107,15 +92,6 @@
   void MaybeAddWorker(ModelSafeWorkerFactory worker_factory,
                       ModelSafeGroup group);
 
-  // Returns the change processor for the given model, or null if none
-  // exists.  Must be called from |group|'s native thread.
-  ChangeProcessor* GetProcessor(ModelType type) const;
-
-  // Must be called with |lock_| held.  Simply returns the change
-  // processor for the given type, if it exists.  May be called from
-  // any thread.
-  ChangeProcessor* GetProcessorUnsafe(ModelType type) const;
-
   // Return true if |model_type| lives on the current thread.  Must be
   // called with |lock_| held.  May be called on any thread.
   bool IsCurrentThreadSafeForModel(ModelType model_type) const;
@@ -137,9 +113,6 @@
   // Workers created by this SyncBackendRegistrar.
   std::map<ModelSafeGroup, scoped_refptr<ModelSafeWorker>> workers_;
 
-  // The change processors that handle the different data types.
-  std::map<ModelType, ChangeProcessor*> processors_;
-
   // Maps ModelType to ModelSafeGroup.
   ModelSafeRoutingInfo routing_info_;
 
@@ -147,8 +120,7 @@
   // call to ConfigureDataTypes as well as SetInitialTypes.
   ModelTypeSet last_configured_types_;
 
-  // Set of types with non-blocking implementation (as opposed to directory
-  // based).
+  // Set of types with non-blocking implementation.
   ModelTypeSet non_blocking_types_;
 
   DISALLOW_COPY_AND_ASSIGN(SyncBackendRegistrar);
diff --git a/components/sync/engine/sync_backend_registrar_unittest.cc b/components/sync/engine/sync_backend_registrar_unittest.cc
index b8b8db60..b4f3c8d 100644
--- a/components/sync/engine/sync_backend_registrar_unittest.cc
+++ b/components/sync/engine/sync_backend_registrar_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread.h"
 #include "components/sync/engine/passive_model_worker.h"
-#include "components/sync/syncable/test_user_share.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -19,11 +18,6 @@
 
 namespace {
 
-using ::testing::_;
-using ::testing::InSequence;
-using ::testing::Return;
-using ::testing::StrictMock;
-
 class SyncBackendRegistrarTest : public testing::Test {
  public:
   SyncBackendRegistrarTest()
@@ -33,7 +27,6 @@
   void SetUp() override {
     db_thread_.StartAndWaitForTesting();
     sync_thread_.StartAndWaitForTesting();
-    test_user_share_.SetUp();
     registrar_ = std::make_unique<SyncBackendRegistrar>(
         "test", base::BindRepeating(
                     &SyncBackendRegistrarTest::CreateModelWorkerForGroup,
@@ -42,30 +35,16 @@
 
   void TearDown() override {
     registrar_->RequestWorkerStopOnUIThread();
-    test_user_share_.TearDown();
     sync_thread_.task_runner()->DeleteSoon(FROM_HERE, registrar_.release());
     sync_thread_.FlushForTesting();
   }
 
-  void TriggerChanges(ModelType type) {
-    registrar_->OnChangesApplied(type, 0, nullptr, ImmutableChangeRecordList());
-    registrar_->OnChangesComplete(type);
-  }
-
   void ExpectRoutingInfo(const ModelSafeRoutingInfo& expected_routing_info) {
     ModelSafeRoutingInfo actual_routing_info;
     registrar_->GetModelSafeRoutingInfo(&actual_routing_info);
     EXPECT_EQ(expected_routing_info, actual_routing_info);
   }
 
-  void ExpectHasProcessorsForTypes(ModelTypeSet types) {
-    for (int i = FIRST_REAL_MODEL_TYPE; i < ModelType::NUM_ENTRIES; ++i) {
-      ModelType model_type = ModelTypeFromInt(i);
-      EXPECT_EQ(types.Has(model_type),
-                registrar_->IsTypeActivatedForTest(model_type));
-    }
-  }
-
   size_t GetWorkersSize() {
     std::vector<scoped_refptr<ModelSafeWorker>> workers;
     registrar_->GetWorkers(&workers);
@@ -73,7 +52,6 @@
   }
 
   SyncBackendRegistrar* registrar() { return registrar_.get(); }
-  UserShare* user_share() { return test_user_share_.user_share(); }
   scoped_refptr<base::SequencedTaskRunner> db_task_runner() {
     return db_thread_.task_runner();
   }
@@ -93,7 +71,6 @@
   base::Thread db_thread_;
   base::Thread sync_thread_;
 
-  TestUserShare test_user_share_;
   std::unique_ptr<SyncBackendRegistrar> registrar_;
 };
 
@@ -102,7 +79,6 @@
   EXPECT_FALSE(registrar()->IsNigoriEnabled());
   EXPECT_EQ(1u, GetWorkersSize());
   ExpectRoutingInfo(ModelSafeRoutingInfo());
-  ExpectHasProcessorsForTypes(ModelTypeSet());
 }
 
 TEST_F(SyncBackendRegistrarTest, ConstructorNonEmpty) {
@@ -114,7 +90,6 @@
   // Bookmarks dropped because it is nonblocking.
   // Passwords dropped because of no password store.
   ExpectRoutingInfo({{NIGORI, GROUP_PASSIVE}});
-  ExpectHasProcessorsForTypes(ModelTypeSet());
 }
 
 TEST_F(SyncBackendRegistrarTest, ConstructorNonEmptyReversedInitialization) {
@@ -127,7 +102,6 @@
   // Bookmarks dropped because it is nonblocking.
   // Passwords dropped because of no password store.
   ExpectRoutingInfo({{NIGORI, GROUP_PASSIVE}});
-  ExpectHasProcessorsForTypes(ModelTypeSet());
 }
 
 TEST_F(SyncBackendRegistrarTest, ConfigureDataTypes) {
@@ -140,7 +114,6 @@
   ExpectRoutingInfo({{BOOKMARKS, GROUP_NON_BLOCKING},
                      {NIGORI, GROUP_PASSIVE},
                      {AUTOFILL, GROUP_PASSIVE}});
-  ExpectHasProcessorsForTypes(ModelTypeSet());
   EXPECT_EQ(types1, registrar()->GetLastConfiguredTypes());
 
   // Add and remove.
@@ -148,13 +121,11 @@
   EXPECT_EQ(types2, registrar()->ConfigureDataTypes(types2, types1));
 
   ExpectRoutingInfo({{PREFERENCES, GROUP_PASSIVE}, {THEMES, GROUP_PASSIVE}});
-  ExpectHasProcessorsForTypes(ModelTypeSet());
   EXPECT_EQ(types2, registrar()->GetLastConfiguredTypes());
 
   // Remove.
   EXPECT_TRUE(registrar()->ConfigureDataTypes(ModelTypeSet(), types2).Empty());
   ExpectRoutingInfo(ModelSafeRoutingInfo());
-  ExpectHasProcessorsForTypes(ModelTypeSet());
   EXPECT_EQ(ModelTypeSet(), registrar()->GetLastConfiguredTypes());
 }
 
diff --git a/components/sync/engine/sync_manager.cc b/components/sync/engine/sync_manager.cc
index 8c2939f..5d96663a 100644
--- a/components/sync/engine/sync_manager.cc
+++ b/components/sync/engine/sync_manager.cc
@@ -6,16 +6,11 @@
 
 namespace syncer {
 
-SyncManager::ChangeDelegate::~ChangeDelegate() {}
-
-SyncManager::ChangeObserver::~ChangeObserver() {}
-
 SyncManager::Observer::~Observer() {}
 
 SyncManager::InitArgs::InitArgs()
     : enable_local_sync_backend(false),
       extensions_activity(nullptr),
-      change_delegate(nullptr),
       encryption_handler(nullptr),
       cancelation_signal(nullptr) {}
 
diff --git a/components/sync/engine/sync_manager.h b/components/sync/engine/sync_manager.h
index eb505b5..5041fa0e 100644
--- a/components/sync/engine/sync_manager.h
+++ b/components/sync/engine/sync_manager.h
@@ -31,18 +31,10 @@
 #include "components/sync/engine/sync_encryption_handler.h"
 #include "components/sync/engine/sync_status.h"
 #include "components/sync/protocol/sync_protocol_error.h"
-#include "components/sync/syncable/change_record.h"
 #include "url/gurl.h"
 
-namespace base {
-namespace trace_event {
-class ProcessMemoryDump;
-}  // namespace trace_event
-}  // namespace base
-
 namespace syncer {
 
-class BaseTransaction;
 class CancelationSignal;
 class DataTypeDebugInfoListener;
 class EngineComponentsFactory;
@@ -54,99 +46,10 @@
 class SyncStatusObserver;
 class TypeDebugInfoObserver;
 
-// SyncManager encapsulates syncable::Directory and serves as the parent of all
-// other objects in the sync API.  If multiple threads interact with the same
-// local sync repository (i.e. the same sqlite database), they should share a
-// single SyncManager instance.  The caller should typically create one
-// SyncManager for the lifetime of a user session.
-//
 // Unless stated otherwise, all methods of SyncManager should be called on the
 // same thread.
 class SyncManager {
  public:
-  // An interface the embedding application implements to be notified
-  // on change events.  Note that these methods may be called on *any*
-  // thread.
-  class ChangeDelegate {
-   public:
-    // Notify the delegate that changes have been applied to the sync model.
-    //
-    // This will be invoked on the same thread as on which ApplyChanges was
-    // called. |changes| is an array of size |change_count|, and contains the
-    // ID of each individual item that was changed. |changes| exists only for
-    // the duration of the call. If items of multiple data types change at
-    // the same time, this method is invoked once per data type and |changes|
-    // is restricted to items of the ModelType indicated by |model_type|.
-    // Because the observer is passed a |trans|, the observer can assume a
-    // read lock on the sync model that will be released after the function
-    // returns.
-    //
-    // The SyncManager constructs |changes| in the following guaranteed order:
-    //
-    // 1. Deletions, from leaves up to parents.
-    // 2. Updates to existing items with synced parents & predecessors.
-    // 3. New items with synced parents & predecessors.
-    // 4. Items with parents & predecessors in |changes|.
-    // 5. Repeat #4 until all items are in |changes|.
-    //
-    // Thus, an implementation of OnChangesApplied should be able to
-    // process the change records in the order without having to worry about
-    // forward dependencies.  But since deletions come before reparent
-    // operations, a delete may temporarily orphan a node that is
-    // updated later in the list.
-    virtual void OnChangesApplied(ModelType model_type,
-                                  int64_t model_version,
-                                  const BaseTransaction* trans,
-                                  const ImmutableChangeRecordList& changes) = 0;
-
-    // OnChangesComplete gets called when the TransactionComplete event is
-    // posted (after OnChangesApplied finishes), after the transaction lock
-    // and the change channel mutex are released.
-    //
-    // The purpose of this function is to support processors that require
-    // split-transactions changes. For example, if a model processor wants to
-    // perform blocking I/O due to a change, it should calculate the changes
-    // while holding the transaction lock (from within OnChangesApplied), buffer
-    // those changes, let the transaction fall out of scope, and then commit
-    // those changes from within OnChangesComplete (postponing the blocking
-    // I/O to when it no longer holds any lock).
-    virtual void OnChangesComplete(ModelType model_type) = 0;
-
-   protected:
-    virtual ~ChangeDelegate();
-  };
-
-  // Like ChangeDelegate, except called only on the sync thread and
-  // not while a transaction is held.  For objects that want to know
-  // when changes happen, but don't need to process them.
-  class ChangeObserver {
-   public:
-    // Ids referred to in |changes| may or may not be in the write
-    // transaction specified by |write_transaction_id|.  If they're
-    // not, that means that the node didn't actually change, but we
-    // marked them as changed for some other reason (e.g., siblings of
-    // re-ordered nodes).
-    //
-    // TODO(sync, long-term): Ideally, ChangeDelegate/Observer would
-    // be passed a transformed version of EntryKernelMutation instead
-    // of a transaction that would have to be used to look up the
-    // changed nodes.  That is, ChangeDelegate::OnChangesApplied()
-    // would still be called under the transaction, but all the needed
-    // data will be passed down.
-    //
-    // Even more ideally, we would have sync semantics such that we'd
-    // be able to apply changes without being under a transaction.
-    // But that's a ways off...
-    virtual void OnChangesApplied(ModelType model_type,
-                                  int64_t write_transaction_id,
-                                  const ImmutableChangeRecordList& changes) = 0;
-
-    virtual void OnChangesComplete(ModelType model_type) = 0;
-
-   protected:
-    virtual ~ChangeObserver();
-  };
-
   // An interface the embedding application implements to receive
   // notifications from the SyncManager.  Register an observer via
   // SyncManager::AddObserver.  All methods are called only on the
@@ -215,9 +118,6 @@
     // Must outlive SyncManager.
     ExtensionsActivity* extensions_activity;
 
-    // Must outlive SyncManager.
-    ChangeDelegate* change_delegate;
-
     CoreAccountId authenticated_account_id;
 
     // Unqiuely identifies this client to the invalidation notification server.
@@ -262,19 +162,6 @@
 
   virtual ModelTypeSet InitialSyncEndedTypes() = 0;
 
-  // Returns those types within |types| that have an empty progress marker
-  // token.
-  virtual ModelTypeSet GetTypesWithEmptyProgressMarkerToken(
-      ModelTypeSet types) = 0;
-
-  // Purge from the directory those types with non-empty progress markers
-  // but without initial synced ended set.
-  // Returns false if an error occurred, true otherwise.
-  virtual void PurgePartiallySyncedTypes() = 0;
-
-  // Purge those disabled types as specified by |to_purge|.
-  virtual void PurgeDisabledTypes(ModelTypeSet to_purge) = 0;
-
   // Update tokens that we're using in Sync. Email must stay the same.
   virtual void UpdateCredentials(const SyncCredentials& credentials) = 0;
 
@@ -353,6 +240,7 @@
   GetBufferedProtocolEvents() = 0;
 
   // Functions to manage registrations of DebugInfoObservers.
+  // TODO(crbug.com/923287): Delete because they no longer make any difference.
   virtual void RegisterDirectoryTypeDebugInfoObserver(
       TypeDebugInfoObserver* observer) = 0;
   virtual void UnregisterDirectoryTypeDebugInfoObserver(
@@ -369,9 +257,6 @@
   // Note: this does not trigger a sync cycle. It just updates the sync context.
   virtual void OnCookieJarChanged(bool account_mismatch, bool empty_jar) = 0;
 
-  // Adds memory usage statistics to |pmd| for chrome://tracing.
-  virtual void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd) = 0;
-
   // Updates invalidation client id.
   virtual void UpdateInvalidationClientId(const std::string& client_id) = 0;
 };
diff --git a/components/sync/engine/test_engine_components_factory.cc b/components/sync/engine/test_engine_components_factory.cc
index d439b2dc..00fec435 100644
--- a/components/sync/engine/test_engine_components_factory.cc
+++ b/components/sync/engine/test_engine_components_factory.cc
@@ -5,17 +5,11 @@
 #include "components/sync/engine/test_engine_components_factory.h"
 
 #include "components/sync/engine_impl/cycle/sync_cycle_context.h"
-#include "components/sync/syncable/in_memory_directory_backing_store.h"
-#include "components/sync/syncable/invalid_directory_backing_store.h"
-#include "components/sync/syncable/on_disk_directory_backing_store.h"
 #include "components/sync/test/engine/fake_sync_scheduler.h"
 
 namespace syncer {
 
-TestEngineComponentsFactory::TestEngineComponentsFactory(
-    StorageOption option,
-    StorageOption* storage_used)
-    : storage_override_(option), storage_used_(storage_used) {}
+TestEngineComponentsFactory::TestEngineComponentsFactory() {}
 
 TestEngineComponentsFactory::~TestEngineComponentsFactory() {}
 
@@ -29,46 +23,21 @@
 
 std::unique_ptr<SyncCycleContext> TestEngineComponentsFactory::BuildContext(
     ServerConnectionManager* connection_manager,
-    syncable::Directory* directory,
     ExtensionsActivity* monitor,
     const std::vector<SyncEngineEventListener*>& listeners,
     DebugInfoGetter* debug_info_getter,
     ModelTypeRegistry* model_type_registry,
     const std::string& invalidator_client_id,
+    const std::string& cache_guid,
     const std::string& store_birthday,
     const std::string& bag_of_chips,
     base::TimeDelta poll_interval) {
   // Tests don't wire up listeners.
   std::vector<SyncEngineEventListener*> empty_listeners;
   return std::unique_ptr<SyncCycleContext>(new SyncCycleContext(
-      connection_manager, directory, monitor, empty_listeners,
-      debug_info_getter, model_type_registry, invalidator_client_id,
-      store_birthday, bag_of_chips, poll_interval));
-}
-
-std::unique_ptr<syncable::DirectoryBackingStore>
-TestEngineComponentsFactory::BuildDirectoryBackingStore(
-    StorageOption storage,
-    const std::string& dir_name,
-    const std::string& cache_guid,
-    const base::FilePath& backing_filepath) {
-  if (storage_used_)
-    *storage_used_ = storage;
-
-  switch (storage_override_) {
-    case STORAGE_IN_MEMORY:
-      return std::unique_ptr<syncable::DirectoryBackingStore>(
-          new syncable::InMemoryDirectoryBackingStore(dir_name, cache_guid));
-    case STORAGE_ON_DISK:
-      return std::unique_ptr<syncable::DirectoryBackingStore>(
-          new syncable::OnDiskDirectoryBackingStore(dir_name, cache_guid,
-                                                    backing_filepath));
-    case STORAGE_INVALID:
-      return std::unique_ptr<syncable::DirectoryBackingStore>(
-          new syncable::InvalidDirectoryBackingStore());
-  }
-  NOTREACHED();
-  return std::unique_ptr<syncable::DirectoryBackingStore>();
+      connection_manager, monitor, empty_listeners, debug_info_getter,
+      model_type_registry, invalidator_client_id, cache_guid, store_birthday,
+      bag_of_chips, poll_interval));
 }
 
 }  // namespace syncer
diff --git a/components/sync/engine/test_engine_components_factory.h b/components/sync/engine/test_engine_components_factory.h
index 59952e67..982ec7a 100644
--- a/components/sync/engine/test_engine_components_factory.h
+++ b/components/sync/engine/test_engine_components_factory.h
@@ -16,8 +16,7 @@
 
 class TestEngineComponentsFactory : public EngineComponentsFactory {
  public:
-  explicit TestEngineComponentsFactory(StorageOption option,
-                                       StorageOption* storage_used);
+  TestEngineComponentsFactory();
   ~TestEngineComponentsFactory() override;
 
   std::unique_ptr<SyncScheduler> BuildScheduler(
@@ -28,26 +27,17 @@
 
   std::unique_ptr<SyncCycleContext> BuildContext(
       ServerConnectionManager* connection_manager,
-      syncable::Directory* directory,
       ExtensionsActivity* monitor,
       const std::vector<SyncEngineEventListener*>& listeners,
       DebugInfoGetter* debug_info_getter,
       ModelTypeRegistry* model_type_registry,
       const std::string& invalidator_client_id,
+      const std::string& cache_guid,
       const std::string& store_birthday,
       const std::string& bag_of_chips,
       base::TimeDelta poll_interval) override;
 
-  std::unique_ptr<syncable::DirectoryBackingStore> BuildDirectoryBackingStore(
-      StorageOption storage,
-      const std::string& dir_name,
-      const std::string& cache_guid,
-      const base::FilePath& backing_filepath) override;
-
  private:
-  const StorageOption storage_override_;
-  StorageOption* storage_used_;
-
   DISALLOW_COPY_AND_ASSIGN(TestEngineComponentsFactory);
 };
 
diff --git a/components/sync/engine_impl/DEPS b/components/sync/engine_impl/DEPS
index 23db689..56e30bc 100644
--- a/components/sync/engine_impl/DEPS
+++ b/components/sync/engine_impl/DEPS
@@ -5,7 +5,6 @@
   "+components/sync/model",
   "+components/sync/nigori",
   "+components/sync/protocol",
-  "+components/sync/syncable",
   "+components/sync/test",
   "+net",
   "+services/network/public",
diff --git a/components/sync/engine_impl/commit_util.cc b/components/sync/engine_impl/commit_util.cc
index 71fbc540..c200956 100644
--- a/components/sync/engine_impl/commit_util.cc
+++ b/components/sync/engine_impl/commit_util.cc
@@ -4,8 +4,6 @@
 
 #include "components/sync/engine_impl/commit_util.h"
 
-#include "components/sync/engine_impl/syncer_proto_util.h"
-
 namespace syncer {
 
 namespace commit_util {
diff --git a/components/sync/engine_impl/cycle/sync_cycle.cc b/components/sync/engine_impl/cycle/sync_cycle.cc
index 3a05a779..bced06e7 100644
--- a/components/sync/engine_impl/cycle/sync_cycle.cc
+++ b/components/sync/engine_impl/cycle/sync_cycle.cc
@@ -9,7 +9,6 @@
 
 #include "base/logging.h"
 #include "components/sync/engine_impl/update_handler.h"
-#include "components/sync/syncable/directory.h"
 
 namespace syncer {
 
@@ -39,14 +38,10 @@
     download_progress_markers[type] = progress_marker.SerializeAsString();
   }
 
-  // TODO(mastiz): Remove dependency to directory, since it most likely hides
-  // an issue with USS types.
-  syncable::Directory* dir = context_->directory();
-
+  // TODO(crbug.com/923287): Most of the counters below are outdated. Remove.
+  int num_entries = 0;
   std::vector<int> num_entries_by_type(ModelType::NUM_ENTRIES, 0);
   std::vector<int> num_to_delete_entries_by_type(ModelType::NUM_ENTRIES, 0);
-  dir->CollectMetaHandleCounts(&num_entries_by_type,
-                               &num_to_delete_entries_by_type);
 
   SyncCycleSnapshot snapshot(
       context_->birthday(), context_->bag_of_chips(),
@@ -55,7 +50,7 @@
       status_controller_->num_encryption_conflicts(),
       status_controller_->num_hierarchy_conflicts(),
       status_controller_->num_server_conflicts(),
-      context_->notifications_enabled(), dir->GetEntriesCount(),
+      context_->notifications_enabled(), num_entries,
       status_controller_->sync_start_time(),
       status_controller_->poll_finish_time(), num_entries_by_type,
       num_to_delete_entries_by_type, get_updates_origin,
diff --git a/components/sync/engine_impl/cycle/sync_cycle_context.cc b/components/sync/engine_impl/cycle/sync_cycle_context.cc
index d7b0c58..2eb69d04b 100644
--- a/components/sync/engine_impl/cycle/sync_cycle_context.cc
+++ b/components/sync/engine_impl/cycle/sync_cycle_context.cc
@@ -5,25 +5,24 @@
 #include "components/sync/engine_impl/cycle/sync_cycle_context.h"
 
 #include "components/sync/base/extensions_activity.h"
-#include "components/sync/syncable/directory.h"
 
 namespace syncer {
 
 SyncCycleContext::SyncCycleContext(
     ServerConnectionManager* connection_manager,
-    syncable::Directory* directory,
     ExtensionsActivity* extensions_activity,
     const std::vector<SyncEngineEventListener*>& listeners,
     DebugInfoGetter* debug_info_getter,
     ModelTypeRegistry* model_type_registry,
     const std::string& invalidator_client_id,
+    const std::string& cache_guid,
     const std::string& birthday,
     const std::string& bag_of_chips,
     base::TimeDelta poll_interval)
     : connection_manager_(connection_manager),
-      directory_(directory),
       extensions_activity_(extensions_activity),
       notifications_enabled_(false),
+      cache_guid_(cache_guid),
       birthday_(birthday),
       bag_of_chips_(bag_of_chips),
       max_commit_batch_size_(kDefaultMaxCommitBatchSize),
@@ -48,16 +47,10 @@
 void SyncCycleContext::set_birthday(const std::string& birthday) {
   DCHECK(birthday_.empty());
   birthday_ = birthday;
-  // Persist the value in Directory as well, in case recent code changes are
-  // reverted and we start reading from Directory again.
-  directory_->set_legacy_store_birthday(birthday);
 }
 
 void SyncCycleContext::set_bag_of_chips(const std::string& bag_of_chips) {
   bag_of_chips_ = bag_of_chips;
-  // Persist the value in Directory as well, in case recent code changes are
-  // reverted and we start reading from Directory again.
-  directory_->set_legacy_bag_of_chips(bag_of_chips);
 }
 
 }  // namespace syncer
diff --git a/components/sync/engine_impl/cycle/sync_cycle_context.h b/components/sync/engine_impl/cycle/sync_cycle_context.h
index 91f44d0..4954a259 100644
--- a/components/sync/engine_impl/cycle/sync_cycle_context.h
+++ b/components/sync/engine_impl/cycle/sync_cycle_context.h
@@ -23,10 +23,6 @@
 class ModelTypeRegistry;
 class ServerConnectionManager;
 
-namespace syncable {
-class Directory;
-}
-
 // Default number of items a client can commit in a single message.
 static const int kDefaultMaxCommitBatchSize = 25;
 
@@ -42,12 +38,12 @@
 class SyncCycleContext {
  public:
   SyncCycleContext(ServerConnectionManager* connection_manager,
-                   syncable::Directory* directory,
                    ExtensionsActivity* extensions_activity,
                    const std::vector<SyncEngineEventListener*>& listeners,
                    DebugInfoGetter* debug_info_getter,
                    ModelTypeRegistry* model_type_registry,
                    const std::string& invalidator_client_id,
+                   const std::string& cache_guid,
                    const std::string& birthday,
                    const std::string& bag_of_chips,
                    base::TimeDelta poll_interval);
@@ -55,7 +51,6 @@
   ~SyncCycleContext();
 
   ServerConnectionManager* connection_manager() { return connection_manager_; }
-  syncable::Directory* directory() { return directory_; }
 
   ModelTypeSet GetEnabledTypes() const;
 
@@ -71,6 +66,8 @@
   }
   bool notifications_enabled() { return notifications_enabled_; }
 
+  const std::string& cache_guid() const { return cache_guid_; }
+
   void set_birthday(const std::string& birthday);
   const std::string& birthday() const { return birthday_; }
 
@@ -129,7 +126,6 @@
   base::ObserverList<SyncEngineEventListener>::Unchecked listeners_;
 
   ServerConnectionManager* const connection_manager_;
-  syncable::Directory* const directory_;
 
   // We use this to stuff extensions activity into CommitMessages so the server
   // can correlate commit traffic with extension-related bookmark mutations.
@@ -139,6 +135,8 @@
   // enabled. True only if the notification channel is authorized and open.
   bool notifications_enabled_;
 
+  const std::string cache_guid_;
+
   std::string birthday_;
 
   std::string bag_of_chips_;
diff --git a/components/sync/engine_impl/get_updates_processor.cc b/components/sync/engine_impl/get_updates_processor.cc
index 0b0e5423..fa410396 100644
--- a/components/sync/engine_impl/get_updates_processor.cc
+++ b/components/sync/engine_impl/get_updates_processor.cc
@@ -16,7 +16,6 @@
 #include "components/sync/engine_impl/syncer_proto_util.h"
 #include "components/sync/engine_impl/update_handler.h"
 #include "components/sync/nigori/keystore_keys_handler.h"
-#include "components/sync/syncable/syncable_read_transaction.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
 
 namespace syncer {
diff --git a/components/sync/engine_impl/js_mutation_event_observer.cc b/components/sync/engine_impl/js_mutation_event_observer.cc
deleted file mode 100644
index fef4d22..0000000
--- a/components/sync/engine_impl/js_mutation_event_observer.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/engine_impl/js_mutation_event_observer.h"
-
-#include <stddef.h>
-
-#include <memory>
-#include <utility>
-
-#include "base/check_op.h"
-#include "base/location.h"
-#include "base/notreached.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/values.h"
-#include "components/sync/js/js_event_details.h"
-#include "components/sync/js/js_event_handler.h"
-
-namespace syncer {
-
-JsMutationEventObserver::JsMutationEventObserver() {}
-
-JsMutationEventObserver::~JsMutationEventObserver() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-base::WeakPtr<JsMutationEventObserver> JsMutationEventObserver::AsWeakPtr() {
-  return weak_ptr_factory_.GetWeakPtr();
-}
-
-void JsMutationEventObserver::InvalidateWeakPtrs() {
-  weak_ptr_factory_.InvalidateWeakPtrs();
-}
-
-void JsMutationEventObserver::SetJsEventHandler(
-    const WeakHandle<JsEventHandler>& event_handler) {
-  event_handler_ = event_handler;
-}
-
-namespace {
-
-// Max number of changes we attempt to convert to values (to avoid
-// running out of memory).
-const size_t kChangeLimit = 100;
-
-}  // namespace
-
-void JsMutationEventObserver::OnChangesApplied(
-    ModelType model_type,
-    int64_t write_transaction_id,
-    const ImmutableChangeRecordList& changes) {
-  if (!event_handler_.IsInitialized()) {
-    return;
-  }
-  base::DictionaryValue details;
-  details.SetString("modelType", ModelTypeToString(model_type));
-  details.SetString("writeTransactionId",
-                    base::NumberToString(write_transaction_id));
-  std::unique_ptr<base::Value> changes_value;
-  const size_t changes_size = changes.Get().size();
-  if (changes_size <= kChangeLimit) {
-    auto changes_list = std::make_unique<base::ListValue>();
-    for (auto it = changes.Get().begin(); it != changes.Get().end(); ++it) {
-      changes_list->Append(it->ToValue());
-    }
-    changes_value = std::move(changes_list);
-  } else {
-    changes_value = std::make_unique<base::Value>(
-        base::NumberToString(changes_size) + " changes");
-  }
-  details.Set("changes", std::move(changes_value));
-  HandleJsEvent(FROM_HERE, "onChangesApplied", JsEventDetails(&details));
-}
-
-void JsMutationEventObserver::OnChangesComplete(ModelType model_type) {
-  if (!event_handler_.IsInitialized()) {
-    return;
-  }
-  base::DictionaryValue details;
-  details.SetString("modelType", ModelTypeToString(model_type));
-  HandleJsEvent(FROM_HERE, "onChangesComplete", JsEventDetails(&details));
-}
-
-void JsMutationEventObserver::OnTransactionWrite(
-    const syncable::ImmutableWriteTransactionInfo& write_transaction_info,
-    ModelTypeSet models_with_changes) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!event_handler_.IsInitialized()) {
-    return;
-  }
-  base::DictionaryValue details;
-  details.Set("writeTransactionInfo",
-              write_transaction_info.Get().ToValue(kChangeLimit));
-  details.Set("modelsWithChanges", ModelTypeSetToValue(models_with_changes));
-  HandleJsEvent(FROM_HERE, "onTransactionWrite", JsEventDetails(&details));
-}
-
-void JsMutationEventObserver::HandleJsEvent(const base::Location& from_here,
-                                            const std::string& name,
-                                            const JsEventDetails& details) {
-  if (!event_handler_.IsInitialized()) {
-    NOTREACHED();
-    return;
-  }
-  event_handler_.Call(from_here, &JsEventHandler::HandleJsEvent, name, details);
-}
-
-}  // namespace syncer
diff --git a/components/sync/engine_impl/js_mutation_event_observer.h b/components/sync/engine_impl/js_mutation_event_observer.h
deleted file mode 100644
index eea8dda..0000000
--- a/components/sync/engine_impl/js_mutation_event_observer.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_ENGINE_IMPL_JS_MUTATION_EVENT_OBSERVER_H_
-#define COMPONENTS_SYNC_ENGINE_IMPL_JS_MUTATION_EVENT_OBSERVER_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/sequence_checker.h"
-#include "components/sync/base/weak_handle.h"
-#include "components/sync/engine/sync_manager.h"
-#include "components/sync/syncable/transaction_observer.h"
-
-namespace base {
-class Location;
-}  // namespace base
-
-namespace syncer {
-
-class JsEventDetails;
-class JsEventHandler;
-
-// Observes all change- and transaction-related events and routes a
-// summarized version to a JsEventHandler.
-class JsMutationEventObserver : public SyncManager::ChangeObserver,
-                                public syncable::TransactionObserver {
- public:
-  JsMutationEventObserver();
-
-  ~JsMutationEventObserver() override;
-
-  base::WeakPtr<JsMutationEventObserver> AsWeakPtr();
-
-  void InvalidateWeakPtrs();
-
-  void SetJsEventHandler(const WeakHandle<JsEventHandler>& event_handler);
-
-  // SyncManager::ChangeObserver implementation.
-  void OnChangesApplied(ModelType model_type,
-                        int64_t write_transaction_id,
-                        const ImmutableChangeRecordList& changes) override;
-  void OnChangesComplete(ModelType model_type) override;
-
-  // syncable::TransactionObserver implementation.
-  void OnTransactionWrite(
-      const syncable::ImmutableWriteTransactionInfo& write_transaction_info,
-      ModelTypeSet models_with_changes) override;
-
- private:
-  WeakHandle<JsEventHandler> event_handler_;
-
-  void HandleJsEvent(const base::Location& from_here,
-                     const std::string& name,
-                     const JsEventDetails& details);
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  base::WeakPtrFactory<JsMutationEventObserver> weak_ptr_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(JsMutationEventObserver);
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_ENGINE_IMPL_JS_MUTATION_EVENT_OBSERVER_H_
diff --git a/components/sync/engine_impl/js_mutation_event_observer_unittest.cc b/components/sync/engine_impl/js_mutation_event_observer_unittest.cc
deleted file mode 100644
index d4be3d31..0000000
--- a/components/sync/engine_impl/js_mutation_event_observer_unittest.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/engine_impl/js_mutation_event_observer.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/run_loop.h"
-#include "base/test/task_environment.h"
-#include "base/values.h"
-#include "components/sync/base/model_type.h"
-#include "components/sync/js/js_event_details.h"
-#include "components/sync/js/js_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace syncer {
-namespace {
-
-using ::testing::InSequence;
-using ::testing::StrictMock;
-
-class JsMutationEventObserverTest : public testing::Test {
- protected:
-  JsMutationEventObserverTest() {
-    js_mutation_event_observer_.SetJsEventHandler(
-        mock_js_event_handler_.AsWeakHandle());
-  }
-
- private:
-  // This must be destroyed after the member variables below in order
-  // for WeakHandles to be destroyed properly.
-  base::test::SingleThreadTaskEnvironment task_environment_;
-
- protected:
-  StrictMock<MockJsEventHandler> mock_js_event_handler_;
-  JsMutationEventObserver js_mutation_event_observer_;
-
-  void PumpLoop() { base::RunLoop().RunUntilIdle(); }
-};
-
-TEST_F(JsMutationEventObserverTest, OnChangesApplied) {
-  InSequence dummy;
-
-  // We don't test with passwords as that requires additional setup.
-
-  // Build a list of example ChangeRecords.
-  ChangeRecord changes[ModelType::NUM_ENTRIES];
-  for (int i = AUTOFILL_PROFILE; i < ModelType::NUM_ENTRIES; ++i) {
-    changes[i].id = i;
-    switch (i % 3) {
-      case 0:
-        changes[i].action = ChangeRecord::ACTION_ADD;
-        break;
-      case 1:
-        changes[i].action = ChangeRecord::ACTION_UPDATE;
-        break;
-      default:
-        changes[i].action = ChangeRecord::ACTION_DELETE;
-        break;
-    }
-  }
-
-  // For each i, we call OnChangesApplied() with the first arg equal
-  // to i cast to ModelType and the second argument with the changes
-  // starting from changes[i].
-
-  // Set expectations for each data type.
-  for (int i = AUTOFILL_PROFILE; i < ModelType::NUM_ENTRIES; ++i) {
-    const std::string& model_type_str = ModelTypeToString(ModelTypeFromInt(i));
-    base::DictionaryValue expected_details;
-    expected_details.SetString("modelType", model_type_str);
-    expected_details.SetString("writeTransactionId", "0");
-    auto expected_changes = std::make_unique<base::ListValue>();
-    for (int j = i; j < ModelType::NUM_ENTRIES; ++j) {
-      expected_changes->Append(changes[j].ToValue());
-    }
-    expected_details.Set("changes", std::move(expected_changes));
-    EXPECT_CALL(mock_js_event_handler_,
-                HandleJsEvent("onChangesApplied",
-                              HasDetailsAsDictionary(expected_details)));
-  }
-
-  // Fire OnChangesApplied() for each data type.
-  for (int i = AUTOFILL_PROFILE; i < ModelType::NUM_ENTRIES; ++i) {
-    ChangeRecordList local_changes(std::begin(changes) + i, std::end(changes));
-    js_mutation_event_observer_.OnChangesApplied(
-        ModelTypeFromInt(i), 0, ImmutableChangeRecordList(&local_changes));
-  }
-
-  PumpLoop();
-}
-
-TEST_F(JsMutationEventObserverTest, OnChangesComplete) {
-  InSequence dummy;
-
-  for (int i = FIRST_REAL_MODEL_TYPE; i < ModelType::NUM_ENTRIES; ++i) {
-    base::DictionaryValue expected_details;
-    expected_details.SetString("modelType",
-                               ModelTypeToString(ModelTypeFromInt(i)));
-    EXPECT_CALL(mock_js_event_handler_,
-                HandleJsEvent("onChangesComplete",
-                              HasDetailsAsDictionary(expected_details)));
-  }
-
-  for (int i = FIRST_REAL_MODEL_TYPE; i < ModelType::NUM_ENTRIES; ++i) {
-    js_mutation_event_observer_.OnChangesComplete(ModelTypeFromInt(i));
-  }
-  PumpLoop();
-}
-
-}  // namespace
-}  // namespace syncer
diff --git a/components/sync/engine_impl/js_sync_manager_observer.cc b/components/sync/engine_impl/js_sync_manager_observer.cc
index 0387bb86..e1ffc21 100644
--- a/components/sync/engine_impl/js_sync_manager_observer.cc
+++ b/components/sync/engine_impl/js_sync_manager_observer.cc
@@ -15,7 +15,6 @@
 #include "components/sync/engine/sync_string_conversions.h"
 #include "components/sync/js/js_event_details.h"
 #include "components/sync/js/js_event_handler.h"
-#include "components/sync/syncable/change_record.h"
 
 namespace syncer {
 
diff --git a/components/sync/engine_impl/model_type_registry.cc b/components/sync/engine_impl/model_type_registry.cc
index bd05745f..61336f17 100644
--- a/components/sync/engine_impl/model_type_registry.cc
+++ b/components/sync/engine_impl/model_type_registry.cc
@@ -20,9 +20,6 @@
 #include "components/sync/engine_impl/model_type_worker.h"
 #include "components/sync/nigori/cryptographer.h"
 #include "components/sync/nigori/keystore_keys_handler.h"
-#include "components/sync/syncable/directory.h"
-#include "components/sync/syncable/read_transaction.h"
-#include "components/sync/syncable/syncable_base_transaction.h"
 
 namespace syncer {
 
@@ -57,12 +54,10 @@
 
 ModelTypeRegistry::ModelTypeRegistry(
     const std::vector<scoped_refptr<ModelSafeWorker>>& workers,
-    UserShare* user_share,
     NudgeHandler* nudge_handler,
     CancelationSignal* cancelation_signal,
     KeystoreKeysHandler* keystore_keys_handler)
-    : user_share_(user_share),
-      nudge_handler_(nudge_handler),
+    : nudge_handler_(nudge_handler),
       cancelation_signal_(cancelation_signal),
       keystore_keys_handler_(keystore_keys_handler) {
   for (size_t i = 0u; i < workers.size(); ++i) {
@@ -116,16 +111,6 @@
   // Initialize Processor -> Worker communication channel.
   type_processor->ConnectSync(std::make_unique<CommitQueueProxy>(
       worker_ptr->AsWeakPtr(), base::SequencedTaskRunnerHandle::Get()));
-
-  // If there is still data for this type left in the directory, purge it now.
-  // TODO(crbug.com/1084499): The purge should be safe to do even if the initial
-  // USS sync has already happened, and also for NIGORI.
-  if (!initial_sync_done && directory()->InitialSyncEndedForType(type) &&
-      type != NIGORI) {
-    directory()->PurgeEntriesWithTypeIn(/*disabled_types=*/ModelTypeSet(type),
-                                        /*types_to_journal=*/ModelTypeSet(),
-                                        /*types_to_unapply=*/ModelTypeSet());
-  }
 }
 
 void ModelTypeRegistry::DisconnectNonBlockingType(ModelType type) {
@@ -225,9 +210,7 @@
     }
   }
 
-  // Verify directory state.
-  ReadTransaction trans(FROM_HERE, user_share_);
-  return trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0;
+  return false;
 }
 
 base::WeakPtr<ModelTypeConnector> ModelTypeRegistry::AsWeakPtr() {
diff --git a/components/sync/engine_impl/model_type_registry.h b/components/sync/engine_impl/model_type_registry.h
index c7283e6..5d64f140 100644
--- a/components/sync/engine_impl/model_type_registry.h
+++ b/components/sync/engine_impl/model_type_registry.h
@@ -21,7 +21,6 @@
 #include "components/sync/engine/non_blocking_sync_common.h"
 #include "components/sync/engine/sync_encryption_handler.h"
 #include "components/sync/engine_impl/nudge_handler.h"
-#include "components/sync/syncable/user_share.h"
 
 namespace syncer {
 
@@ -40,7 +39,6 @@
                           public SyncEncryptionHandler::Observer {
  public:
   ModelTypeRegistry(const std::vector<scoped_refptr<ModelSafeWorker>>& workers,
-                    UserShare* user_share,
                     NudgeHandler* nudge_handler,
                     CancelationSignal* cancelation_signal,
                     KeystoreKeysHandler* keystore_keys_handler);
@@ -109,10 +107,6 @@
 
   ModelTypeSet GetEnabledNonBlockingTypes() const;
 
-  syncable::Directory* directory() const {
-    return user_share_->directory.get();
-  }
-
   // Enabled proxy types, which don't have a worker.
   ModelTypeSet enabled_proxy_types_;
 
@@ -130,8 +124,6 @@
   // The known ModelSafeWorkers.
   std::map<ModelSafeGroup, scoped_refptr<ModelSafeWorker>> workers_map_;
 
-  UserShare* const user_share_;
-
   // A copy of the directory's most recent cryptographer.
   std::unique_ptr<Cryptographer> cryptographer_;
 
diff --git a/components/sync/engine_impl/model_type_registry_unittest.cc b/components/sync/engine_impl/model_type_registry_unittest.cc
index 488d84b..fe34ef9 100644
--- a/components/sync/engine_impl/model_type_registry_unittest.cc
+++ b/components/sync/engine_impl/model_type_registry_unittest.cc
@@ -14,12 +14,9 @@
 #include "components/sync/engine/data_type_activation_response.h"
 #include "components/sync/engine/fake_model_type_processor.h"
 #include "components/sync/protocol/model_type_state.pb.h"
-#include "components/sync/syncable/directory.h"
-#include "components/sync/syncable/model_neutral_mutable_entry.h"
-#include "components/sync/syncable/syncable_model_neutral_write_transaction.h"
-#include "components/sync/syncable/test_user_share.h"
 #include "components/sync/test/engine/fake_model_worker.h"
 #include "components/sync/test/engine/mock_nudge_handler.h"
+#include "components/sync/test/fake_sync_encryption_handler.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace syncer {
@@ -29,7 +26,6 @@
 class ModelTypeRegistryTest : public ::testing::Test {
  public:
   void SetUp() override {
-    test_user_share_.SetUp();
     scoped_refptr<ModelSafeWorker> passive_worker(
         new FakeModelWorker(GROUP_PASSIVE));
     scoped_refptr<ModelSafeWorker> ui_worker(
@@ -38,14 +34,13 @@
     workers_.push_back(ui_worker);
 
     registry_ = std::make_unique<ModelTypeRegistry>(
-        workers_, test_user_share_.user_share(), &mock_nudge_handler_,
-        &cancelation_signal_, test_user_share_.keystore_keys_handler());
+        workers_, &mock_nudge_handler_, &cancelation_signal_,
+        &encryption_handler_);
   }
 
   void TearDown() override {
     registry_.reset();
     workers_.clear();
-    test_user_share_.TearDown();
   }
 
   ModelTypeRegistry* registry() { return registry_.get(); }
@@ -66,24 +61,10 @@
     return context;
   }
 
-  void SetDummyProgressMarkerForType(ModelType type) {
-    sync_pb::DataTypeProgressMarker progress_marker;
-    progress_marker.set_token("dummy");
-    directory()->SetDownloadProgress(type, progress_marker);
-  }
-
-  syncable::MetahandleSet metahandles_to_purge() {
-    return directory()->kernel()->metahandles_to_purge;
-  }
-
  private:
-  syncable::Directory* directory() {
-    return test_user_share_.user_share()->directory.get();
-  }
-
   base::test::SingleThreadTaskEnvironment task_environment_;
 
-  TestUserShare test_user_share_;
+  FakeSyncEncryptionHandler encryption_handler_;
   CancelationSignal cancelation_signal_;
   std::vector<scoped_refptr<ModelSafeWorker>> workers_;
   std::unique_ptr<ModelTypeRegistry> registry_;
diff --git a/components/sync/engine_impl/net/server_connection_manager.cc b/components/sync/engine_impl/net/server_connection_manager.cc
index bfcbef3..545a906d 100644
--- a/components/sync/engine_impl/net/server_connection_manager.cc
+++ b/components/sync/engine_impl/net/server_connection_manager.cc
@@ -14,7 +14,6 @@
 #include "components/sync/engine_impl/net/url_translator.h"
 #include "components/sync/engine_impl/syncer.h"
 #include "components/sync/protocol/sync.pb.h"
-#include "components/sync/syncable/directory.h"
 #include "net/http/http_status_code.h"
 #include "url/gurl.h"
 
diff --git a/components/sync/engine_impl/net/server_connection_manager.h b/components/sync/engine_impl/net/server_connection_manager.h
index 4414c570..0b7af61 100644
--- a/components/sync/engine_impl/net/server_connection_manager.h
+++ b/components/sync/engine_impl/net/server_connection_manager.h
@@ -14,7 +14,6 @@
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/sequence_checker.h"
-#include "components/sync/syncable/syncable_id.h"
 
 namespace syncer {
 
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc
index 6a2015a..e722182 100644
--- a/components/sync/engine_impl/sync_manager_impl.cc
+++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -33,21 +33,8 @@
 #include "components/sync/nigori/keystore_keys_handler.h"
 #include "components/sync/nigori/nigori.h"
 #include "components/sync/protocol/sync.pb.h"
-#include "components/sync/syncable/base_node.h"
-#include "components/sync/syncable/directory.h"
-#include "components/sync/syncable/directory_backing_store.h"
-#include "components/sync/syncable/entry.h"
-#include "components/sync/syncable/read_node.h"
-#include "components/sync/syncable/read_transaction.h"
-#include "components/sync/syncable/write_node.h"
-#include "components/sync/syncable/write_transaction.h"
 
 namespace syncer {
-
-using syncable::ImmutableWriteTransactionInfo;
-using syncable::SPECIFICS;
-using syncable::UNIQUE_POSITION;
-
 namespace {
 
 sync_pb::SyncEnums::GetUpdatesOrigin GetOriginFromReason(
@@ -70,73 +57,13 @@
   return sync_pb::SyncEnums::UNKNOWN_ORIGIN;
 }
 
-// Relevant for UMA, do not change.
-enum class StringConsistency {
-  kBothEqual = 0,
-  kOnlyLhsEmpty = 1,
-  kOnlyRhsEmpty = 2,
-  kBothNonEmptyAndDifferent = 3,
-  kMaxValue = kBothNonEmptyAndDifferent
-};
-
-StringConsistency CompareStringsForConsistency(const std::string& lhs,
-                                               const std::string& rhs) {
-  if (lhs == rhs) {
-    return StringConsistency::kBothEqual;
-  }
-  if (lhs.empty()) {
-    return StringConsistency::kOnlyLhsEmpty;
-  }
-  if (rhs.empty()) {
-    return StringConsistency::kOnlyRhsEmpty;
-  }
-  return StringConsistency::kBothNonEmptyAndDifferent;
-}
-
-constexpr int GetStringConsistencyUmaBucket(
-    StringConsistency cache_guid_consistency,
-    StringConsistency birthday_consistency) {
-  return static_cast<int>(cache_guid_consistency) *
-             (static_cast<int>(StringConsistency::kMaxValue) + 1) +
-         static_cast<int>(birthday_consistency);
-}
-
-// Logs information to UMA to understand whether prefs are populated with
-// information identical to the Directory's value, for the fields that are
-// stored in both. We mostly care about cache GUID and store birthday.
-void RecordConsistencyBetweenDirectoryAndPrefs(
-    const syncable::Directory* directory,
-    const SyncManager::InitArgs* args) {
-  DCHECK(directory);
-
-  const std::string directory_cache_guid =
-      directory->legacy_cache_guid_for_uma();
-  const std::string directory_birthday =
-      directory->legacy_store_birthday_for_uma();
-
-  const StringConsistency cache_guid_consistency =
-      CompareStringsForConsistency(args->cache_guid, directory_cache_guid);
-  const StringConsistency birthday_consistency =
-      CompareStringsForConsistency(args->birthday, directory_birthday);
-
-  UMA_HISTOGRAM_ENUMERATION(
-      "Sync.DirectoryVsPrefsConsistency",
-      GetStringConsistencyUmaBucket(cache_guid_consistency,
-                                    birthday_consistency),
-      GetStringConsistencyUmaBucket(StringConsistency::kMaxValue,
-                                    StringConsistency::kMaxValue) +
-          1);
-}
-
 }  // namespace
 
 SyncManagerImpl::SyncManagerImpl(
     const std::string& name,
     network::NetworkConnectionTracker* network_connection_tracker)
     : name_(name),
-      nigori_handler_proxy_(&user_share_),
       network_connection_tracker_(network_connection_tracker),
-      change_delegate_(nullptr),
       initialized_(false),
       observing_network_connectivity_changes_(false),
       sync_encryption_handler_(nullptr) {
@@ -162,68 +89,11 @@
   return value;
 }
 
-bool SyncManagerImpl::VisiblePositionsDiffer(
-    const syncable::EntryKernelMutation& mutation) const {
-  const syncable::EntryKernel& a = mutation.original;
-  const syncable::EntryKernel& b = mutation.mutated;
-  if (!b.ShouldMaintainPosition())
-    return false;
-  if (!a.ref(UNIQUE_POSITION).Equals(b.ref(UNIQUE_POSITION)))
-    return true;
-  if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID))
-    return true;
-  return false;
-}
-
-bool SyncManagerImpl::VisiblePropertiesDiffer(
-    const syncable::EntryKernelMutation& mutation,
-    const Cryptographer* cryptographer) const {
-  const syncable::EntryKernel& a = mutation.original;
-  const syncable::EntryKernel& b = mutation.mutated;
-  const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS);
-  const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS);
-  DCHECK_EQ(GetModelTypeFromSpecifics(a_specifics),
-            GetModelTypeFromSpecifics(b_specifics));
-  ModelType model_type = GetModelTypeFromSpecifics(b_specifics);
-  // Suppress updates to items that aren't tracked by any browser model.
-  if (model_type < FIRST_REAL_MODEL_TYPE ||
-      !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) {
-    return false;
-  }
-  if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR))
-    return true;
-  if (!AreSpecificsEqual(cryptographer, a.ref(syncable::SPECIFICS),
-                         b.ref(syncable::SPECIFICS))) {
-    return true;
-  }
-  // We only care if the name has changed if neither specifics is encrypted
-  // (encrypted nodes blow away the NON_UNIQUE_NAME).
-  if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() &&
-      a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME))
-    return true;
-  if (VisiblePositionsDiffer(mutation))
-    return true;
-  return false;
-}
-
 ModelTypeSet SyncManagerImpl::InitialSyncEndedTypes() {
   DCHECK(initialized_);
   return model_type_registry_->GetInitialSyncEndedTypes();
 }
 
-ModelTypeSet SyncManagerImpl::GetTypesWithEmptyProgressMarkerToken(
-    ModelTypeSet types) {
-  ModelTypeSet result;
-  for (ModelType type : types) {
-    sync_pb::DataTypeProgressMarker marker;
-    directory()->GetDownloadProgress(type, &marker);
-
-    if (marker.token().empty())
-      result.Put(type);
-  }
-  return result;
-}
-
 void SyncManagerImpl::ConfigureSyncer(ConfigureReason reason,
                                       ModelTypeSet to_download,
                                       SyncFeatureState sync_feature_state,
@@ -258,10 +128,6 @@
   DCHECK(args->cancelation_signal);
   DVLOG(1) << "SyncManager starting Init...";
 
-  weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr());
-
-  change_delegate_ = args->change_delegate;
-
   DCHECK(args->encryption_observer_proxy);
   encryption_observer_proxy_ = std::move(args->encryption_observer_proxy);
 
@@ -270,9 +136,6 @@
 
   AddObserver(&debug_info_event_listener_);
 
-  database_path_ = args->database_location.Append(
-      syncable::Directory::kSyncDatabaseFilename);
-
   DCHECK(args->encryption_handler);
   sync_encryption_handler_ = args->encryption_handler;
 
@@ -281,32 +144,11 @@
   // handler in order to receive notifications triggered during encryption
   // startup.
   sync_encryption_handler_->AddObserver(this);
-  sync_encryption_handler_->AddObserver(&nigori_handler_proxy_);
   sync_encryption_handler_->AddObserver(encryption_observer_proxy_.get());
   sync_encryption_handler_->AddObserver(&debug_info_event_listener_);
   sync_encryption_handler_->AddObserver(&js_sync_encryption_handler_observer_);
 
-  base::FilePath absolute_db_path = database_path_;
-  DCHECK(absolute_db_path.IsAbsolute());
-
-  std::unique_ptr<syncable::DirectoryBackingStore> backing_store =
-      args->engine_components_factory->BuildDirectoryBackingStore(
-          EngineComponentsFactory::STORAGE_ON_DISK,
-          args->authenticated_account_id.ToString(), args->cache_guid,
-          absolute_db_path);
-
-  DCHECK(backing_store);
-
-  user_share_.directory = std::make_unique<syncable::Directory>(
-      std::move(backing_store), WeakHandle<UnrecoverableErrorHandler>(),
-      base::DoNothing(), &nigori_handler_proxy_);
-
   DVLOG(1) << "AccountId: " << args->authenticated_account_id;
-  if (!OpenDirectory(args)) {
-    NotifyInitializationFailure();
-    DLOG(ERROR) << "Sync manager initialization failed!";
-    return;
-  }
 
   allstatus_.SetHasKeystoreKey(
       !sync_encryption_handler_->GetKeystoreKeysHandler()->NeedKeystoreKey());
@@ -324,13 +166,11 @@
         args->service_url.SchemeIsCryptographic(),
         std::move(args->post_factory), args->cancelation_signal);
   }
-  connection_manager_->set_client_id(directory()->cache_guid());
+  connection_manager_->set_client_id(args->cache_guid);
   connection_manager_->AddListener(this);
 
-  std::string sync_id = directory()->cache_guid();
-
-  DVLOG(1) << "Setting sync client ID: " << sync_id;
-  allstatus_.SetSyncId(sync_id);
+  DVLOG(1) << "Setting sync client ID: " << args->cache_guid;
+  allstatus_.SetSyncId(args->cache_guid);
   DVLOG(1) << "Setting invalidator client ID: " << args->invalidator_client_id;
   allstatus_.SetInvalidatorClientId(args->invalidator_client_id);
 
@@ -341,7 +181,7 @@
   }
 
   model_type_registry_ = std::make_unique<ModelTypeRegistry>(
-      args->workers, &user_share_, this, args->cancelation_signal,
+      args->workers, this, args->cancelation_signal,
       sync_encryption_handler_->GetKeystoreKeysHandler());
   sync_encryption_handler_->AddObserver(model_type_registry_.get());
 
@@ -351,10 +191,10 @@
   listeners.push_back(&allstatus_);
   listeners.push_back(this);
   cycle_context_ = args->engine_components_factory->BuildContext(
-      connection_manager_.get(), directory(), args->extensions_activity,
-      listeners, &debug_info_event_listener_, model_type_registry_.get(),
-      args->invalidator_client_id, args->birthday, args->bag_of_chips,
-      args->poll_interval);
+      connection_manager_.get(), args->extensions_activity, listeners,
+      &debug_info_event_listener_, model_type_registry_.get(),
+      args->invalidator_client_id, args->cache_guid, args->birthday,
+      args->bag_of_chips, args->poll_interval);
   scheduler_ = args->engine_components_factory->BuildScheduler(
       name_, cycle_context_.get(), args->cancelation_signal,
       args->enable_local_sync_backend);
@@ -451,68 +291,6 @@
   scheduler_->Start(SyncScheduler::CONFIGURATION_MODE, base::Time());
 }
 
-syncable::Directory* SyncManagerImpl::directory() {
-  return user_share_.directory.get();
-}
-
-bool SyncManagerImpl::OpenDirectory(const InitArgs* args) {
-  DCHECK(!initialized_) << "Should only happen once";
-
-  // Set before Open().
-  change_observer_ = MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr());
-  WeakHandle<syncable::TransactionObserver> transaction_observer(
-      MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr()));
-
-  syncable::DirOpenResult open_result = syncable::NOT_INITIALIZED;
-  open_result = directory()->Open(args->authenticated_account_id.ToString(),
-                                  this, transaction_observer);
-  if (open_result != syncable::OPENED_NEW &&
-      open_result != syncable::OPENED_EXISTING) {
-    DLOG(ERROR) << "Could not open share for: "
-                << args->authenticated_account_id;
-    return false;
-  }
-
-  RecordConsistencyBetweenDirectoryAndPrefs(directory(), args);
-
-  // Unapplied datatypes (those that do not have initial sync ended set) get
-  // re-downloaded during any configuration. But, it's possible for a datatype
-  // to have a progress marker but not have initial sync ended yet, making
-  // it a candidate for migration. This is a problem, as the DataTypeManager
-  // does not support a migration while it's already in the middle of a
-  // configuration. As a result, any partially synced datatype can stall the
-  // DTM, waiting for the configuration to complete, which it never will due
-  // to the migration error. In addition, a partially synced nigori will
-  // trigger the migration logic before the backend is initialized, resulting
-  // in crashes. We therefore detect and purge any partially synced types as
-  // part of initialization.
-  PurgePartiallySyncedTypes();
-  return true;
-}
-
-void SyncManagerImpl::PurgePartiallySyncedTypes() {
-  ModelTypeSet partially_synced_types = ModelTypeSet::All();
-  partially_synced_types.RemoveAll(directory()->InitialSyncEndedTypes());
-  partially_synced_types.RemoveAll(
-      GetTypesWithEmptyProgressMarkerToken(ModelTypeSet::All()));
-
-  DVLOG(1) << "Purging partially synced types "
-           << ModelTypeSetToString(partially_synced_types);
-  UMA_HISTOGRAM_COUNTS_1M("Sync.PartiallySyncedTypes",
-                          partially_synced_types.Size());
-  directory()->PurgeEntriesWithTypeIn(partially_synced_types, ModelTypeSet(),
-                                      ModelTypeSet());
-}
-
-void SyncManagerImpl::PurgeDisabledTypes(ModelTypeSet to_purge) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(initialized_);
-  DVLOG(1) << "Purging disabled types:\n\t"
-           << "types to purge: " << ModelTypeSetToString(to_purge);
-  directory()->PurgeEntriesWithTypeIn(to_purge, /*to_journal=*/ModelTypeSet(),
-                                      /*to_unapply=*/ModelTypeSet());
-}
-
 void SyncManagerImpl::UpdateCredentials(const SyncCredentials& credentials) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(initialized_);
@@ -546,10 +324,8 @@
 void SyncManagerImpl::ShutdownOnSyncThread() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // Prevent any in-flight method calls from running.  Also
-  // invalidates |weak_handle_this_| and |change_observer_|.
+  // Prevent any in-flight method calls from running.
   weak_ptr_factory_.InvalidateWeakPtrs();
-  js_mutation_event_observer_.InvalidateWeakPtrs();
 
   scheduler_.reset();
   cycle_context_.reset();
@@ -580,20 +356,7 @@
   network_connection_tracker_->RemoveNetworkConnectionObserver(this);
   observing_network_connectivity_changes_ = false;
 
-  if (initialized_ && directory()) {
-    directory()->SaveChanges();
-  }
-
-  user_share_.directory.reset();
-
-  change_delegate_ = nullptr;
-
   initialized_ = false;
-
-  // We reset these here, since only now we know they will not be
-  // accessed from other threads (since we shut down everything).
-  change_observer_.Reset();
-  weak_handle_this_.Reset();
 }
 
 void SyncManagerImpl::OnConnectionChanged(network::mojom::ConnectionType type) {
@@ -629,184 +392,6 @@
   }
 }
 
-void SyncManagerImpl::HandleTransactionCompleteChangeEvent(
-    ModelTypeSet models_with_changes) {
-  // This notification happens immediately after the transaction mutex is
-  // released. This allows work to be performed without blocking other threads
-  // from acquiring a transaction.
-  if (!change_delegate_)
-    return;
-
-  // Call commit.
-  for (ModelType type : models_with_changes) {
-    change_delegate_->OnChangesComplete(type);
-    change_observer_.Call(
-        FROM_HERE, &SyncManager::ChangeObserver::OnChangesComplete, type);
-  }
-}
-
-ModelTypeSet SyncManagerImpl::HandleTransactionEndingChangeEvent(
-    const ImmutableWriteTransactionInfo& write_transaction_info,
-    syncable::BaseTransaction* trans) {
-  // This notification happens immediately before a syncable WriteTransaction
-  // falls out of scope. It happens while the channel mutex is still held,
-  // and while the transaction mutex is held, so it cannot be re-entrant.
-  if (!change_delegate_ || change_records_.empty())
-    return ModelTypeSet();
-
-  // This will continue the WriteTransaction using a read only wrapper.
-  // This is the last chance for read to occur in the WriteTransaction
-  // that's closing. This special ReadTransaction will not close the
-  // underlying transaction.
-  ReadTransaction read_trans(GetUserShare(), trans);
-
-  ModelTypeSet models_with_changes;
-  for (ChangeRecordMap::const_iterator it = change_records_.begin();
-       it != change_records_.end(); ++it) {
-    DCHECK(!it->second.Get().empty());
-    ModelType type = ModelTypeFromInt(it->first);
-    change_delegate_->OnChangesApplied(
-        type, trans->directory()->GetTransactionVersion(type), &read_trans,
-        it->second);
-    change_observer_.Call(FROM_HERE,
-                          &SyncManager::ChangeObserver::OnChangesApplied, type,
-                          write_transaction_info.Get().id, it->second);
-    models_with_changes.Put(type);
-  }
-  change_records_.clear();
-  return models_with_changes;
-}
-
-void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncApi(
-    const ImmutableWriteTransactionInfo& write_transaction_info,
-    syncable::BaseTransaction* trans,
-    std::vector<int64_t>* entries_changed) {
-  // We have been notified about a user action changing a sync model.
-  LOG_IF(WARNING, !change_records_.empty())
-      << "CALCULATE_CHANGES called with unapplied old changes.";
-
-  // The mutated model type, or UNSPECIFIED if nothing was mutated.
-  ModelTypeSet mutated_model_types;
-
-  const syncable::ImmutableEntryKernelMutationMap& mutations =
-      write_transaction_info.Get().mutations;
-  for (auto it = mutations.Get().begin(); it != mutations.Get().end(); ++it) {
-    if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) {
-      continue;
-    }
-
-    ModelType model_type =
-        GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS));
-    if (model_type < FIRST_REAL_MODEL_TYPE) {
-      NOTREACHED() << "Permanent or underspecified item changed via syncapi.";
-      continue;
-    }
-
-    // Found real mutation.
-    if (model_type != UNSPECIFIED) {
-      mutated_model_types.Put(model_type);
-      entries_changed->push_back(it->second.mutated.ref(syncable::META_HANDLE));
-    }
-  }
-
-  // Nudge if necessary.
-  if (!mutated_model_types.Empty()) {
-    if (weak_handle_this_.IsInitialized()) {
-      weak_handle_this_.Call(FROM_HERE,
-                             &SyncManagerImpl::RequestNudgeForDataTypes,
-                             FROM_HERE, mutated_model_types);
-    } else {
-      NOTREACHED();
-    }
-  }
-}
-
-void SyncManagerImpl::SetExtraChangeRecordData(
-    int64_t id,
-    ModelType type,
-    ChangeReorderBuffer* buffer,
-    const Cryptographer* cryptographer,
-    const syncable::EntryKernel& original,
-    bool existed_before,
-    bool exists_now) {
-  // If this is a deletion and the datatype was encrypted, we need to decrypt it
-  // and attach it to the buffer.
-  if (!exists_now && existed_before) {
-    sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS));
-    if (type == PASSWORDS) {
-      // Passwords must use their own legacy ExtraPasswordChangeRecordData.
-      std::unique_ptr<sync_pb::PasswordSpecificsData> data =
-          DecryptPasswordSpecifics(original_specifics, cryptographer);
-      if (!data) {
-        NOTREACHED();
-        return;
-      }
-      buffer->SetExtraDataForId(
-          id, std::make_unique<ExtraPasswordChangeRecordData>(*data));
-    } else if (original_specifics.has_encrypted()) {
-      // All other datatypes can just create a new unencrypted specifics and
-      // attach it.
-      const sync_pb::EncryptedData& encrypted = original_specifics.encrypted();
-      if (!cryptographer->Decrypt(encrypted, &original_specifics)) {
-        NOTREACHED();
-        return;
-      }
-    }
-    buffer->SetSpecificsForId(id, original_specifics);
-  }
-}
-
-void SyncManagerImpl::HandleCalculateChangesChangeEventFromSyncer(
-    const ImmutableWriteTransactionInfo& write_transaction_info,
-    syncable::BaseTransaction* trans,
-    std::vector<int64_t>* entries_changed) {
-  // We only expect one notification per sync step, so change_buffers_ should
-  // contain no pending entries.
-  LOG_IF(WARNING, !change_records_.empty())
-      << "CALCULATE_CHANGES called with unapplied old changes.";
-
-  ChangeReorderBuffer change_buffers[ModelType::NUM_ENTRIES];
-
-  const Cryptographer* crypto = directory()->GetCryptographer(trans);
-  const syncable::ImmutableEntryKernelMutationMap& mutations =
-      write_transaction_info.Get().mutations;
-  for (auto it = mutations.Get().begin(); it != mutations.Get().end(); ++it) {
-    bool existed_before = !it->second.original.ref(syncable::IS_DEL);
-    bool exists_now = !it->second.mutated.ref(syncable::IS_DEL);
-
-    // Omit items that aren't associated with a model.
-    ModelType type =
-        GetModelTypeFromSpecifics(it->second.mutated.ref(SPECIFICS));
-    if (type < FIRST_REAL_MODEL_TYPE)
-      continue;
-
-    int64_t handle = it->first;
-    if (exists_now && !existed_before)
-      change_buffers[type].PushAddedItem(handle);
-    else if (!exists_now && existed_before)
-      change_buffers[type].PushDeletedItem(handle);
-    else if (exists_now && existed_before &&
-             VisiblePropertiesDiffer(it->second, crypto))
-      change_buffers[type].PushUpdatedItem(handle);
-
-    SetExtraChangeRecordData(handle, type, &change_buffers[type], crypto,
-                             it->second.original, existed_before, exists_now);
-  }
-
-  ReadTransaction read_trans(GetUserShare(), trans);
-  for (int i = FIRST_REAL_MODEL_TYPE; i < ModelType::NUM_ENTRIES; ++i) {
-    if (!change_buffers[i].IsEmpty()) {
-      if (change_buffers[i].GetAllChangesInTreeOrder(&read_trans,
-                                                     &(change_records_[i]))) {
-        for (size_t j = 0; j < change_records_[i].Get().size(); ++j)
-          entries_changed->push_back((change_records_[i].Get())[j].id);
-      }
-      if (change_records_[i].Get().empty())
-        change_records_.erase(i);
-    }
-  }
-}
-
 void SyncManagerImpl::RequestNudgeForDataTypes(
     const base::Location& nudge_location,
     ModelTypeSet types) {
@@ -876,7 +461,6 @@
 void SyncManagerImpl::SetJsEventHandler(
     const WeakHandle<JsEventHandler>& event_handler) {
   js_sync_manager_observer_.SetJsEventHandler(event_handler);
-  js_mutation_event_observer_.SetJsEventHandler(event_handler);
   js_sync_encryption_handler_observer_.SetJsEventHandler(event_handler);
 }
 
@@ -907,11 +491,6 @@
   }
 }
 
-UserShare* SyncManagerImpl::GetUserShare() {
-  DCHECK(initialized_);
-  return &user_share_;
-}
-
 ModelTypeConnector* SyncManagerImpl::GetModelTypeConnector() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return model_type_registry_.get();
@@ -927,7 +506,7 @@
 
 std::string SyncManagerImpl::cache_guid() {
   DCHECK(initialized_);
-  return directory()->cache_guid();
+  return cycle_context_->cache_guid();
 }
 
 std::string SyncManagerImpl::birthday() {
@@ -982,10 +561,6 @@
   cycle_context_->set_cookie_jar_empty(empty_jar);
 }
 
-void SyncManagerImpl::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd) {
-  directory()->OnMemoryDump(pmd);
-}
-
 void SyncManagerImpl::UpdateInvalidationClientId(const std::string& client_id) {
   DVLOG(1) << "Setting invalidator client ID: " << client_id;
   allstatus_.SetInvalidatorClientId(client_id);
diff --git a/components/sync/engine_impl/sync_manager_impl.h b/components/sync/engine_impl/sync_manager_impl.h
index b04e540c..b02acee 100644
--- a/components/sync/engine_impl/sync_manager_impl.h
+++ b/components/sync/engine_impl/sync_manager_impl.h
@@ -21,17 +21,12 @@
 #include "components/sync/engine_impl/all_status.h"
 #include "components/sync/engine_impl/debug_info_event_listener.h"
 #include "components/sync/engine_impl/events/protocol_event_buffer.h"
-#include "components/sync/engine_impl/js_mutation_event_observer.h"
 #include "components/sync/engine_impl/js_sync_encryption_handler_observer.h"
 #include "components/sync/engine_impl/js_sync_manager_observer.h"
 #include "components/sync/engine_impl/net/server_connection_manager.h"
 #include "components/sync/engine_impl/nudge_handler.h"
 #include "components/sync/engine_impl/sync_engine_event_listener.h"
 #include "components/sync/js/js_backend.h"
-#include "components/sync/syncable/change_reorder_buffer.h"
-#include "components/sync/syncable/directory_change_delegate.h"
-#include "components/sync/syncable/nigori_handler_proxy.h"
-#include "components/sync/syncable/user_share.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 
 namespace syncer {
@@ -41,12 +36,6 @@
 class SyncCycleContext;
 class TypeDebugInfoObserver;
 
-// SyncManager encapsulates syncable::Directory and serves as the parent of all
-// other objects in the sync API.  If multiple threads interact with the same
-// local sync repository (i.e. the same sqlite database), they should share a
-// single SyncManager instance.  The caller should typically create one
-// SyncManager for the lifetime of a user session.
-//
 // Unless stated otherwise, all methods of SyncManager should be called on the
 // same thread.
 class SyncManagerImpl
@@ -55,7 +44,6 @@
       public JsBackend,
       public SyncEngineEventListener,
       public ServerConnectionEventListener,
-      public syncable::DirectoryChangeDelegate,
       public SyncEncryptionHandler::Observer,
       public NudgeHandler {
  public:
@@ -69,10 +57,6 @@
   // SyncManager implementation.
   void Init(InitArgs* args) override;
   ModelTypeSet InitialSyncEndedTypes() override;
-  ModelTypeSet GetTypesWithEmptyProgressMarkerToken(
-      ModelTypeSet types) override;
-  void PurgePartiallySyncedTypes() override;
-  void PurgeDisabledTypes(ModelTypeSet to_purge) override;
   void UpdateCredentials(const SyncCredentials& credentials) override;
   void InvalidateCredentials() override;
   void StartSyncingNormally(base::Time last_poll_time) override;
@@ -105,7 +89,6 @@
       TypeDebugInfoObserver* observer) override;
   void RequestEmitDebugInfo() override;
   void OnCookieJarChanged(bool account_mismatch, bool empty_jar) override;
-  void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd) override;
   void UpdateInvalidationClientId(const std::string& client_id) override;
 
   // SyncEncryptionHandler::Observer implementation.
@@ -142,24 +125,6 @@
   void SetJsEventHandler(
       const WeakHandle<JsEventHandler>& event_handler) override;
 
-  // DirectoryChangeDelegate implementation.
-  // This listener is called upon completion of a syncable transaction, and
-  // builds the list of sync-engine initiated changes that will be forwarded to
-  // the SyncManager's Observers.
-  void HandleTransactionCompleteChangeEvent(
-      ModelTypeSet models_with_changes) override;
-  ModelTypeSet HandleTransactionEndingChangeEvent(
-      const syncable::ImmutableWriteTransactionInfo& write_transaction_info,
-      syncable::BaseTransaction* trans) override;
-  void HandleCalculateChangesChangeEventFromSyncApi(
-      const syncable::ImmutableWriteTransactionInfo& write_transaction_info,
-      syncable::BaseTransaction* trans,
-      std::vector<int64_t>* entries_changed) override;
-  void HandleCalculateChangesChangeEventFromSyncer(
-      const syncable::ImmutableWriteTransactionInfo& write_transaction_info,
-      syncable::BaseTransaction* trans,
-      std::vector<int64_t>* entries_changed) override;
-
   // Handle explicit requests to fetch updates for the given types.
   void RefreshTypes(ModelTypeSet types) override;
 
@@ -170,8 +135,6 @@
   void NudgeForInitialDownload(ModelType type) override;
   void NudgeForCommit(ModelType type) override;
 
-  UserShare* GetUserShare();
-
  protected:
   // Helper functions.  Virtual for testing.
   virtual void NotifyInitializationSuccess();
@@ -180,8 +143,6 @@
  private:
   friend class SyncManagerTest;
   FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, NudgeDelayTest);
-  FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, PurgeDisabledTypes);
-  FRIEND_TEST_ALL_PREFIXES(SyncManagerTest, PurgeUnappliedTypes);
 
   struct NotificationInfo {
     NotificationInfo();
@@ -196,66 +157,15 @@
 
   using NotificationInfoMap = std::map<ModelType, NotificationInfo>;
 
-  // Determine if the parents or predecessors differ between the old and new
-  // versions of an entry.  Note that a node's index may change without its
-  // UNIQUE_POSITION changing if its sibling nodes were changed.  To handle such
-  // cases, we rely on the caller to treat a position update on any sibling as
-  // updating the positions of all siblings.
-  bool VisiblePositionsDiffer(
-      const syncable::EntryKernelMutation& mutation) const;
-
-  // Determine if any of the fields made visible to clients of the Sync API
-  // differ between the versions of an entry stored in |a| and |b|. A return
-  // value of false means that it should be OK to ignore this change.
-  bool VisiblePropertiesDiffer(const syncable::EntryKernelMutation& mutation,
-                               const Cryptographer* cryptographer) const;
-
-  // Opens the directory.
-  bool OpenDirectory(const InitArgs* args);
-
   void RequestNudgeForDataTypes(const base::Location& nudge_location,
                                 ModelTypeSet type);
 
-  // If this is a deletion for a password, sets the legacy
-  // ExtraPasswordChangeRecordData field of |buffer|. Otherwise sets
-  // |buffer|'s specifics field to contain the unencrypted data.
-  void SetExtraChangeRecordData(int64_t id,
-                                ModelType type,
-                                ChangeReorderBuffer* buffer,
-                                const Cryptographer* cryptographer,
-                                const syncable::EntryKernel& original,
-                                bool existed_before,
-                                bool exists_now);
-
-  syncable::Directory* directory();
-
-  base::FilePath database_path_;
-
   const std::string name_;
 
-  UserShare user_share_;
-
-  syncable::NigoriHandlerProxy nigori_handler_proxy_;
-
   network::NetworkConnectionTracker* network_connection_tracker_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  // Thread-safe handle used by
-  // HandleCalculateChangesChangeEventFromSyncApi(), which can be
-  // called from any thread.  Valid only between between calls to
-  // Init() and Shutdown().
-  //
-  // TODO(akalin): Ideally, we wouldn't need to store this; instead,
-  // we'd have another worker class which implements
-  // HandleCalculateChangesChangeEventFromSyncApi() and we'd pass it a
-  // WeakHandle when we construct it.
-  WeakHandle<SyncManagerImpl> weak_handle_this_;
-
-  // This can be called from any thread, but only between calls to
-  // OpenDirectory() and ShutdownOnSyncThread().
-  WeakHandle<SyncManager::ChangeObserver> change_observer_;
-
   base::ObserverList<SyncManager::Observer>::Unchecked observers_;
 
   // The ServerConnectionManager used to abstract communication between the
@@ -278,17 +188,6 @@
   // sync components.
   AllStatus allstatus_;
 
-  // Each element of this map is a store of change records produced by
-  // HandleChangeEventFromSyncer during the CALCULATE_CHANGES step. The changes
-  // are grouped by model type, and are stored here in tree order to be
-  // forwarded to the observer slightly later, at the TRANSACTION_ENDING step
-  // by HandleTransactionEndingChangeEvent. The list is cleared after observer
-  // finishes processing.
-  using ChangeRecordMap = std::map<int, ImmutableChangeRecordList>;
-  ChangeRecordMap change_records_;
-
-  SyncManager::ChangeDelegate* change_delegate_;
-
   // Set to true once Init has been called.
   bool initialized_;
 
@@ -300,7 +199,6 @@
 
   // These are for interacting with chrome://sync-internals.
   JsSyncManagerObserver js_sync_manager_observer_;
-  JsMutationEventObserver js_mutation_event_observer_;
   JsSyncEncryptionHandlerObserver js_sync_encryption_handler_observer_;
 
   // This is for keeping track of client events to send to the server.
diff --git a/components/sync/engine_impl/sync_manager_impl_unittest.cc b/components/sync/engine_impl/sync_manager_impl_unittest.cc
index d1f82b7..30fbc5c 100644
--- a/components/sync/engine_impl/sync_manager_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_manager_impl_unittest.cc
@@ -35,7 +35,6 @@
 #include "components/sync/engine/test_engine_components_factory.h"
 #include "components/sync/engine_impl/cycle/sync_cycle.h"
 #include "components/sync/engine_impl/sync_scheduler.h"
-#include "components/sync/engine_impl/test_entry_factory.h"
 #include "components/sync/js/js_event_handler.h"
 #include "components/sync/js/js_test_util.h"
 #include "components/sync/protocol/bookmark_specifics.pb.h"
@@ -45,20 +44,6 @@
 #include "components/sync/protocol/preference_specifics.pb.h"
 #include "components/sync/protocol/proto_value_conversions.h"
 #include "components/sync/protocol/sync.pb.h"
-#include "components/sync/syncable/change_record.h"
-#include "components/sync/syncable/directory.h"
-#include "components/sync/syncable/entry.h"
-#include "components/sync/syncable/mutable_entry.h"
-#include "components/sync/syncable/nigori_handler.h"
-#include "components/sync/syncable/nigori_util.h"
-#include "components/sync/syncable/read_node.h"
-#include "components/sync/syncable/read_transaction.h"
-#include "components/sync/syncable/syncable_id.h"
-#include "components/sync/syncable/syncable_read_transaction.h"
-#include "components/sync/syncable/syncable_write_transaction.h"
-#include "components/sync/syncable/test_user_share.h"
-#include "components/sync/syncable/write_node.h"
-#include "components/sync/syncable/write_transaction.h"
 #include "components/sync/test/callback_counter.h"
 #include "components/sync/test/engine/fake_model_worker.h"
 #include "components/sync/test/engine/fake_sync_scheduler.h"
@@ -81,590 +66,6 @@
 
 namespace syncer {
 
-using syncable::GET_BY_HANDLE;
-using syncable::IS_DEL;
-using syncable::IS_UNSYNCED;
-using syncable::NON_UNIQUE_NAME;
-using syncable::SPECIFICS;
-using syncable::kEncryptedString;
-
-namespace {
-
-// Tests in this file covers directory behavior, so we did't migrate them to
-// use ClientTagHash.
-std::string GenerateSyncableHash(ModelType type,
-                                 const std::string& client_tag) {
-  return ClientTagHash::FromUnhashed(type, client_tag).value();
-}
-
-// Makes a child node under the type root folder.  Returns the id of the
-// newly-created node.
-int64_t MakeNode(UserShare* share,
-                 ModelType model_type,
-                 const std::string& client_tag) {
-  WriteTransaction trans(FROM_HERE, share);
-  WriteNode node(&trans);
-  WriteNode::InitUniqueByCreationResult result =
-      node.InitUniqueByCreation(model_type, client_tag);
-  EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
-  node.SetIsFolder(false);
-  return node.GetId();
-}
-
-// Makes a non-folder child of the root node.  Returns the id of the
-// newly-created node.
-int64_t MakeNodeWithRoot(UserShare* share,
-                         ModelType model_type,
-                         const std::string& client_tag) {
-  WriteTransaction trans(FROM_HERE, share);
-  ReadNode root_node(&trans);
-  root_node.InitByRootLookup();
-  WriteNode node(&trans);
-  WriteNode::InitUniqueByCreationResult result =
-      node.InitUniqueByCreation(model_type, root_node, client_tag);
-  EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
-  node.SetIsFolder(false);
-  return node.GetId();
-}
-
-// Makes a folder child of a non-root node. Returns the id of the
-// newly-created node.
-int64_t MakeFolderWithParent(UserShare* share,
-                             ModelType model_type,
-                             int64_t parent_id,
-                             BaseNode* predecessor) {
-  WriteTransaction trans(FROM_HERE, share);
-  ReadNode parent_node(&trans);
-  EXPECT_EQ(BaseNode::INIT_OK, parent_node.InitByIdLookup(parent_id));
-  WriteNode node(&trans);
-  EXPECT_TRUE(node.InitBookmarkByCreation(parent_node, predecessor));
-  node.SetIsFolder(true);
-  return node.GetId();
-}
-
-int64_t MakeBookmarkWithParent(UserShare* share,
-                               int64_t parent_id,
-                               BaseNode* predecessor) {
-  WriteTransaction trans(FROM_HERE, share);
-  ReadNode parent_node(&trans);
-  EXPECT_EQ(BaseNode::INIT_OK, parent_node.InitByIdLookup(parent_id));
-  WriteNode node(&trans);
-  EXPECT_TRUE(node.InitBookmarkByCreation(parent_node, predecessor));
-  return node.GetId();
-}
-
-// Creates the "synced" root node for a particular datatype. We use the syncable
-// methods here so that the syncer treats these nodes as if they were already
-// received from the server.
-int64_t MakeTypeRoot(UserShare* share, ModelType model_type) {
-  sync_pb::EntitySpecifics specifics;
-  AddDefaultFieldValue(model_type, &specifics);
-  syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST,
-                                   share->directory.get());
-  // Attempt to lookup by nigori tag.
-  std::string type_tag = ModelTypeToRootTag(model_type);
-  syncable::Id node_id = syncable::Id::CreateFromServerId(type_tag);
-  syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
-                               node_id);
-  EXPECT_TRUE(entry.good());
-  entry.PutBaseVersion(1);
-  entry.PutServerVersion(1);
-  entry.PutIsUnappliedUpdate(false);
-  entry.PutParentId(syncable::Id::GetRoot());
-  entry.PutServerParentId(syncable::Id::GetRoot());
-  entry.PutServerIsDir(true);
-  entry.PutIsDir(true);
-  entry.PutServerSpecifics(specifics);
-  entry.PutSpecifics(specifics);
-  entry.PutUniqueServerTag(type_tag);
-  entry.PutNonUniqueName(type_tag);
-  entry.PutIsDel(false);
-  return entry.GetMetahandle();
-}
-
-// Simulates creating a "synced" node as a child of the root datatype node.
-int64_t MakeServerNode(UserShare* share,
-                       ModelType model_type,
-                       const std::string& client_tag,
-                       const std::string& hashed_tag,
-                       const sync_pb::EntitySpecifics& specifics) {
-  syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST,
-                                   share->directory.get());
-  syncable::Entry root_entry(&trans, syncable::GET_TYPE_ROOT, model_type);
-  EXPECT_TRUE(root_entry.good());
-  syncable::Id root_id = root_entry.GetId();
-  syncable::Id node_id = syncable::Id::CreateFromServerId(client_tag);
-  syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
-                               node_id);
-  EXPECT_TRUE(entry.good());
-  entry.PutBaseVersion(1);
-  entry.PutServerVersion(1);
-  entry.PutIsUnappliedUpdate(false);
-  entry.PutServerParentId(root_id);
-  entry.PutParentId(root_id);
-  entry.PutServerIsDir(false);
-  entry.PutIsDir(false);
-  entry.PutServerSpecifics(specifics);
-  entry.PutSpecifics(specifics);
-  entry.PutNonUniqueName(client_tag);
-  entry.PutUniqueClientTag(hashed_tag);
-  entry.PutIsDel(false);
-  return entry.GetMetahandle();
-}
-
-int GetTotalNodeCount(UserShare* share, int64_t root) {
-  ReadTransaction trans(FROM_HERE, share);
-  ReadNode node(&trans);
-  EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(root));
-  return node.GetTotalNodeCount();
-}
-
-}  // namespace
-
-// Unit tests for the SyncApi. Note that a lot of the underlying
-// functionality is provided by the Syncable layer, which has its own
-// unit tests. We'll test SyncApi specific things in this harness.
-class SyncApiTest : public testing::Test {
- public:
-  void SetUp() override { test_user_share_.SetUp(); }
-
-  void TearDown() override { test_user_share_.TearDown(); }
-
- protected:
-  // Create an entry with the given |model_type| and |client_tag|.
-  void CreateEntry(const ModelType& model_type, const std::string& client_tag);
-
-  // Attempts to load the entry specified by |model_type| and |client_tag| and
-  // returns the lookup result code.
-  BaseNode::InitByLookupResult LookupEntryByClientTag(
-      const ModelType& model_type,
-      const std::string& client_tag);
-
-  // Replace the entry specified by |model_type| and |client_tag| with a
-  // tombstone.
-  void ReplaceWithTombstone(const ModelType& model_type,
-                            const std::string& client_tag);
-
-  // Save changes to the Directory, destroy it then reload it.
-  bool ReloadDir();
-
-  UserShare* user_share();
-  syncable::Directory* dir();
-  SyncEncryptionHandler* encryption_handler();
-  PassphraseType GetPassphraseType(BaseTransaction* trans);
-
- private:
-  base::test::SingleThreadTaskEnvironment task_environment_;
-  TestUserShare test_user_share_;
-};
-
-UserShare* SyncApiTest::user_share() {
-  return test_user_share_.user_share();
-}
-
-syncable::Directory* SyncApiTest::dir() {
-  return test_user_share_.user_share()->directory.get();
-}
-
-SyncEncryptionHandler* SyncApiTest::encryption_handler() {
-  return test_user_share_.encryption_handler();
-}
-
-PassphraseType SyncApiTest::GetPassphraseType(BaseTransaction* trans) {
-  return dir()->GetNigoriHandler()->GetPassphraseType(trans->GetWrappedTrans());
-}
-
-bool SyncApiTest::ReloadDir() {
-  return test_user_share_.Reload();
-}
-
-void SyncApiTest::CreateEntry(const ModelType& model_type,
-                              const std::string& client_tag) {
-  WriteTransaction trans(FROM_HERE, user_share());
-  ReadNode root_node(&trans);
-  root_node.InitByRootLookup();
-  WriteNode node(&trans);
-  ASSERT_EQ(node.InitUniqueByCreation(model_type, root_node, client_tag),
-            WriteNode::INIT_SUCCESS);
-}
-
-BaseNode::InitByLookupResult SyncApiTest::LookupEntryByClientTag(
-    const ModelType& model_type,
-    const std::string& client_tag) {
-  ReadTransaction trans(FROM_HERE, user_share());
-  ReadNode node(&trans);
-  return node.InitByClientTagLookup(model_type, client_tag);
-}
-
-void SyncApiTest::ReplaceWithTombstone(const ModelType& model_type,
-                                       const std::string& client_tag) {
-  WriteTransaction trans(FROM_HERE, user_share());
-  WriteNode node(&trans);
-  ASSERT_EQ(node.InitByClientTagLookup(model_type, client_tag),
-            WriteNode::INIT_OK);
-  node.Tombstone();
-}
-
-TEST_F(SyncApiTest, SanityCheckTest) {
-  {
-    ReadTransaction trans(FROM_HERE, user_share());
-    EXPECT_TRUE(trans.GetWrappedTrans());
-  }
-  {
-    WriteTransaction trans(FROM_HERE, user_share());
-    EXPECT_TRUE(trans.GetWrappedTrans());
-  }
-  {
-    // No entries but root should exist
-    ReadTransaction trans(FROM_HERE, user_share());
-    ReadNode node(&trans);
-    // Metahandle 1 can be root, sanity check 2
-    EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_NOT_GOOD, node.InitByIdLookup(2));
-  }
-}
-
-TEST_F(SyncApiTest, BasicTagWrite) {
-  {
-    ReadTransaction trans(FROM_HERE, user_share());
-    ReadNode root_node(&trans);
-    root_node.InitByRootLookup();
-    EXPECT_EQ(kInvalidId, root_node.GetFirstChildId());
-  }
-
-  ignore_result(MakeNodeWithRoot(user_share(), BOOKMARKS, "testtag"));
-
-  {
-    ReadTransaction trans(FROM_HERE, user_share());
-    ReadNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              node.InitByClientTagLookup(BOOKMARKS, "testtag"));
-    EXPECT_NE(0, node.GetId());
-
-    ReadNode root_node(&trans);
-    root_node.InitByRootLookup();
-    EXPECT_EQ(node.GetId(), root_node.GetFirstChildId());
-  }
-}
-
-TEST_F(SyncApiTest, BasicTagWriteWithImplicitParent) {
-  int64_t type_root = MakeTypeRoot(user_share(), PREFERENCES);
-
-  {
-    ReadTransaction trans(FROM_HERE, user_share());
-    ReadNode type_root_node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK, type_root_node.InitByIdLookup(type_root));
-    EXPECT_EQ(kInvalidId, type_root_node.GetFirstChildId());
-  }
-
-  ignore_result(MakeNode(user_share(), PREFERENCES, "testtag"));
-
-  {
-    ReadTransaction trans(FROM_HERE, user_share());
-    ReadNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              node.InitByClientTagLookup(PREFERENCES, "testtag"));
-    EXPECT_EQ(kInvalidId, node.GetParentId());
-
-    ReadNode type_root_node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK, type_root_node.InitByIdLookup(type_root));
-    EXPECT_EQ(node.GetId(), type_root_node.GetFirstChildId());
-  }
-}
-
-TEST_F(SyncApiTest, ModelTypesSiloed) {
-  {
-    WriteTransaction trans(FROM_HERE, user_share());
-    ReadNode root_node(&trans);
-    root_node.InitByRootLookup();
-    EXPECT_EQ(root_node.GetFirstChildId(), 0);
-  }
-
-  ignore_result(MakeNodeWithRoot(user_share(), BOOKMARKS, "collideme"));
-  ignore_result(MakeNodeWithRoot(user_share(), PREFERENCES, "collideme"));
-  ignore_result(MakeNodeWithRoot(user_share(), AUTOFILL, "collideme"));
-
-  {
-    ReadTransaction trans(FROM_HERE, user_share());
-
-    ReadNode bookmarknode(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              bookmarknode.InitByClientTagLookup(BOOKMARKS, "collideme"));
-
-    ReadNode prefnode(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              prefnode.InitByClientTagLookup(PREFERENCES, "collideme"));
-
-    ReadNode autofillnode(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              autofillnode.InitByClientTagLookup(AUTOFILL, "collideme"));
-
-    EXPECT_NE(bookmarknode.GetId(), prefnode.GetId());
-    EXPECT_NE(autofillnode.GetId(), prefnode.GetId());
-    EXPECT_NE(bookmarknode.GetId(), autofillnode.GetId());
-  }
-}
-
-TEST_F(SyncApiTest, ReadMissingTagsFails) {
-  {
-    ReadTransaction trans(FROM_HERE, user_share());
-    ReadNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_NOT_GOOD,
-              node.InitByClientTagLookup(BOOKMARKS, "testtag"));
-  }
-  {
-    WriteTransaction trans(FROM_HERE, user_share());
-    WriteNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_NOT_GOOD,
-              node.InitByClientTagLookup(BOOKMARKS, "testtag"));
-  }
-}
-
-// TODO(chron): Hook this all up to the server and write full integration tests
-//              for update->undelete behavior.
-TEST_F(SyncApiTest, TestDeleteBehavior) {
-  int64_t node_id;
-  int64_t folder_id;
-  std::string test_title("test1");
-
-  {
-    WriteTransaction trans(FROM_HERE, user_share());
-    ReadNode root_node(&trans);
-    root_node.InitByRootLookup();
-
-    // we'll use this spare folder later
-    WriteNode folder_node(&trans);
-    EXPECT_TRUE(folder_node.InitBookmarkByCreation(root_node, nullptr));
-    folder_id = folder_node.GetId();
-
-    WriteNode wnode(&trans);
-    WriteNode::InitUniqueByCreationResult result =
-        wnode.InitUniqueByCreation(BOOKMARKS, root_node, "testtag");
-    EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
-    wnode.SetIsFolder(false);
-    wnode.SetTitle(test_title);
-
-    node_id = wnode.GetId();
-  }
-
-  // Ensure we can delete something with a tag.
-  {
-    WriteTransaction trans(FROM_HERE, user_share());
-    WriteNode wnode(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              wnode.InitByClientTagLookup(BOOKMARKS, "testtag"));
-    EXPECT_FALSE(wnode.GetIsFolder());
-    EXPECT_EQ(wnode.GetTitle(), test_title);
-
-    wnode.Tombstone();
-  }
-
-  // Lookup of a node which was deleted should return failure,
-  // but have found some data about the node.
-  {
-    ReadTransaction trans(FROM_HERE, user_share());
-    ReadNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_IS_DEL,
-              node.InitByClientTagLookup(BOOKMARKS, "testtag"));
-    // Note that for proper function of this API this doesn't need to be
-    // filled, we're checking just to make sure the DB worked in this test.
-    EXPECT_EQ(node.GetTitle(), test_title);
-  }
-
-  {
-    WriteTransaction trans(FROM_HERE, user_share());
-    ReadNode folder_node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK, folder_node.InitByIdLookup(folder_id));
-
-    WriteNode wnode(&trans);
-    // This will undelete the tag.
-    WriteNode::InitUniqueByCreationResult result =
-        wnode.InitUniqueByCreation(BOOKMARKS, folder_node, "testtag");
-    EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
-    EXPECT_EQ(wnode.GetIsFolder(), false);
-    EXPECT_EQ(wnode.GetParentId(), folder_node.GetId());
-    EXPECT_EQ(wnode.GetId(), node_id);
-    EXPECT_NE(wnode.GetTitle(), test_title);  // Title should be cleared
-    wnode.SetTitle(test_title);
-  }
-
-  // Now look up should work.
-  {
-    ReadTransaction trans(FROM_HERE, user_share());
-    ReadNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              node.InitByClientTagLookup(BOOKMARKS, "testtag"));
-    EXPECT_EQ(node.GetTitle(), test_title);
-    EXPECT_EQ(node.GetModelType(), BOOKMARKS);
-  }
-}
-
-// Non-unique name should not be empty. For bookmarks non-unique name is copied
-// from bookmark title. This test verifies that setting bookmark title to ""
-// results in single space title and non-unique name in internal representation.
-// GetTitle should still return empty string.
-TEST_F(SyncApiTest, WriteEmptyBookmarkTitle) {
-  int bookmark_id;
-  {
-    WriteTransaction trans(FROM_HERE, user_share());
-    ReadNode root_node(&trans);
-    root_node.InitByRootLookup();
-
-    WriteNode bookmark_node(&trans);
-    ASSERT_TRUE(bookmark_node.InitBookmarkByCreation(root_node, nullptr));
-    bookmark_id = bookmark_node.GetId();
-    bookmark_node.SetTitle("");
-  }
-  {
-    ReadTransaction trans(FROM_HERE, user_share());
-
-    ReadNode bookmark_node(&trans);
-    ASSERT_EQ(BaseNode::INIT_OK, bookmark_node.InitByIdLookup(bookmark_id));
-    EXPECT_EQ("", bookmark_node.GetTitle());
-    EXPECT_EQ(" ", bookmark_node.GetEntitySpecifics()
-                       .bookmark()
-                       .legacy_canonicalized_title());
-    EXPECT_EQ(" ", bookmark_node.GetEntry()->GetNonUniqueName());
-  }
-}
-
-TEST_F(SyncApiTest, BaseNodeSetSpecifics) {
-  int64_t child_id = MakeNodeWithRoot(user_share(), BOOKMARKS, "testtag");
-  WriteTransaction trans(FROM_HERE, user_share());
-  WriteNode node(&trans);
-  EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(child_id));
-
-  sync_pb::EntitySpecifics entity_specifics;
-  entity_specifics.mutable_bookmark()->set_url("http://www.google.com");
-
-  EXPECT_NE(entity_specifics.SerializeAsString(),
-            node.GetEntitySpecifics().SerializeAsString());
-  node.SetEntitySpecifics(entity_specifics);
-  EXPECT_EQ(entity_specifics.SerializeAsString(),
-            node.GetEntitySpecifics().SerializeAsString());
-}
-
-TEST_F(SyncApiTest, BaseNodeSetSpecificsPreservesUnknownFields) {
-  int64_t child_id = MakeNodeWithRoot(user_share(), BOOKMARKS, "testtag");
-  WriteTransaction trans(FROM_HERE, user_share());
-  WriteNode node(&trans);
-  EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(child_id));
-  EXPECT_TRUE(node.GetEntitySpecifics().unknown_fields().empty());
-
-  sync_pb::EntitySpecifics entity_specifics;
-  entity_specifics.mutable_bookmark()->set_url("http://www.google.com");
-  std::string unknown_fields;
-  {
-    ::google::protobuf::io::StringOutputStream unknown_fields_stream(
-        &unknown_fields);
-    ::google::protobuf::io::CodedOutputStream output(&unknown_fields_stream);
-    const int tag = 5;
-    const int value = 100;
-    output.WriteTag(tag);
-    output.WriteLittleEndian32(value);
-  }
-  *entity_specifics.mutable_unknown_fields() = unknown_fields;
-  node.SetEntitySpecifics(entity_specifics);
-  EXPECT_FALSE(node.GetEntitySpecifics().unknown_fields().empty());
-  EXPECT_EQ(unknown_fields, node.GetEntitySpecifics().unknown_fields());
-
-  entity_specifics.mutable_unknown_fields()->clear();
-  node.SetEntitySpecifics(entity_specifics);
-  EXPECT_FALSE(node.GetEntitySpecifics().unknown_fields().empty());
-  EXPECT_EQ(unknown_fields, node.GetEntitySpecifics().unknown_fields());
-}
-
-TEST_F(SyncApiTest, EmptyTags) {
-  WriteTransaction trans(FROM_HERE, user_share());
-  ReadNode root_node(&trans);
-  root_node.InitByRootLookup();
-  WriteNode node(&trans);
-  std::string empty_tag;
-  WriteNode::InitUniqueByCreationResult result =
-      node.InitUniqueByCreation(TYPED_URLS, root_node, empty_tag);
-  EXPECT_NE(WriteNode::INIT_SUCCESS, result);
-  EXPECT_EQ(BaseNode::INIT_FAILED_PRECONDITION,
-            node.InitByClientTagLookup(TYPED_URLS, empty_tag));
-}
-
-// Test counting nodes when the type's root node has no children.
-TEST_F(SyncApiTest, GetTotalNodeCountEmpty) {
-  int64_t type_root = MakeTypeRoot(user_share(), BOOKMARKS);
-  EXPECT_EQ(1, GetTotalNodeCount(user_share(), type_root));
-}
-
-// Test counting nodes when there is one child beneath the type's root.
-TEST_F(SyncApiTest, GetTotalNodeCountOneChild) {
-  int64_t type_root = MakeTypeRoot(user_share(), BOOKMARKS);
-  int64_t parent =
-      MakeFolderWithParent(user_share(), BOOKMARKS, type_root, nullptr);
-  EXPECT_EQ(2, GetTotalNodeCount(user_share(), type_root));
-  EXPECT_EQ(1, GetTotalNodeCount(user_share(), parent));
-}
-
-// Test counting nodes when there are multiple children beneath the type root,
-// and one of those children has children of its own.
-TEST_F(SyncApiTest, GetTotalNodeCountMultipleChildren) {
-  int64_t type_root = MakeTypeRoot(user_share(), BOOKMARKS);
-  int64_t parent =
-      MakeFolderWithParent(user_share(), BOOKMARKS, type_root, nullptr);
-  ignore_result(
-      MakeFolderWithParent(user_share(), BOOKMARKS, type_root, nullptr));
-  int64_t child1 =
-      MakeFolderWithParent(user_share(), BOOKMARKS, parent, nullptr);
-  ignore_result(MakeBookmarkWithParent(user_share(), parent, nullptr));
-  ignore_result(MakeBookmarkWithParent(user_share(), child1, nullptr));
-  EXPECT_EQ(6, GetTotalNodeCount(user_share(), type_root));
-  EXPECT_EQ(4, GetTotalNodeCount(user_share(), parent));
-}
-
-// This tests directory integrity in the case of creating a new unique node
-// with client tag matching that of an existing unapplied node with server only
-// data. See crbug.com/505761.
-TEST_F(SyncApiTest, WriteNode_UniqueByCreation_UndeleteCase) {
-  int64_t preferences_root = MakeTypeRoot(user_share(), PREFERENCES);
-
-  // Create a node with server only data.
-  int64_t item1 = 0;
-  {
-    syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST,
-                                     user_share()->directory.get());
-    syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
-                                 syncable::Id::CreateFromServerId("foo1"));
-    DCHECK(entry.good());
-    entry.PutServerVersion(10);
-    entry.PutIsUnappliedUpdate(true);
-    sync_pb::EntitySpecifics specifics;
-    AddDefaultFieldValue(PREFERENCES, &specifics);
-    entry.PutServerSpecifics(specifics);
-    const std::string hash = GenerateSyncableHash(PREFERENCES, "foo");
-    entry.PutUniqueClientTag(hash);
-    item1 = entry.GetMetahandle();
-  }
-
-  // Verify that the server-only item is invisible as a child of
-  // of |preferences_root| because at this point it should have the
-  // "deleted" flag set.
-  EXPECT_EQ(1, GetTotalNodeCount(user_share(), preferences_root));
-
-  // Create a client node with the same tag as the node above.
-  int64_t item2 = MakeNode(user_share(), PREFERENCES, "foo");
-  // Expect this to be the same directory entry as |item1|.
-  EXPECT_EQ(item1, item2);
-  // Expect it to be visible as a child of |preferences_root|.
-  EXPECT_EQ(2, GetTotalNodeCount(user_share(), preferences_root));
-
-  // Tombstone the new item
-  {
-    WriteTransaction trans(FROM_HERE, user_share());
-    WriteNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(item1));
-    node.Tombstone();
-  }
-
-  // Verify that it is gone from the index.
-  EXPECT_EQ(1, GetTotalNodeCount(user_share(), preferences_root));
-}
-
 namespace {
 
 class TestHttpPostProviderInterface : public HttpPostProviderInterface {
@@ -737,8 +138,7 @@
 
 }  // namespace
 
-class SyncManagerTest : public testing::Test,
-                        public SyncManager::ChangeDelegate {
+class SyncManagerTest : public testing::Test {
  protected:
   enum NigoriStatus { DONT_WRITE_NIGORI, WRITE_TO_NIGORI };
 
@@ -781,8 +181,7 @@
     args.post_factory = std::make_unique<TestHttpPostProviderFactory>();
     args.workers = workers;
     args.encryption_observer_proxy = std::move(encryption_observer);
-    args.extensions_activity = extensions_activity_.get(),
-    args.change_delegate = this;
+    args.extensions_activity = extensions_activity_.get();
     if (!enable_local_sync_backend)
       args.authenticated_account_id = CoreAccountId("account_id");
     args.cache_guid = "fake_cache_guid";
@@ -796,14 +195,6 @@
     sync_manager_.Init(&args);
 
     EXPECT_TRUE(js_backend_.IsInitialized());
-    EXPECT_EQ(EngineComponentsFactory::STORAGE_ON_DISK, storage_used_);
-
-    if (initialization_succeeded_) {
-      ModelTypeSet enabled_types = GetEnabledTypes();
-      for (ModelType type : enabled_types) {
-        type_roots_[type] = MakeTypeRoot(sync_manager_.GetUserShare(), type);
-      }
-    }
 
     PumpLoop();
   }
@@ -831,19 +222,6 @@
     return enabled_types;
   }
 
-  void OnChangesApplied(ModelType model_type,
-                        int64_t model_version,
-                        const BaseTransaction* trans,
-                        const ImmutableChangeRecordList& changes) override {}
-
-  void OnChangesComplete(ModelType model_type) override {}
-
-  int64_t GetIdForDataType(ModelType type) {
-    if (type_roots_.count(type) == 0)
-      return 0;
-    return type_roots_[type];
-  }
-
   void PumpLoop() { base::RunLoop().RunUntilIdle(); }
 
   void SetJsEventHandler(const WeakHandle<JsEventHandler>& event_handler) {
@@ -851,67 +229,8 @@
     PumpLoop();
   }
 
-  // Looks up an entry by client tag and resets IS_UNSYNCED value to false.
-  // Returns true if entry was previously unsynced, false if IS_UNSYNCED was
-  // already false.
-  bool ResetUnsyncedEntry(ModelType type, const std::string& client_tag) {
-    UserShare* share = sync_manager_.GetUserShare();
-    syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST,
-                                     share->directory.get());
-    const std::string hash = GenerateSyncableHash(type, client_tag);
-    syncable::MutableEntry entry(&trans, syncable::GET_BY_CLIENT_TAG, hash);
-    EXPECT_TRUE(entry.good());
-    if (!entry.GetIsUnsynced())
-      return false;
-    entry.PutIsUnsynced(false);
-    return true;
-  }
-
   virtual EngineComponentsFactory* GetFactory() {
-    return new TestEngineComponentsFactory(
-        EngineComponentsFactory::STORAGE_IN_MEMORY, &storage_used_);
-  }
-
-  // Returns true if we are currently encrypting all sync data.  May
-  // be called on any thread.
-  bool IsEncryptEverythingEnabledForTest() {
-    return sync_manager_.GetEncryptionHandler()->IsEncryptEverythingEnabled();
-  }
-
-  // Gets the set of encrypted types from the cryptographer
-  // Note: opens a transaction.  May be called from any thread.
-  ModelTypeSet GetEncryptedTypes() {
-    ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
-    return GetEncryptedTypesWithTrans(&trans);
-  }
-
-  ModelTypeSet GetEncryptedTypesWithTrans(BaseTransaction* trans) {
-    return trans->GetDirectory()->GetNigoriHandler()->GetEncryptedTypes(
-        trans->GetWrappedTrans());
-  }
-
-  void SimulateInvalidatorEnabledForTest(bool is_enabled) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sync_manager_.sequence_checker_);
-    sync_manager_.SetInvalidatorEnabled(is_enabled);
-  }
-
-  void SetProgressMarkerForType(ModelType type, bool set) {
-    if (set) {
-      sync_pb::DataTypeProgressMarker marker;
-      marker.set_token("token");
-      marker.set_data_type_id(GetSpecificsFieldNumberFromModelType(type));
-      sync_manager_.directory()->SetDownloadProgress(type, marker);
-    } else {
-      sync_pb::DataTypeProgressMarker marker;
-      sync_manager_.directory()->SetDownloadProgress(type, marker);
-    }
-  }
-
-  void ExpectPassphraseAcceptance() {
-    EXPECT_CALL(*encryption_observer_, OnPassphraseAccepted());
-    EXPECT_CALL(*encryption_observer_, OnEncryptionComplete());
-    EXPECT_CALL(*encryption_observer_,
-                OnCryptographerStateChanged(_, /*has_pending_keys=*/false));
+    return new TestEngineComponentsFactory();
   }
 
  private:
@@ -919,8 +238,6 @@
   base::test::TaskEnvironment task_environment_;
   // Needed by |sync_manager_|.
   base::ScopedTempDir temp_dir_;
-  // Sync Id's for the roots of the enabled datatypes.
-  std::map<ModelType, int64_t> type_roots_;
   scoped_refptr<ExtensionsActivity> extensions_activity_;
 
  protected:
@@ -932,191 +249,8 @@
   StrictMock<SyncManagerObserverMock> manager_observer_;
   // Owned by |sync_manager_|.
   StrictMock<SyncEncryptionHandlerObserverMock>* encryption_observer_;
-  EngineComponentsFactory::StorageOption storage_used_;
 };
 
-// Create a bookmark and set the title/url, then verify the data was properly
-// set. This replicates the unique way bookmarks have of creating sync nodes.
-// See BookmarkChangeProcessor::PlaceSyncNode(..).
-TEST_F(SyncManagerTest, CreateLocalBookmark) {
-  std::string title = "title";
-  std::string url = "url";
-  {
-    WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
-    ReadNode bookmark_root(&trans);
-    ASSERT_EQ(BaseNode::INIT_OK, bookmark_root.InitTypeRoot(BOOKMARKS));
-    WriteNode node(&trans);
-    ASSERT_TRUE(node.InitBookmarkByCreation(bookmark_root, nullptr));
-    node.SetIsFolder(false);
-    node.SetTitle(title);
-
-    sync_pb::BookmarkSpecifics bookmark_specifics(node.GetBookmarkSpecifics());
-    bookmark_specifics.set_url(url);
-    node.SetBookmarkSpecifics(bookmark_specifics);
-  }
-  {
-    ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
-    ReadNode bookmark_root(&trans);
-    ASSERT_EQ(BaseNode::INIT_OK, bookmark_root.InitTypeRoot(BOOKMARKS));
-    int64_t child_id = bookmark_root.GetFirstChildId();
-
-    ReadNode node(&trans);
-    ASSERT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(child_id));
-    EXPECT_FALSE(node.GetIsFolder());
-    EXPECT_EQ(title, node.GetTitle());
-    EXPECT_EQ(url, node.GetBookmarkSpecifics().url());
-  }
-}
-
-// Verify SetTitle(..) doesn't unnecessarily set IS_UNSYNCED for bookmarks
-// when we write the same data, but does set it when we write new data.
-TEST_F(SyncManagerTest, SetBookmarkTitle) {
-  std::string client_tag = "title";
-  sync_pb::EntitySpecifics entity_specifics;
-  entity_specifics.mutable_bookmark()->set_url("url");
-  entity_specifics.mutable_bookmark()->set_legacy_canonicalized_title("title");
-  MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag,
-                 GenerateSyncableHash(BOOKMARKS, client_tag), entity_specifics);
-  // New node shouldn't start off unsynced.
-  EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
-
-  // Manually change to the same title. Should not set is_unsynced.
-  {
-    WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
-    WriteNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              node.InitByClientTagLookup(BOOKMARKS, client_tag));
-    node.SetTitle(client_tag);
-  }
-  EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
-
-  // Manually change to new title. Should set is_unsynced.
-  {
-    WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
-    WriteNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              node.InitByClientTagLookup(BOOKMARKS, client_tag));
-    node.SetTitle("title2");
-  }
-  EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
-}
-
-// Verify SetTitle(..) doesn't unnecessarily set IS_UNSYNCED for non-bookmarks
-// when we write the same data, but does set it when we write new data.
-TEST_F(SyncManagerTest, SetNonBookmarkTitle) {
-  std::string client_tag = "title";
-  sync_pb::EntitySpecifics entity_specifics;
-  entity_specifics.mutable_preference()->set_name("name");
-  entity_specifics.mutable_preference()->set_value("value");
-  MakeServerNode(sync_manager_.GetUserShare(), PREFERENCES, client_tag,
-                 GenerateSyncableHash(PREFERENCES, client_tag),
-                 entity_specifics);
-  // New node shouldn't start off unsynced.
-  EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag));
-
-  // Manually change to the same title. Should not set is_unsynced.
-  {
-    WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
-    WriteNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              node.InitByClientTagLookup(PREFERENCES, client_tag));
-    node.SetTitle(client_tag);
-  }
-  EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag));
-
-  // Manually change to new title. Should set is_unsynced.
-  {
-    WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
-    WriteNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              node.InitByClientTagLookup(PREFERENCES, client_tag));
-    node.SetTitle("title2");
-  }
-  EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, client_tag));
-}
-
-// Ensure that titles are truncated to 255 bytes, and attempting to reset
-// them to their longer version does not set IS_UNSYNCED.
-TEST_F(SyncManagerTest, SetLongTitle) {
-  const int kNumChars = 512;
-  const std::string kClientTag = "tag";
-  std::string title(kNumChars, '0');
-  sync_pb::EntitySpecifics entity_specifics;
-  entity_specifics.mutable_preference()->set_name("name");
-  entity_specifics.mutable_preference()->set_value("value");
-  MakeServerNode(sync_manager_.GetUserShare(), PREFERENCES, "short_title",
-                 GenerateSyncableHash(PREFERENCES, kClientTag),
-                 entity_specifics);
-  // New node shouldn't start off unsynced.
-  EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, kClientTag));
-
-  // Manually change to the long title. Should set is_unsynced.
-  {
-    WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
-    WriteNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              node.InitByClientTagLookup(PREFERENCES, kClientTag));
-    node.SetTitle(title);
-    EXPECT_EQ(node.GetTitle(), title.substr(0, 255));
-  }
-  EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, kClientTag));
-
-  // Manually change to the same title. Should not set is_unsynced.
-  {
-    WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
-    WriteNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              node.InitByClientTagLookup(PREFERENCES, kClientTag));
-    node.SetTitle(title);
-    EXPECT_EQ(node.GetTitle(), title.substr(0, 255));
-  }
-  EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, kClientTag));
-
-  // Manually change to new title. Should set is_unsynced.
-  {
-    WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
-    WriteNode node(&trans);
-    EXPECT_EQ(BaseNode::INIT_OK,
-              node.InitByClientTagLookup(PREFERENCES, kClientTag));
-    node.SetTitle("title2");
-  }
-  EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, kClientTag));
-}
-
-// Verify transaction version of a model type is incremented when node of
-// that type is updated.
-TEST_F(SyncManagerTest, IncrementTransactionVersion) {
-  {
-    ReadTransaction read_trans(FROM_HERE, sync_manager_.GetUserShare());
-    ModelTypeSet enabled_types = GetEnabledTypes();
-    for (ModelType type : enabled_types) {
-      // Transaction version is incremented when SyncManagerTest::SetUp()
-      // creates a node of each type.
-      EXPECT_EQ(
-          1,
-          sync_manager_.GetUserShare()->directory->GetTransactionVersion(type));
-    }
-  }
-
-  // Create bookmark node to increment transaction version of bookmark model.
-  std::string client_tag = "title";
-  sync_pb::EntitySpecifics entity_specifics;
-  entity_specifics.mutable_bookmark()->set_url("url");
-  entity_specifics.mutable_bookmark()->set_legacy_canonicalized_title("title");
-  MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag,
-                 GenerateSyncableHash(BOOKMARKS, client_tag), entity_specifics);
-
-  {
-    ReadTransaction read_trans(FROM_HERE, sync_manager_.GetUserShare());
-    ModelTypeSet enabled_types = GetEnabledTypes();
-    for (ModelType type : enabled_types) {
-      EXPECT_EQ(
-          type == BOOKMARKS ? 2 : 1,
-          sync_manager_.GetUserShare()->directory->GetTransactionVersion(type));
-    }
-  }
-}
-
 class SyncManagerWithLocalBackendTest : public SyncManagerTest {
  protected:
   void SetUp() override { DoSetUp(true); }
@@ -1137,12 +271,8 @@
 class ComponentsFactory : public TestEngineComponentsFactory {
  public:
   ComponentsFactory(SyncScheduler* scheduler_to_use,
-                    SyncCycleContext** cycle_context,
-                    EngineComponentsFactory::StorageOption* storage_used)
-      : TestEngineComponentsFactory(EngineComponentsFactory::STORAGE_IN_MEMORY,
-                                    storage_used),
-        scheduler_to_use_(scheduler_to_use),
-        cycle_context_(cycle_context) {}
+                    SyncCycleContext** cycle_context)
+      : scheduler_to_use_(scheduler_to_use), cycle_context_(cycle_context) {}
   ~ComponentsFactory() override {}
 
   std::unique_ptr<SyncScheduler> BuildScheduler(
@@ -1164,7 +294,7 @@
   SyncManagerTestWithMockScheduler() : scheduler_(nullptr) {}
   EngineComponentsFactory* GetFactory() override {
     scheduler_ = new MockSyncScheduler();
-    return new ComponentsFactory(scheduler_, &cycle_context_, &storage_used_);
+    return new ComponentsFactory(scheduler_, &cycle_context_);
   }
 
   MockSyncScheduler* scheduler() { return scheduler_; }
@@ -1196,478 +326,4 @@
   EXPECT_EQ(types_to_download, params.types_to_download);
 }
 
-// Test that PurgeDisabledTypes only purges recently disabled types leaving
-// others intact.
-TEST_F(SyncManagerTestWithMockScheduler, PurgeDisabledTypes) {
-  ModelTypeSet enabled_types = GetEnabledTypes();
-  ModelTypeSet disabled_types = Difference(ModelTypeSet::All(), enabled_types);
-  // Set data for all types.
-  ModelTypeSet protocol_types = ProtocolTypes();
-  for (ModelType type : protocol_types) {
-    SetProgressMarkerForType(type, true);
-  }
-
-  sync_manager_.PurgeDisabledTypes(disabled_types);
-  // Verify all the disabled types were purged.
-  EXPECT_EQ(enabled_types,
-            sync_manager_.GetUserShare()->directory->InitialSyncEndedTypes());
-  EXPECT_EQ(disabled_types, sync_manager_.GetTypesWithEmptyProgressMarkerToken(
-                                ModelTypeSet::All()));
-}
-
-// Test that PurgePartiallySyncedTypes purges only those types that have not
-// fully completed their initial download and apply.
-TEST_F(SyncManagerTest, PurgePartiallySyncedTypes) {
-  ModelTypeSet enabled_types = GetEnabledTypes();
-
-  UserShare* share = sync_manager_.GetUserShare();
-
-  // The test harness automatically initializes all types in the routing info.
-  // Check that autofill is not among them.
-  ASSERT_FALSE(enabled_types.Has(AUTOFILL));
-
-  // Further ensure that the test harness did not create its root node.
-  {
-    syncable::ReadTransaction trans(FROM_HERE, share->directory.get());
-    syncable::Entry autofill_root_node(&trans, syncable::GET_TYPE_ROOT,
-                                       AUTOFILL);
-    ASSERT_FALSE(autofill_root_node.good());
-  }
-
-  // One more redundant check.
-  ASSERT_FALSE(
-      sync_manager_.GetUserShare()->directory->InitialSyncEndedTypes().Has(
-          AUTOFILL));
-
-  // Give autofill a progress marker.
-  sync_pb::DataTypeProgressMarker autofill_marker;
-  autofill_marker.set_data_type_id(
-      GetSpecificsFieldNumberFromModelType(AUTOFILL));
-  autofill_marker.set_token("token");
-  share->directory->SetDownloadProgress(AUTOFILL, autofill_marker);
-
-  // Also add a pending autofill root node update from the server.
-  TestEntryFactory factory_(share->directory.get());
-  int autofill_meta = factory_.CreateUnappliedRootNode(AUTOFILL);
-
-  // Preferences is an enabled type.  Check that the harness initialized it.
-  ASSERT_TRUE(enabled_types.Has(PREFERENCES));
-  ASSERT_TRUE(
-      sync_manager_.GetUserShare()->directory->InitialSyncEndedTypes().Has(
-          PREFERENCES));
-
-  // Give preferencse a progress marker.
-  sync_pb::DataTypeProgressMarker prefs_marker;
-  prefs_marker.set_data_type_id(
-      GetSpecificsFieldNumberFromModelType(PREFERENCES));
-  prefs_marker.set_token("token");
-  share->directory->SetDownloadProgress(PREFERENCES, prefs_marker);
-
-  // Add a fully synced preferences node under the root.
-  std::string pref_client_tag = "prefABC";
-  std::string pref_hashed_tag = "hashXYZ";
-  sync_pb::EntitySpecifics pref_specifics;
-  AddDefaultFieldValue(PREFERENCES, &pref_specifics);
-  int pref_meta = MakeServerNode(share, PREFERENCES, pref_client_tag,
-                                 pref_hashed_tag, pref_specifics);
-
-  // And now, the purge.
-  sync_manager_.PurgePartiallySyncedTypes();
-
-  // Ensure that autofill lost its progress marker, but preferences did not.
-  ModelTypeSet empty_tokens =
-      sync_manager_.GetTypesWithEmptyProgressMarkerToken(ModelTypeSet::All());
-  EXPECT_TRUE(empty_tokens.Has(AUTOFILL));
-  EXPECT_FALSE(empty_tokens.Has(PREFERENCES));
-
-  // Ensure that autofill lost its node, but preferences did not.
-  {
-    syncable::ReadTransaction trans(FROM_HERE, share->directory.get());
-    syncable::Entry autofill_node(&trans, GET_BY_HANDLE, autofill_meta);
-    syncable::Entry pref_node(&trans, GET_BY_HANDLE, pref_meta);
-    EXPECT_FALSE(autofill_node.good());
-    EXPECT_TRUE(pref_node.good());
-  }
-}
-
-// Test CleanupDisabledTypes properly purges all disabled types as specified
-// by the previous and current enabled params.
-TEST_F(SyncManagerTest, PurgeDisabledTypes) {
-  ModelTypeSet enabled_types = GetEnabledTypes();
-  ModelTypeSet disabled_types = Difference(ModelTypeSet::All(), enabled_types);
-
-  // The harness should have initialized the enabled_types for us.
-  EXPECT_EQ(enabled_types,
-            sync_manager_.GetUserShare()->directory->InitialSyncEndedTypes());
-
-  // Set progress markers for all types.
-  ModelTypeSet protocol_types = ProtocolTypes();
-  for (ModelType type : protocol_types) {
-    SetProgressMarkerForType(type, true);
-  }
-
-  // Verify all the enabled types remain after cleanup, and all the disabled
-  // types were purged.
-  sync_manager_.PurgeDisabledTypes(disabled_types);
-  EXPECT_EQ(enabled_types,
-            sync_manager_.GetUserShare()->directory->InitialSyncEndedTypes());
-  EXPECT_EQ(disabled_types, sync_manager_.GetTypesWithEmptyProgressMarkerToken(
-                                ModelTypeSet::All()));
-
-  // Disable some more types.
-  disabled_types.Put(BOOKMARKS);
-  disabled_types.Put(PREFERENCES);
-  ModelTypeSet new_enabled_types =
-      Difference(ModelTypeSet::All(), disabled_types);
-
-  // Verify only the non-disabled types remain after cleanup.
-  sync_manager_.PurgeDisabledTypes(disabled_types);
-  EXPECT_EQ(new_enabled_types,
-            sync_manager_.GetUserShare()->directory->InitialSyncEndedTypes());
-  EXPECT_EQ(disabled_types, sync_manager_.GetTypesWithEmptyProgressMarkerToken(
-                                ModelTypeSet::All()));
-}
-
-// A test harness to exercise the code that processes and passes changes from
-// the "SYNCER"-WriteTransaction destructor, through the SyncManager, to the
-// ChangeProcessor.
-class SyncManagerChangeProcessingTest : public SyncManagerTest {
- public:
-  void OnChangesApplied(ModelType model_type,
-                        int64_t model_version,
-                        const BaseTransaction* trans,
-                        const ImmutableChangeRecordList& changes) override {
-    last_changes_ = changes;
-  }
-
-  void OnChangesComplete(ModelType model_type) override {}
-
-  const ImmutableChangeRecordList& GetRecentChangeList() {
-    return last_changes_;
-  }
-
-  UserShare* share() { return sync_manager_.GetUserShare(); }
-
-  // Set some flags so our nodes reasonably approximate the real world scenario
-  // and can get past CheckTreeInvariants.
-  //
-  // It's never going to be truly accurate, since we're squashing update
-  // receipt, processing and application into a single transaction.
-  void SetNodeProperties(syncable::MutableEntry* entry) {
-    entry->PutId(id_factory_.NewServerId());
-    entry->PutBaseVersion(10);
-    entry->PutServerVersion(10);
-  }
-
-  // Looks for the given change in the list.  Returns the index at which it was
-  // found.  Returns -1 on lookup failure.
-  size_t FindChangeInList(int64_t id, ChangeRecord::Action action) {
-    SCOPED_TRACE(id);
-    for (size_t i = 0; i < last_changes_.Get().size(); ++i) {
-      if (last_changes_.Get()[i].id == id &&
-          last_changes_.Get()[i].action == action) {
-        return i;
-      }
-    }
-    ADD_FAILURE() << "Failed to find specified change";
-    return static_cast<size_t>(-1);
-  }
-
-  // Returns the current size of the change list.
-  //
-  // Note that spurious changes do not necessarily indicate a problem.
-  // Assertions on change list size can help detect problems, but it may be
-  // necessary to reduce their strictness if the implementation changes.
-  size_t GetChangeListSize() { return last_changes_.Get().size(); }
-
-  void ClearChangeList() { last_changes_ = ImmutableChangeRecordList(); }
-
- protected:
-  ImmutableChangeRecordList last_changes_;
-  TestIdFactory id_factory_;
-};
-
-// Test creation of a folder and a bookmark.
-TEST_F(SyncManagerChangeProcessingTest, AddBookmarks) {
-  int64_t type_root = GetIdForDataType(BOOKMARKS);
-  int64_t folder_id = kInvalidId;
-  int64_t child_id = kInvalidId;
-
-  // Create a folder and a bookmark under it.
-  {
-    syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER,
-                                     share()->directory.get());
-    syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root);
-    ASSERT_TRUE(root.good());
-
-    syncable::MutableEntry folder(&trans, syncable::CREATE, BOOKMARKS,
-                                  root.GetId(), "folder");
-    ASSERT_TRUE(folder.good());
-    SetNodeProperties(&folder);
-    folder.PutIsDir(true);
-    folder_id = folder.GetMetahandle();
-
-    syncable::MutableEntry child(&trans, syncable::CREATE, BOOKMARKS,
-                                 folder.GetId(), "child");
-    ASSERT_TRUE(child.good());
-    SetNodeProperties(&child);
-    child_id = child.GetMetahandle();
-  }
-
-  // The closing of the above scope will delete the transaction.  Its processed
-  // changes should be waiting for us in a member of the test harness.
-  EXPECT_EQ(2UL, GetChangeListSize());
-
-  // We don't need to check these return values here.  The function will add a
-  // non-fatal failure if these changes are not found.
-  size_t folder_change_pos =
-      FindChangeInList(folder_id, ChangeRecord::ACTION_ADD);
-  size_t child_change_pos =
-      FindChangeInList(child_id, ChangeRecord::ACTION_ADD);
-
-  // Parents are delivered before children.
-  EXPECT_LT(folder_change_pos, child_change_pos);
-}
-
-// Test creation of a preferences (with implicit parent Id)
-TEST_F(SyncManagerChangeProcessingTest, AddPreferences) {
-  int64_t item1_id = kInvalidId;
-  int64_t item2_id = kInvalidId;
-
-  // Create two preferences.
-  {
-    syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER,
-                                     share()->directory.get());
-
-    syncable::MutableEntry item1(&trans, syncable::CREATE, PREFERENCES,
-                                 "test_item_1");
-    ASSERT_TRUE(item1.good());
-    SetNodeProperties(&item1);
-    item1_id = item1.GetMetahandle();
-
-    // Need at least two items to ensure hitting all possible codepaths in
-    // ChangeReorderBuffer::Traversal::ExpandToInclude.
-    syncable::MutableEntry item2(&trans, syncable::CREATE, PREFERENCES,
-                                 "test_item_2");
-    ASSERT_TRUE(item2.good());
-    SetNodeProperties(&item2);
-    item2_id = item2.GetMetahandle();
-  }
-
-  // The closing of the above scope will delete the transaction.  Its processed
-  // changes should be waiting for us in a member of the test harness.
-  EXPECT_EQ(2UL, GetChangeListSize());
-
-  FindChangeInList(item1_id, ChangeRecord::ACTION_ADD);
-  FindChangeInList(item2_id, ChangeRecord::ACTION_ADD);
-}
-
-// Test moving a bookmark into an empty folder.
-TEST_F(SyncManagerChangeProcessingTest, MoveBookmarkIntoEmptyFolder) {
-  int64_t type_root = GetIdForDataType(BOOKMARKS);
-  int64_t folder_b_id = kInvalidId;
-  int64_t child_id = kInvalidId;
-
-  // Create two folders.  Place a child under folder A.
-  {
-    syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER,
-                                     share()->directory.get());
-    syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root);
-    ASSERT_TRUE(root.good());
-
-    syncable::MutableEntry folder_a(&trans, syncable::CREATE, BOOKMARKS,
-                                    root.GetId(), "folderA");
-    ASSERT_TRUE(folder_a.good());
-    SetNodeProperties(&folder_a);
-    folder_a.PutIsDir(true);
-
-    syncable::MutableEntry folder_b(&trans, syncable::CREATE, BOOKMARKS,
-                                    root.GetId(), "folderB");
-    ASSERT_TRUE(folder_b.good());
-    SetNodeProperties(&folder_b);
-    folder_b.PutIsDir(true);
-    folder_b_id = folder_b.GetMetahandle();
-
-    syncable::MutableEntry child(&trans, syncable::CREATE, BOOKMARKS,
-                                 folder_a.GetId(), "child");
-    ASSERT_TRUE(child.good());
-    SetNodeProperties(&child);
-    child_id = child.GetMetahandle();
-  }
-
-  // Close that transaction.  The above was to setup the initial scenario.  The
-  // real test starts now.
-
-  // Move the child from folder A to folder B.
-  {
-    syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER,
-                                     share()->directory.get());
-
-    syncable::Entry folder_b(&trans, syncable::GET_BY_HANDLE, folder_b_id);
-    syncable::MutableEntry child(&trans, syncable::GET_BY_HANDLE, child_id);
-
-    child.PutParentId(folder_b.GetId());
-  }
-
-  EXPECT_EQ(1UL, GetChangeListSize());
-
-  // Verify that this was detected as a real change.  An early version of the
-  // UniquePosition code had a bug where moves from one folder to another were
-  // ignored unless the moved node's UniquePosition value was also changed in
-  // some way.
-  FindChangeInList(child_id, ChangeRecord::ACTION_UPDATE);
-}
-
-// Test moving a bookmark into a non-empty folder.
-TEST_F(SyncManagerChangeProcessingTest, MoveIntoPopulatedFolder) {
-  int64_t type_root = GetIdForDataType(BOOKMARKS);
-  int64_t child_a_id = kInvalidId;
-  int64_t child_b_id = kInvalidId;
-
-  // Create two folders.  Place one child each under folder A and folder B.
-  {
-    syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER,
-                                     share()->directory.get());
-    syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root);
-    ASSERT_TRUE(root.good());
-
-    syncable::MutableEntry folder_a(&trans, syncable::CREATE, BOOKMARKS,
-                                    root.GetId(), "folderA");
-    ASSERT_TRUE(folder_a.good());
-    SetNodeProperties(&folder_a);
-    folder_a.PutIsDir(true);
-
-    syncable::MutableEntry folder_b(&trans, syncable::CREATE, BOOKMARKS,
-                                    root.GetId(), "folderB");
-    ASSERT_TRUE(folder_b.good());
-    SetNodeProperties(&folder_b);
-    folder_b.PutIsDir(true);
-
-    syncable::MutableEntry child_a(&trans, syncable::CREATE, BOOKMARKS,
-                                   folder_a.GetId(), "childA");
-    ASSERT_TRUE(child_a.good());
-    SetNodeProperties(&child_a);
-    child_a_id = child_a.GetMetahandle();
-
-    syncable::MutableEntry child_b(&trans, syncable::CREATE, BOOKMARKS,
-                                   folder_b.GetId(), "childB");
-    SetNodeProperties(&child_b);
-    child_b_id = child_b.GetMetahandle();
-  }
-
-  // Close that transaction.  The above was to setup the initial scenario.  The
-  // real test starts now.
-
-  {
-    syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER,
-                                     share()->directory.get());
-
-    syncable::MutableEntry child_a(&trans, syncable::GET_BY_HANDLE, child_a_id);
-    syncable::MutableEntry child_b(&trans, syncable::GET_BY_HANDLE, child_b_id);
-
-    // Move child A from folder A to folder B and update its position.
-    child_a.PutParentId(child_b.GetParentId());
-    child_a.PutPredecessor(child_b.GetId());
-  }
-
-  EXPECT_EQ(1UL, GetChangeListSize());
-
-  // Verify that only child a is in the change list.
-  // (This function will add a failure if the lookup fails.)
-  FindChangeInList(child_a_id, ChangeRecord::ACTION_UPDATE);
-}
-
-// Tests the ordering of deletion changes.
-TEST_F(SyncManagerChangeProcessingTest, DeletionsAndChanges) {
-  int64_t type_root = GetIdForDataType(BOOKMARKS);
-  int64_t folder_a_id = kInvalidId;
-  int64_t folder_b_id = kInvalidId;
-  int64_t child_id = kInvalidId;
-
-  // Create two folders.  Place a child under folder A.
-  {
-    syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER,
-                                     share()->directory.get());
-    syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root);
-    ASSERT_TRUE(root.good());
-
-    syncable::MutableEntry folder_a(&trans, syncable::CREATE, BOOKMARKS,
-                                    root.GetId(), "folderA");
-    ASSERT_TRUE(folder_a.good());
-    SetNodeProperties(&folder_a);
-    folder_a.PutIsDir(true);
-    folder_a_id = folder_a.GetMetahandle();
-
-    syncable::MutableEntry folder_b(&trans, syncable::CREATE, BOOKMARKS,
-                                    root.GetId(), "folderB");
-    ASSERT_TRUE(folder_b.good());
-    SetNodeProperties(&folder_b);
-    folder_b.PutIsDir(true);
-    folder_b_id = folder_b.GetMetahandle();
-
-    syncable::MutableEntry child(&trans, syncable::CREATE, BOOKMARKS,
-                                 folder_a.GetId(), "child");
-    ASSERT_TRUE(child.good());
-    SetNodeProperties(&child);
-    child_id = child.GetMetahandle();
-  }
-
-  // Close that transaction.  The above was to setup the initial scenario.  The
-  // real test starts now.
-
-  {
-    syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER,
-                                     share()->directory.get());
-
-    syncable::MutableEntry folder_a(&trans, syncable::GET_BY_HANDLE,
-                                    folder_a_id);
-    syncable::MutableEntry folder_b(&trans, syncable::GET_BY_HANDLE,
-                                    folder_b_id);
-    syncable::MutableEntry child(&trans, syncable::GET_BY_HANDLE, child_id);
-
-    // Delete folder B and its child.
-    child.PutIsDel(true);
-    folder_b.PutIsDel(true);
-
-    // Make an unrelated change to folder A.
-    folder_a.PutNonUniqueName("NewNameA");
-  }
-
-  EXPECT_EQ(3UL, GetChangeListSize());
-
-  size_t folder_a_pos =
-      FindChangeInList(folder_a_id, ChangeRecord::ACTION_UPDATE);
-  size_t folder_b_pos =
-      FindChangeInList(folder_b_id, ChangeRecord::ACTION_DELETE);
-  size_t child_pos = FindChangeInList(child_id, ChangeRecord::ACTION_DELETE);
-
-  // Deletes should appear before updates.
-  EXPECT_LT(child_pos, folder_a_pos);
-  EXPECT_LT(folder_b_pos, folder_a_pos);
-}
-
-// During initialization SyncManagerImpl loads sqlite database. If it fails to
-// do so it should fail initialization. This test verifies this behavior.
-// Test reuses SyncManagerImpl initialization from SyncManagerTest but overrides
-// EngineComponentsFactory to return DirectoryBackingStore that always fails
-// to load.
-class SyncManagerInitInvalidStorageTest : public SyncManagerTest {
- public:
-  SyncManagerInitInvalidStorageTest() {}
-
-  EngineComponentsFactory* GetFactory() override {
-    return new TestEngineComponentsFactory(
-        EngineComponentsFactory::STORAGE_INVALID, &storage_used_);
-  }
-};
-
-// SyncManagerInitInvalidStorageTest::GetFactory will return
-// DirectoryBackingStore that ensures that SyncManagerImpl::OpenDirectory fails.
-// SyncManagerImpl initialization is done in SyncManagerTest::SetUp. This test's
-// task is to ensure that SyncManagerImpl reported initialization failure in
-// OnInitializationComplete callback.
-TEST_F(SyncManagerInitInvalidStorageTest, FailToOpenDatabase) {
-  EXPECT_FALSE(initialization_succeeded_);
-}
-
 }  // namespace syncer
diff --git a/components/sync/engine_impl/sync_scheduler_impl_unittest.cc b/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
index 34658587..dbb5f85 100644
--- a/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
@@ -26,11 +26,11 @@
 #include "components/sync/engine/sync_engine_switches.h"
 #include "components/sync/engine_impl/backoff_delay_provider.h"
 #include "components/sync/engine_impl/cycle/test_util.h"
-#include "components/sync/syncable/test_user_share.h"
 #include "components/sync/test/callback_counter.h"
 #include "components/sync/test/engine/fake_model_worker.h"
 #include "components/sync/test/engine/mock_connection_manager.h"
 #include "components/sync/test/engine/mock_nudge_handler.h"
+#include "components/sync/test/fake_sync_encryption_handler.h"
 #include "components/sync/test/mock_invalidation.h"
 #include "net/http/http_status_code.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -125,7 +125,6 @@
   };
 
   void SetUp() override {
-    test_user_share_.SetUp();
     delay_ = nullptr;
     extensions_activity_ = new ExtensionsActivity();
 
@@ -134,12 +133,12 @@
         base::MakeRefCounted<FakeModelWorker>(GROUP_NON_BLOCKING));
     workers_.push_back(base::MakeRefCounted<FakeModelWorker>(GROUP_PASSIVE));
 
-    connection_ = std::make_unique<MockConnectionManager>(directory());
+    connection_ = std::make_unique<MockConnectionManager>();
     connection_->SetServerReachable();
 
     model_type_registry_ = std::make_unique<ModelTypeRegistry>(
-        workers_, test_user_share_.user_share(), &mock_nudge_handler_,
-        &cancelation_signal_, test_user_share_.keystore_keys_handler());
+        workers_, &mock_nudge_handler_, &cancelation_signal_,
+        &encryption_handler_);
     model_type_registry_->ConnectNonBlockingType(
         HISTORY_DELETE_DIRECTIVES,
         MakeFakeActivationResponse(HISTORY_DELETE_DIRECTIVES));
@@ -151,10 +150,10 @@
         TYPED_URLS, MakeFakeActivationResponse(TYPED_URLS));
 
     context_ = std::make_unique<SyncCycleContext>(
-        connection_.get(), directory(), extensions_activity_.get(),
+        connection_.get(), extensions_activity_.get(),
         std::vector<SyncEngineEventListener*>(), nullptr,
         model_type_registry_.get(), "fake_invalidator_client_id",
-        "fake_birthday", "fake_bag_of_chips",
+        "fake_cache_guid", "fake_birthday", "fake_bag_of_chips",
         /*poll_interval=*/base::TimeDelta::FromMinutes(30));
     context_->set_notifications_enabled(true);
     context_->set_account_name("Test");
@@ -186,7 +185,6 @@
     PumpLoop();
     scheduler_.reset();
     PumpLoop();
-    test_user_share_.TearDown();
   }
 
   void AnalyzePollRun(const SyncShareTimes& times,
@@ -328,11 +326,7 @@
     return tick_clock_->NowTicks();
   }
 
-  syncable::Directory* directory() {
-    return test_user_share_.user_share()->directory.get();
-  }
-
-  TestUserShare test_user_share_;
+  FakeSyncEncryptionHandler encryption_handler_;
   CancelationSignal cancelation_signal_;
   std::unique_ptr<MockConnectionManager> connection_;
   std::unique_ptr<ModelTypeRegistry> model_type_registry_;
diff --git a/components/sync/engine_impl/syncer.cc b/components/sync/engine_impl/syncer.cc
index 799a0da..f62d424 100644
--- a/components/sync/engine_impl/syncer.cc
+++ b/components/sync/engine_impl/syncer.cc
@@ -20,7 +20,6 @@
 #include "components/sync/engine_impl/get_updates_delegate.h"
 #include "components/sync/engine_impl/get_updates_processor.h"
 #include "components/sync/engine_impl/net/server_connection_manager.h"
-#include "components/sync/syncable/directory.h"
 
 namespace syncer {
 
@@ -146,14 +145,13 @@
   // errors from the ServerConnectionManager if an exist has been requested.
   // However, it doesn't hurt to check it anyway.
   while (!ExitRequested()) {
-    std::unique_ptr<Commit> commit(
-        Commit::Init(cycle->context()->GetEnabledTypes(),
-                     cycle->context()->max_commit_batch_size(),
-                     cycle->context()->account_name(),
-                     cycle->context()->directory()->cache_guid(),
-                     cycle->context()->cookie_jar_mismatch(),
-                     cycle->context()->cookie_jar_empty(), &commit_processor,
-                     cycle->context()->extensions_activity()));
+    std::unique_ptr<Commit> commit(Commit::Init(
+        cycle->context()->GetEnabledTypes(),
+        cycle->context()->max_commit_batch_size(),
+        cycle->context()->account_name(), cycle->context()->cache_guid(),
+        cycle->context()->cookie_jar_mismatch(),
+        cycle->context()->cookie_jar_empty(), &commit_processor,
+        cycle->context()->extensions_activity()));
     if (!commit) {
       break;
     }
diff --git a/components/sync/engine_impl/syncer_proto_util.cc b/components/sync/engine_impl/syncer_proto_util.cc
index 3173ed12..6ec00cb5 100644
--- a/components/sync/engine_impl/syncer_proto_util.cc
+++ b/components/sync/engine_impl/syncer_proto_util.cc
@@ -19,8 +19,6 @@
 #include "components/sync/engine_impl/traffic_logger.h"
 #include "components/sync/protocol/sync_enums.pb.h"
 #include "components/sync/protocol/sync_protocol_error.h"
-#include "components/sync/syncable/entry.h"
-#include "components/sync/syncable/syncable_proto_util.h"
 #include "google_apis/google_api_keys.h"
 
 using std::string;
@@ -29,16 +27,6 @@
 using sync_pb::ClientToServerResponse;
 
 namespace syncer {
-
-using syncable::BASE_VERSION;
-using syncable::CTIME;
-using syncable::ID;
-using syncable::IS_DEL;
-using syncable::IS_DIR;
-using syncable::IS_UNSYNCED;
-using syncable::MTIME;
-using syncable::PARENT_ID;
-
 namespace {
 
 // Time to backoff syncing after receiving a throttled response.
diff --git a/components/sync/engine_impl/syncer_proto_util_unittest.cc b/components/sync/engine_impl/syncer_proto_util_unittest.cc
index 8c33d64b..b45c0de2 100644
--- a/components/sync/engine_impl/syncer_proto_util_unittest.cc
+++ b/components/sync/engine_impl/syncer_proto_util_unittest.cc
@@ -17,7 +17,6 @@
 #include "components/sync/protocol/sync.pb.h"
 #include "components/sync/protocol/sync_enums.pb.h"
 #include "components/sync/test/engine/mock_connection_manager.h"
-#include "components/sync/test/engine/test_directory_setter_upper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
@@ -111,22 +110,19 @@
 class SyncerProtoUtilTest : public testing::Test {
  public:
   void SetUp() override {
-    dir_maker_.SetUp();
     context_ = std::make_unique<SyncCycleContext>(
         /*connection_manager=*/nullptr,
-        /*directory=*/dir_maker_.directory(),
         /*extensions_activity=*/nullptr,
         /*listeners=*/std::vector<SyncEngineEventListener*>(),
         /*debug_info_getter=*/nullptr,
         /*model_type_registry=*/nullptr,
         /*invalidator_client_id=*/"",
+        /*cache_guid=*/"",
         /*birthday=*/"",
         /*bag_of_chips=*/"",
         /*poll_internal=*/base::TimeDelta::FromSeconds(1));
   }
 
-  void TearDown() override { dir_maker_.TearDown(); }
-
   SyncCycleContext* context() { return context_.get(); }
 
   // Helper function to call GetProtocolErrorFromResponse. Allows not adding
@@ -139,7 +135,6 @@
 
  protected:
   base::test::SingleThreadTaskEnvironment task_environment_;
-  TestDirectorySetterUpper dir_maker_;
   std::unique_ptr<SyncCycleContext> context_;
 };
 
diff --git a/components/sync/engine_impl/syncer_unittest.cc b/components/sync/engine_impl/syncer_unittest.cc
index 26fc1787..2fd4a06 100644
--- a/components/sync/engine_impl/syncer_unittest.cc
+++ b/components/sync/engine_impl/syncer_unittest.cc
@@ -44,15 +44,11 @@
 #include "components/sync/nigori/keystore_keys_handler.h"
 #include "components/sync/protocol/bookmark_specifics.pb.h"
 #include "components/sync/protocol/preference_specifics.pb.h"
-#include "components/sync/syncable/directory.h"
-#include "components/sync/syncable/syncable_id.h"
-#include "components/sync/syncable/test_user_share.h"
-#include "components/sync/syncable/user_share.h"
 #include "components/sync/test/engine/fake_model_worker.h"
 #include "components/sync/test/engine/mock_connection_manager.h"
 #include "components/sync/test/engine/mock_model_type_processor.h"
 #include "components/sync/test/engine/mock_nudge_handler.h"
-#include "components/sync/test/engine/test_syncable_utils.h"
+#include "components/sync/test/fake_sync_encryption_handler.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -235,8 +231,7 @@
   }
 
   void SetUp() override {
-    test_user_share_.SetUp();
-    mock_server_ = std::make_unique<MockConnectionManager>(directory());
+    mock_server_ = std::make_unique<MockConnectionManager>();
     debug_info_getter_ = std::make_unique<MockDebugInfoGetter>();
     workers_.push_back(scoped_refptr<ModelSafeWorker>(
         new FakeModelWorker(GROUP_NON_BLOCKING)));
@@ -244,8 +239,8 @@
     listeners.push_back(this);
 
     model_type_registry_ = std::make_unique<ModelTypeRegistry>(
-        workers_, test_user_share_.user_share(), &mock_nudge_handler_,
-        &cancelation_signal_, test_user_share_.keystore_keys_handler());
+        workers_, &mock_nudge_handler_, &cancelation_signal_,
+        &encryption_handler_);
     model_type_registry_->RegisterDirectoryTypeDebugInfoObserver(
         &debug_info_cache_);
 
@@ -255,10 +250,10 @@
     EnableDatatype(PREFERENCES);
 
     context_ = std::make_unique<SyncCycleContext>(
-        mock_server_.get(), directory(), extensions_activity_.get(), listeners,
+        mock_server_.get(), extensions_activity_.get(), listeners,
         debug_info_getter_.get(), model_type_registry_.get(),
-        "fake_invalidator_client_id", mock_server_->store_birthday(),
-        "fake_bag_of_chips",
+        "fake_invalidator_client_id", local_cache_guid(),
+        mock_server_->store_birthday(), "fake_bag_of_chips",
         /*poll_interval=*/base::TimeDelta::FromMinutes(30));
     syncer_ = new Syncer(&cancelation_signal_);
     scheduler_ = std::make_unique<SyncSchedulerImpl>(
@@ -275,7 +270,6 @@
         &debug_info_cache_);
     mock_server_.reset();
     scheduler_.reset();
-    test_user_share_.TearDown();
   }
 
   void VerifyNoHierarchyConflictsReported(
@@ -298,11 +292,7 @@
     return debug_info_cache_.GetLatestStatusCounters(type);
   }
 
-  syncable::Directory* directory() {
-    return test_user_share_.user_share()->directory.get();
-  }
-
-  const std::string local_cache_guid() { return directory()->cache_guid(); }
+  const std::string local_cache_guid() { return "lD16ebCGCZh+zkiZ68gWDw=="; }
 
   const std::string foreign_cache_guid() { return "kqyg7097kro6GSUod+GSg=="; }
 
@@ -346,7 +336,7 @@
 
   base::test::SingleThreadTaskEnvironment task_environment_;
 
-  TestUserShare test_user_share_;
+  FakeSyncEncryptionHandler encryption_handler_;
   scoped_refptr<ExtensionsActivity> extensions_activity_;
   std::unique_ptr<MockConnectionManager> mock_server_;
   CancelationSignal cancelation_signal_;
diff --git a/components/sync/engine_impl/test_entry_factory.cc b/components/sync/engine_impl/test_entry_factory.cc
deleted file mode 100644
index 06dc2f9..0000000
--- a/components/sync/engine_impl/test_entry_factory.cc
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/engine_impl/test_entry_factory.h"
-
-#include "components/sync/base/client_tag_hash.h"
-#include "components/sync/syncable/directory.h"
-#include "components/sync/syncable/entry.h"
-#include "components/sync/syncable/model_neutral_mutable_entry.h"
-#include "components/sync/syncable/mutable_entry.h"
-#include "components/sync/syncable/syncable_id.h"
-#include "components/sync/syncable/syncable_model_neutral_write_transaction.h"
-#include "components/sync/syncable/syncable_read_transaction.h"
-#include "components/sync/syncable/syncable_write_transaction.h"
-#include "components/sync/test/engine/test_id_factory.h"
-
-using std::string;
-
-namespace syncer {
-
-using syncable::Id;
-using syncable::MutableEntry;
-using syncable::UNITTEST;
-
-TestEntryFactory::TestEntryFactory(syncable::Directory* dir)
-    : directory_(dir), next_revision_(1) {}
-
-TestEntryFactory::~TestEntryFactory() {}
-
-int64_t TestEntryFactory::CreateUnappliedNewItemWithParent(
-    const string& item_id,
-    const sync_pb::EntitySpecifics& specifics,
-    const string& parent_id) {
-  syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory_);
-  MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
-                     Id::CreateFromServerId(item_id));
-  DCHECK(entry.good());
-  entry.PutServerVersion(GetNextRevision());
-  entry.PutIsUnappliedUpdate(true);
-
-  entry.PutServerNonUniqueName(item_id);
-  entry.PutServerParentId(Id::CreateFromServerId(parent_id));
-  entry.PutServerIsDir(true);
-  entry.PutServerSpecifics(specifics);
-  return entry.GetMetahandle();
-}
-
-int64_t TestEntryFactory::CreateUnappliedNewBookmarkItemWithParent(
-    const string& item_id,
-    const sync_pb::EntitySpecifics& specifics,
-    const string& parent_id) {
-  syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory_);
-  MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
-                     Id::CreateFromServerId(item_id));
-  DCHECK(entry.good());
-  entry.PutServerVersion(GetNextRevision());
-  entry.PutIsUnappliedUpdate(true);
-
-  entry.PutServerNonUniqueName(item_id);
-  entry.PutServerParentId(Id::CreateFromServerId(parent_id));
-  entry.PutServerIsDir(true);
-  entry.PutServerSpecifics(specifics);
-
-  return entry.GetMetahandle();
-}
-
-int64_t TestEntryFactory::CreateUnappliedNewItem(
-    const string& item_id,
-    const sync_pb::EntitySpecifics& specifics,
-    bool is_unique) {
-  syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory_);
-  MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
-                     Id::CreateFromServerId(item_id));
-  DCHECK(entry.good());
-  entry.PutServerVersion(GetNextRevision());
-  entry.PutIsUnappliedUpdate(true);
-  entry.PutServerNonUniqueName(item_id);
-  entry.PutServerParentId(syncable::Id::GetRoot());
-  entry.PutServerIsDir(is_unique);
-  entry.PutServerSpecifics(specifics);
-  if (is_unique) {  // For top-level nodes.
-    entry.PutUniqueServerTag(
-        ModelTypeToRootTag(GetModelTypeFromSpecifics(specifics)));
-  }
-  return entry.GetMetahandle();
-}
-
-void TestEntryFactory::CreateUnsyncedItem(const Id& item_id,
-                                          const Id& parent_id,
-                                          const string& name,
-                                          bool is_folder,
-                                          ModelType model_type,
-                                          int64_t* metahandle_out) {
-  if (is_folder) {
-    DCHECK_EQ(model_type, BOOKMARKS);
-  }
-
-  syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory_);
-
-  MutableEntry entry(&trans, syncable::CREATE, model_type, parent_id, name);
-  DCHECK(entry.good());
-  entry.PutId(item_id);
-  entry.PutBaseVersion(item_id.ServerKnows() ? GetNextRevision() : 0);
-  entry.PutIsUnsynced(true);
-  entry.PutIsDir(is_folder);
-  entry.PutIsDel(false);
-  entry.PutParentId(parent_id);
-  sync_pb::EntitySpecifics default_specifics;
-  AddDefaultFieldValue(model_type, &default_specifics);
-  entry.PutSpecifics(default_specifics);
-
-  if (item_id.ServerKnows()) {
-    entry.PutServerSpecifics(default_specifics);
-    entry.PutServerIsDir(false);
-    entry.PutServerParentId(parent_id);
-    entry.PutServerIsDel(false);
-  }
-  if (metahandle_out)
-    *metahandle_out = entry.GetMetahandle();
-}
-
-int64_t TestEntryFactory::CreateUnappliedAndUnsyncedBookmarkItem(
-    const string& name) {
-  int64_t metahandle = 0;
-  CreateUnsyncedItem(TestIdFactory::MakeServer(name), TestIdFactory::root(),
-                     name, false, BOOKMARKS, &metahandle);
-
-  syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory_);
-  MutableEntry entry(&trans, syncable::GET_BY_HANDLE, metahandle);
-  if (!entry.good()) {
-    NOTREACHED();
-    return syncable::kInvalidMetaHandle;
-  }
-
-  entry.PutIsUnappliedUpdate(true);
-  entry.PutServerVersion(GetNextRevision());
-
-  return metahandle;
-}
-
-int64_t TestEntryFactory::CreateSyncedItem(const std::string& name,
-                                           ModelType model_type,
-                                           bool is_folder) {
-  return CreateSyncedItem(name, model_type, is_folder,
-                          sync_pb::EntitySpecifics());
-}
-
-int64_t TestEntryFactory::CreateSyncedItem(
-    const std::string& name,
-    ModelType model_type,
-    bool is_folder,
-    const sync_pb::EntitySpecifics& specifics) {
-  syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory_);
-
-  // Use the type root if it exists or the real root otherwise.
-  syncable::Entry root(&trans, syncable::GET_TYPE_ROOT, model_type);
-  syncable::Id parent_id = root.good() ? root.GetId() : TestIdFactory::root();
-
-  MutableEntry entry(&trans, syncable::CREATE, model_type, parent_id, name);
-  if (!entry.good()) {
-    NOTREACHED();
-    return syncable::kInvalidMetaHandle;
-  }
-
-  PopulateEntry(parent_id, name, model_type, &entry);
-  entry.PutIsDir(is_folder);
-  entry.PutServerIsDir(is_folder);
-
-  // Only rewrite the default specifics inside the entry (which have the model
-  // type marker already) if |specifics| actually contains data.
-  if (specifics.ByteSize() > 0) {
-    entry.PutSpecifics(specifics);
-    entry.PutServerSpecifics(specifics);
-  } else {
-    entry.PutServerSpecifics(entry.GetSpecifics());
-  }
-
-  return entry.GetMetahandle();
-}
-
-int64_t TestEntryFactory::CreateTombstone(const std::string& name,
-                                          ModelType model_type) {
-  syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory_);
-
-  // Use the type root if it exists or the real root otherwise.
-  syncable::Entry root(&trans, syncable::GET_TYPE_ROOT, model_type);
-  syncable::Id parent_id = root.good() ? root.GetId() : TestIdFactory::root();
-
-  MutableEntry entry(&trans, syncable::CREATE, model_type, parent_id, name);
-  if (!entry.good()) {
-    NOTREACHED();
-    return syncable::kInvalidMetaHandle;
-  }
-
-  PopulateEntry(parent_id, name, model_type, &entry);
-  entry.PutIsDel(true);
-  entry.PutServerIsDel(true);
-  return entry.GetMetahandle();
-}
-
-int64_t TestEntryFactory::CreateTypeRootNode(ModelType model_type) {
-  syncable::ModelNeutralWriteTransaction trans(FROM_HERE, syncable::UNITTEST,
-                                               directory_);
-  sync_pb::EntitySpecifics specifics;
-  AddDefaultFieldValue(model_type, &specifics);
-  syncable::ModelNeutralMutableEntry entry(
-      &trans, syncable::CREATE_NEW_TYPE_ROOT, model_type);
-  DCHECK(entry.good());
-  entry.PutServerIsDir(true);
-  entry.PutUniqueServerTag(ModelTypeToRootTag(model_type));
-  return entry.GetMetahandle();
-}
-
-int64_t TestEntryFactory::CreateUnappliedRootNode(ModelType model_type) {
-  syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, directory_);
-  sync_pb::EntitySpecifics specifics;
-  AddDefaultFieldValue(model_type, &specifics);
-  syncable::Id node_id = TestIdFactory::MakeServer("xyz");
-  syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
-                               node_id);
-  DCHECK(entry.good());
-  // Make it look like sort of like a pending creation from the server.
-  // The SERVER_PARENT_ID and UNIQUE_CLIENT_TAG aren't quite right, but
-  // it's good enough for our purposes.
-  entry.PutServerVersion(1);
-  entry.PutIsUnappliedUpdate(true);
-  entry.PutServerIsDir(false);
-  entry.PutServerParentId(TestIdFactory::root());
-  entry.PutServerSpecifics(specifics);
-  entry.PutNonUniqueName("xyz");
-
-  return entry.GetMetahandle();
-}
-
-bool TestEntryFactory::SetServerSpecificsForItem(
-    int64_t meta_handle,
-    const sync_pb::EntitySpecifics specifics) {
-  syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory_);
-  MutableEntry entry(&trans, syncable::GET_BY_HANDLE, meta_handle);
-  if (!entry.good()) {
-    return false;
-  }
-  entry.PutServerSpecifics(specifics);
-  entry.PutIsUnappliedUpdate(true);
-  return true;
-}
-
-bool TestEntryFactory::SetLocalSpecificsForItem(
-    int64_t meta_handle,
-    const sync_pb::EntitySpecifics specifics) {
-  syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory_);
-  MutableEntry entry(&trans, syncable::GET_BY_HANDLE, meta_handle);
-  if (!entry.good()) {
-    return false;
-  }
-  entry.PutSpecifics(specifics);
-  entry.PutIsUnsynced(true);
-  return true;
-}
-
-const sync_pb::EntitySpecifics& TestEntryFactory::GetServerSpecificsForItem(
-    int64_t meta_handle) const {
-  syncable::ReadTransaction trans(FROM_HERE, directory_);
-  syncable::Entry entry(&trans, syncable::GET_BY_HANDLE, meta_handle);
-  DCHECK(entry.good());
-  return entry.GetServerSpecifics();
-}
-
-const sync_pb::EntitySpecifics& TestEntryFactory::GetLocalSpecificsForItem(
-    int64_t meta_handle) const {
-  syncable::ReadTransaction trans(FROM_HERE, directory_);
-  syncable::Entry entry(&trans, syncable::GET_BY_HANDLE, meta_handle);
-  DCHECK(entry.good());
-  return entry.GetSpecifics();
-}
-
-
-bool TestEntryFactory::GetIsUnsyncedForItem(int64_t meta_handle) const {
-  syncable::ReadTransaction trans(FROM_HERE, directory_);
-  syncable::Entry entry(&trans, syncable::GET_BY_HANDLE, meta_handle);
-  if (!entry.good()) {
-    NOTREACHED();
-    return false;
-  }
-  return entry.GetIsUnsynced();
-}
-
-bool TestEntryFactory::GetIsUnappliedForItem(int64_t meta_handle) const {
-  syncable::ReadTransaction trans(FROM_HERE, directory_);
-  syncable::Entry entry(&trans, syncable::GET_BY_HANDLE, meta_handle);
-  if (!entry.good()) {
-    NOTREACHED();
-    return false;
-  }
-  return entry.GetIsUnappliedUpdate();
-}
-
-int64_t TestEntryFactory::GetNextRevision() {
-  return next_revision_++;
-}
-
-void TestEntryFactory::PopulateEntry(const syncable::Id& parent_id,
-                                     const std::string& name,
-                                     ModelType model_type,
-                                     MutableEntry* entry) {
-  syncable::Id item_id(TestIdFactory::MakeServer(name));
-  int64_t version = GetNextRevision();
-  base::Time now = base::Time::Now();
-
-  entry->PutId(item_id);
-  entry->PutCtime(now);
-  entry->PutMtime(now);
-  entry->PutUniqueClientTag(
-      ClientTagHash::FromUnhashed(model_type, name).value());
-  entry->PutBaseVersion(version);
-  entry->PutIsUnsynced(false);
-  entry->PutNonUniqueName(name);
-  entry->PutIsDir(false);
-  entry->PutIsDel(false);
-  entry->PutParentId(parent_id);
-
-  entry->PutServerCtime(now);
-  entry->PutServerMtime(now);
-  entry->PutServerVersion(version);
-  entry->PutIsUnappliedUpdate(false);
-  entry->PutServerNonUniqueName(name);
-  entry->PutServerParentId(parent_id);
-  entry->PutServerIsDir(false);
-  entry->PutServerIsDel(false);
-}
-
-}  // namespace syncer
diff --git a/components/sync/engine_impl/test_entry_factory.h b/components/sync/engine_impl/test_entry_factory.h
deleted file mode 100644
index 946e069..0000000
--- a/components/sync/engine_impl/test_entry_factory.h
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_ENGINE_IMPL_TEST_ENTRY_FACTORY_H_
-#define COMPONENTS_SYNC_ENGINE_IMPL_TEST_ENTRY_FACTORY_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/macros.h"
-#include "components/sync/base/model_type.h"
-#include "components/sync/protocol/sync.pb.h"
-
-namespace syncer {
-
-namespace syncable {
-class Directory;
-class Id;
-class MutableEntry;
-}
-
-class TestEntryFactory {
- public:
-  explicit TestEntryFactory(syncable::Directory* dir);
-  ~TestEntryFactory();
-
-  // Create a new unapplied folder node with a parent.
-  int64_t CreateUnappliedNewItemWithParent(
-      const std::string& item_id,
-      const sync_pb::EntitySpecifics& specifics,
-      const std::string& parent_id);
-
-  int64_t CreateUnappliedNewBookmarkItemWithParent(
-      const std::string& item_id,
-      const sync_pb::EntitySpecifics& specifics,
-      const std::string& parent_id);
-
-  // Create a new unapplied update without a parent.
-  int64_t CreateUnappliedNewItem(const std::string& item_id,
-                                 const sync_pb::EntitySpecifics& specifics,
-                                 bool is_unique);
-
-  // Create an unsynced unique_client_tag item in the database.  If item_id is a
-  // local ID, it will be treated as a create-new.  Otherwise, if it's a server
-  // ID, we'll fake the server data so that it looks like it exists on the
-  // server.  Returns the methandle of the created item in |metahandle_out| if
-  // not null.
-  void CreateUnsyncedItem(const syncable::Id& item_id,
-                          const syncable::Id& parent_id,
-                          const std::string& name,
-                          bool is_folder,
-                          ModelType model_type,
-                          int64_t* metahandle_out);
-
-  // Creates a bookmark that is both unsynced an an unapplied update.  Returns
-  // the metahandle of the created item.
-  int64_t CreateUnappliedAndUnsyncedBookmarkItem(const std::string& name);
-
-  // Creates a unique_client_tag item that has neither IS_UNSYNCED or
-  // IS_UNAPPLIED_UPDATE. The item is known to both the server and client.
-  // Returns the metahandle of the created item. |specifics| is optional.
-  int64_t CreateSyncedItem(const std::string& name,
-                           ModelType model_type,
-                           bool is_folder);
-  int64_t CreateSyncedItem(const std::string& name,
-                           ModelType model_type,
-                           bool is_folder,
-                           const sync_pb::EntitySpecifics& specifics);
-
-  // Create a tombstone for |name|; returns the metahandle of the entry.
-  int64_t CreateTombstone(const std::string& name, ModelType model_type);
-
-  // Creates a root node for |model_type|.
-  int64_t CreateTypeRootNode(ModelType model_type);
-
-  // Creates a root node that IS_UNAPPLIED. Similar to what one would find in
-  // the database between the ProcessUpdates of an initial datatype configure
-  // cycle and the ApplyUpdates step of the same sync cycle.
-  int64_t CreateUnappliedRootNode(ModelType model_type);
-
-  // Looks up the item referenced by |meta_handle|. If successful, overwrites
-  // the server specifics with |specifics|, sets
-  // IS_UNAPPLIED_UPDATES/IS_UNSYNCED appropriately, and returns true.
-  // Else, return false.
-  bool SetServerSpecificsForItem(int64_t meta_handle,
-                                 const sync_pb::EntitySpecifics specifics);
-
-  // Looks up the item referenced by |meta_handle|. If successful, overwrites
-  // the local specifics with |specifics|, sets
-  // IS_UNAPPLIED_UPDATES/IS_UNSYNCED appropriately, and returns true.
-  // Else, return false.
-  bool SetLocalSpecificsForItem(int64_t meta_handle,
-                                const sync_pb::EntitySpecifics specifics);
-
-  // Looks up the item referenced by |meta_handle| and returns its server
-  // specifics.
-  const sync_pb::EntitySpecifics& GetServerSpecificsForItem(
-      int64_t meta_handle) const;
-
-  // Looks up the item referenced by |meta_handle| and returns its specifics.
-  const sync_pb::EntitySpecifics& GetLocalSpecificsForItem(
-      int64_t meta_handle) const;
-
-  // Getters for IS_UNSYNCED and IS_UNAPPLIED_UPDATE bit fields.
-  bool GetIsUnsyncedForItem(int64_t meta_handle) const;
-  bool GetIsUnappliedForItem(int64_t meta_handle) const;
-
-  int64_t GetNextRevision();
-
- private:
-  // Populate an entry with a bunch of default values.
-  void PopulateEntry(const syncable::Id& parent_id,
-                     const std::string& name,
-                     ModelType model_type,
-                     syncable::MutableEntry* entry);
-
-  syncable::Directory* directory_;
-  int64_t next_revision_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestEntryFactory);
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_ENGINE_IMPL_TEST_ENTRY_FACTORY_H_
diff --git a/components/sync/test/engine/mock_connection_manager.cc b/components/sync/test/engine/mock_connection_manager.cc
index 7b7af55..7237b1f 100644
--- a/components/sync/test/engine/mock_connection_manager.cc
+++ b/components/sync/test/engine/mock_connection_manager.cc
@@ -32,7 +32,7 @@
 static char kValidAccessToken[] = "AccessToken";
 static char kCacheGuid[] = "kqyg7097kro6GSUod+GSg==";
 
-MockConnectionManager::MockConnectionManager(syncable::Directory* directory)
+MockConnectionManager::MockConnectionManager()
     : server_reachable_(true),
       conflict_all_commits_(false),
       conflict_n_commits_(0),
@@ -41,7 +41,6 @@
       store_birthday_sent_(false),
       client_stuck_(false),
       countdown_to_postbuffer_fail_(0),
-      directory_(directory),
       mid_commit_observer_(nullptr),
       throttling_(false),
       partial_failure_(false),
@@ -97,18 +96,6 @@
   sync_pb::ClientToServerResponse client_to_server_response;
   client_to_server_response.Clear();
 
-  if (directory_) {
-    // If the Directory's locked when we do this, it's a problem as in normal
-    // use this function could take a while to return because it accesses the
-    // network. As we can't test this we do the next best thing and hang here
-    // when there's an issue.
-    if (!directory_->good()) {
-      ADD_FAILURE();
-      return false;
-    }
-    syncable::WriteTransaction wt(FROM_HERE, syncable::UNITTEST, directory_);
-  }
-
   if (access_token.empty()) {
     http_response->server_status = HttpResponse::SYNC_AUTH_ERROR;
     return false;
diff --git a/components/sync/test/engine/mock_connection_manager.h b/components/sync/test/engine/mock_connection_manager.h
index 1fb9273..8076d3d 100644
--- a/components/sync/test/engine/mock_connection_manager.h
+++ b/components/sync/test/engine/mock_connection_manager.h
@@ -21,13 +21,10 @@
 #include "components/sync/base/unique_position.h"
 #include "components/sync/engine_impl/net/server_connection_manager.h"
 #include "components/sync/protocol/sync.pb.h"
+#include "components/sync/syncable/syncable_id.h"
 
 namespace syncer {
 
-namespace syncable {
-class Directory;
-}
-
 // Mock ServerConnectionManager class for use in client unit tests.
 class MockConnectionManager : public ServerConnectionManager {
  public:
@@ -39,7 +36,7 @@
     virtual ~MidCommitObserver() {}
   };
 
-  explicit MockConnectionManager(syncable::Directory*);
+  MockConnectionManager();
   ~MockConnectionManager() override;
 
   // Overridden ServerConnectionManager functions.
@@ -360,11 +357,6 @@
   // iff we hit zero at that call.
   int countdown_to_postbuffer_fail_;
 
-  // Our directory.  Used only to ensure that we are not holding the transaction
-  // lock when performing network I/O.  Can be null if the test author is
-  // confident this can't happen.
-  syncable::Directory* directory_;
-
   // The updates we'll return to the next request.
   std::list<sync_pb::GetUpdatesResponse> update_queue_;
   base::OnceClosure mid_commit_callback_;
diff --git a/components/sync/test/fake_sync_encryption_handler.cc b/components/sync/test/fake_sync_encryption_handler.cc
index 6db518f..8b5d08b 100644
--- a/components/sync/test/fake_sync_encryption_handler.cc
+++ b/components/sync/test/fake_sync_encryption_handler.cc
@@ -6,41 +6,20 @@
 
 #include "base/base64.h"
 #include "components/sync/base/model_type.h"
-#include "components/sync/base/passphrase_enums.h"
 #include "components/sync/protocol/nigori_specifics.pb.h"
 #include "components/sync/syncable/nigori_util.h"
 
 namespace syncer {
 
 FakeSyncEncryptionHandler::FakeSyncEncryptionHandler()
-    : encrypted_types_(AlwaysEncryptedUserTypes()),
-      encrypt_everything_(false),
-      passphrase_type_(PassphraseType::kImplicitPassphrase),
-      cryptographer_(CryptographerImpl::CreateEmpty()) {}
+    : encrypt_everything_(false) {}
 
 FakeSyncEncryptionHandler::~FakeSyncEncryptionHandler() = default;
 
 bool FakeSyncEncryptionHandler::Init() {
-  // Set up a basic cryptographer.
-  const std::string keystore_key = "keystore_key";
-  cryptographer_->EmplaceKey(keystore_key,
-                             KeyDerivationParams::CreateForPbkdf2());
   return true;
 }
 
-bool FakeSyncEncryptionHandler::ApplyNigoriUpdate(
-    const sync_pb::NigoriSpecifics& nigori,
-    syncable::BaseTransaction* const trans) {
-  return false;
-}
-
-void FakeSyncEncryptionHandler::UpdateNigoriFromEncryptedTypes(
-    sync_pb::NigoriSpecifics* nigori,
-    const syncable::BaseTransaction* const trans) const {
-  syncable::UpdateNigoriFromEncryptedTypes(encrypted_types_,
-                                           encrypt_everything_, nigori);
-}
-
 bool FakeSyncEncryptionHandler::NeedKeystoreKey() const {
   return keystore_key_.empty();
 }
@@ -62,16 +41,6 @@
   return true;
 }
 
-const Cryptographer* FakeSyncEncryptionHandler::GetCryptographer(
-    const syncable::BaseTransaction* const trans) const {
-  return cryptographer_.get();
-}
-
-ModelTypeSet FakeSyncEncryptionHandler::GetEncryptedTypes(
-    const syncable::BaseTransaction* const trans) const {
-  return encrypted_types_;
-}
-
 void FakeSyncEncryptionHandler::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
 }
@@ -82,7 +51,7 @@
 
 void FakeSyncEncryptionHandler::SetEncryptionPassphrase(
     const std::string& passphrase) {
-  passphrase_type_ = PassphraseType::kCustomPassphrase;
+  // Do nothing.
 }
 
 void FakeSyncEncryptionHandler::SetDecryptionPassphrase(
@@ -99,20 +68,16 @@
   if (encrypt_everything_)
     return;
   encrypt_everything_ = true;
-  encrypted_types_ = ModelTypeSet::All();
-  for (auto& observer : observers_)
-    observer.OnEncryptedTypesChanged(encrypted_types_, encrypt_everything_);
+  for (auto& observer : observers_) {
+    observer.OnEncryptedTypesChanged(/*encrypted_types=*/ModelTypeSet::All(),
+                                     encrypt_everything_);
+  }
 }
 
 bool FakeSyncEncryptionHandler::IsEncryptEverythingEnabled() const {
   return encrypt_everything_;
 }
 
-PassphraseType FakeSyncEncryptionHandler::GetPassphraseType(
-    const syncable::BaseTransaction* const trans) const {
-  return passphrase_type_;
-}
-
 base::Time FakeSyncEncryptionHandler::GetKeystoreMigrationTime() const {
   return base::Time();
 }
diff --git a/components/sync/test/fake_sync_encryption_handler.h b/components/sync/test/fake_sync_encryption_handler.h
index 7532d0cb..16772b163 100644
--- a/components/sync/test/fake_sync_encryption_handler.h
+++ b/components/sync/test/fake_sync_encryption_handler.h
@@ -13,9 +13,7 @@
 #include "base/observer_list.h"
 #include "base/time/time.h"
 #include "components/sync/engine/sync_encryption_handler.h"
-#include "components/sync/nigori/cryptographer_impl.h"
 #include "components/sync/nigori/keystore_keys_handler.h"
-#include "components/sync/syncable/nigori_handler.h"
 
 namespace syncer {
 
@@ -26,8 +24,7 @@
 // Note: NOT thread safe. If threads attempt to check encryption state
 // while another thread is modifying it, races can occur.
 class FakeSyncEncryptionHandler : public KeystoreKeysHandler,
-                                  public SyncEncryptionHandler,
-                                  public syncable::NigoriHandler {
+                                  public SyncEncryptionHandler {
  public:
   FakeSyncEncryptionHandler();
   ~FakeSyncEncryptionHandler() override;
@@ -45,30 +42,13 @@
   base::Time GetKeystoreMigrationTime() const override;
   KeystoreKeysHandler* GetKeystoreKeysHandler() override;
 
-  // NigoriHandler implemenation.
-  bool ApplyNigoriUpdate(const sync_pb::NigoriSpecifics& nigori,
-                         syncable::BaseTransaction* const trans) override;
-  void UpdateNigoriFromEncryptedTypes(
-      sync_pb::NigoriSpecifics* nigori,
-      const syncable::BaseTransaction* const trans) const override;
-  const Cryptographer* GetCryptographer(
-      const syncable::BaseTransaction* const trans) const override;
-  ModelTypeSet GetEncryptedTypes(
-      const syncable::BaseTransaction* const trans) const override;
-  PassphraseType GetPassphraseType(
-      const syncable::BaseTransaction* const trans) const override;
-
   // KeystoreKeysHandler implementation.
   bool NeedKeystoreKey() const override;
   bool SetKeystoreKeys(const std::vector<std::vector<uint8_t>>& keys) override;
 
  private:
   base::ObserverList<SyncEncryptionHandler::Observer>::Unchecked observers_;
-  ModelTypeSet encrypted_types_;
   bool encrypt_everything_;
-  PassphraseType passphrase_type_;
-
-  std::unique_ptr<CryptographerImpl> cryptographer_;
   std::vector<uint8_t> keystore_key_;
 };
 
diff --git a/services/test/data/web_bundle/generate-test-wbns.sh b/components/test/data/web_package/generate-test-wbns.sh
similarity index 100%
rename from services/test/data/web_bundle/generate-test-wbns.sh
rename to components/test/data/web_package/generate-test-wbns.sh
diff --git a/services/test/data/web_bundle/hello.wbn b/components/test/data/web_package/hello.wbn
similarity index 100%
rename from services/test/data/web_bundle/hello.wbn
rename to components/test/data/web_package/hello.wbn
Binary files differ
diff --git a/services/test/data/web_bundle/hello/index.html b/components/test/data/web_package/hello/index.html
similarity index 100%
rename from services/test/data/web_bundle/hello/index.html
rename to components/test/data/web_package/hello/index.html
diff --git a/services/test/data/web_bundle/hello/manifest.webmanifest b/components/test/data/web_package/hello/manifest.webmanifest
similarity index 100%
rename from services/test/data/web_bundle/hello/manifest.webmanifest
rename to components/test/data/web_package/hello/manifest.webmanifest
diff --git a/services/test/data/web_bundle/hello/script.js b/components/test/data/web_package/hello/script.js
similarity index 100%
rename from services/test/data/web_bundle/hello/script.js
rename to components/test/data/web_package/hello/script.js
diff --git a/services/test/data/web_bundle/hello_signed.wbn b/components/test/data/web_package/hello_signed.wbn
similarity index 100%
rename from services/test/data/web_bundle/hello_signed.wbn
rename to components/test/data/web_package/hello_signed.wbn
Binary files differ
diff --git a/components/url_formatter/spoof_checks/idn_spoof_checker.cc b/components/url_formatter/spoof_checks/idn_spoof_checker.cc
index b3b0a454..616f470 100644
--- a/components/url_formatter/spoof_checks/idn_spoof_checker.cc
+++ b/components/url_formatter/spoof_checks/idn_spoof_checker.cc
@@ -425,6 +425,10 @@
         return Result::kWholeScriptConfusable;
       }
     }
+    // Disallow domains that contain only numbers and number-spoofs.
+    if (IsDigitLookalike(label_string))
+      return Result::kDigitLookalikes;
+
     return Result::kSafe;
   }
 
diff --git a/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc b/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
index 69c4d9c8..6adb3b2 100644
--- a/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
+++ b/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc
@@ -153,6 +153,8 @@
     {"xn--0-6ee.com", L"੨0.com", kUnsafe},
     // Block fully numeric lookalikes (৪੨.com using U+09EA and U+0A68).
     {"xn--47b6w.com", L"৪੨.com", kUnsafe},
+    // Block single script digit lookalikes (using three U+0A68 characters).
+    {"xn--qccaa.com", L"੨੨੨.com", kUnsafe},
 
     // URL test with mostly numbers and one confusable character
     // Georgian 'd' 4000.com
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 2372281..ba807b6 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -53,18 +53,24 @@
 const base::Feature kVizFrameSubmissionForWebView{
     "VizFrameSubmissionForWebView", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kUsePreferredIntervalForVideo{
+    "UsePreferredIntervalForVideo", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Whether we should use the real buffers corresponding to overlay candidates in
 // order to do a pageflip test rather than allocating test buffers.
 const base::Feature kUseRealBuffersForPageFlipTest{
     "UseRealBuffersForPageFlipTest", base::FEATURE_DISABLED_BY_DEFAULT};
 
+#if defined(OS_FUCHSIA)
+// Enables SkiaOutputDeviceBufferQueue instead of Vulkan swapchain on Fuchsia.
+const base::Feature kUseSkiaOutputDeviceBufferQueue{
+    "UseSkiaOutputDeviceBufferQueue", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 // Whether we should split partially occluded quads to reduce overdraw.
 const base::Feature kSplitPartiallyOccludedQuads{
     "SplitPartiallyOccludedQuads", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kUsePreferredIntervalForVideo{
-    "UsePreferredIntervalForVideo", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Whether we should log extra debug information to webrtc native log.
 const base::Feature kWebRtcLogCapturePipeline{
     "WebRtcLogCapturePipeline", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/viz/common/features.h b/components/viz/common/features.h
index 2a42f53..1b077aa 100644
--- a/components/viz/common/features.h
+++ b/components/viz/common/features.h
@@ -24,6 +24,9 @@
 VIZ_COMMON_EXPORT extern const base::Feature kVizFrameSubmissionForWebView;
 VIZ_COMMON_EXPORT extern const base::Feature kUsePreferredIntervalForVideo;
 VIZ_COMMON_EXPORT extern const base::Feature kUseRealBuffersForPageFlipTest;
+#if defined(OS_FUCHSIA)
+VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaOutputDeviceBufferQueue;
+#endif
 VIZ_COMMON_EXPORT extern const base::Feature kSplitPartiallyOccludedQuads;
 VIZ_COMMON_EXPORT extern const base::Feature kWebRtcLogCapturePipeline;
 
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 7b6f69e..0979770 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -369,7 +369,10 @@
 
   defines = [ "VIZ_SERVICE_IMPLEMENTATION" ]
 
-  deps = [ "//gpu/config" ]
+  deps = [
+    "//base",
+    "//gpu/config",
+  ]
 
   if (is_win) {
     sources += [
@@ -427,6 +430,19 @@
       "//third_party/dawn/src/dawn_native",
     ]
   }
+
+  if (is_fuchsia) {
+    sources += [
+      "display_embedder/output_presenter_fuchsia.cc",
+      "display_embedder/output_presenter_fuchsia.h",
+    ]
+
+    deps += [
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.images",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem",
+      "//third_party/fuchsia-sdk/sdk/pkg/sys_inspect_cpp",
+    ]
+  }
 }
 
 viz_source_set("unit_tests") {
diff --git a/components/viz/service/display_embedder/image_context_impl.h b/components/viz/service/display_embedder/image_context_impl.h
index df8c133..69db76a 100644
--- a/components/viz/service/display_embedder/image_context_impl.h
+++ b/components/viz/service/display_embedder/image_context_impl.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_IMAGE_CONTEXT_IMPL_H_
 
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/macros.h"
@@ -71,6 +72,11 @@
   SkPromiseImageTexture* promise_image_texture() const {
     return promise_image_texture_;
   }
+  GrBackendSurfaceMutableState* end_access_state() const {
+    return representation_scoped_read_access_
+               ? representation_scoped_read_access_->end_state()
+               : nullptr;
+  }
 
   void BeginAccessIfNecessary(
       gpu::SharedContextState* context_state,
diff --git a/components/viz/service/display_embedder/output_presenter.h b/components/viz/service/display_embedder/output_presenter.h
index 5756253..70a30fb 100644
--- a/components/viz/service/display_embedder/output_presenter.h
+++ b/components/viz/service/display_embedder/output_presenter.h
@@ -5,6 +5,9 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_H_
 
+#include <memory>
+#include <vector>
+
 #include "components/viz/service/display/output_surface.h"
 #include "components/viz/service/display/overlay_processor_interface.h"
 #include "components/viz/service/display/skia_output_surface.h"
@@ -44,7 +47,7 @@
 
     virtual void BeginPresent() = 0;
     virtual void EndPresent() = 0;
-    virtual int present_count() = 0;
+    virtual int present_count() const = 0;
 
     base::WeakPtr<Image> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
 
@@ -100,8 +103,7 @@
                              BufferPresentedCallback presentation_callback) = 0;
   virtual void CommitOverlayPlanes(
       SwapCompletionCallback completion_callback,
-      BufferPresentedCallback presentation_callback,
-      std::vector<ui::LatencyInfo> latency_info) = 0;
+      BufferPresentedCallback presentation_callback) = 0;
   virtual void SchedulePrimaryPlane(
       const OverlayProcessorInterface::OutputSurfaceOverlayPlane& plane,
       Image* image,
diff --git a/components/viz/service/display_embedder/output_presenter_fuchsia.cc b/components/viz/service/display_embedder/output_presenter_fuchsia.cc
new file mode 100644
index 0000000..50a65e9
--- /dev/null
+++ b/components/viz/service/display_embedder/output_presenter_fuchsia.cc
@@ -0,0 +1,490 @@
+// 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/viz/service/display_embedder/output_presenter_fuchsia.h"
+
+#include <fuchsia/sysmem/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+#include <lib/sys/inspect/cpp/component.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/feature_list.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/fuchsia/process_context.h"
+#include "base/trace_event/trace_event.h"
+#include "components/viz/common/features.h"
+#include "components/viz/common/gpu/vulkan_context_provider.h"
+#include "components/viz/service/display_embedder/skia_output_surface_dependency.h"
+#include "gpu/command_buffer/service/shared_context_state.h"
+#include "gpu/ipc/common/gpu_client_ids.h"
+#include "gpu/vulkan/vulkan_device_queue.h"
+#include "gpu/vulkan/vulkan_function_pointers.h"
+#include "gpu/vulkan/vulkan_implementation.h"
+#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
+#include "ui/ozone/public/platform_window_surface.h"
+
+namespace viz {
+
+namespace {
+
+void GrSemaphoresToZxEvents(gpu::VulkanImplementation* vulkan_implementation,
+                            VkDevice vk_device,
+                            const std::vector<GrBackendSemaphore>& semaphores,
+                            std::vector<zx::event>* events) {
+  for (auto& semaphore : semaphores) {
+    gpu::SemaphoreHandle handle = vulkan_implementation->GetSemaphoreHandle(
+        vk_device, semaphore.vkSemaphore());
+    DCHECK(handle.is_valid());
+    events->push_back(handle.TakeHandle());
+  }
+}
+
+class PresenterImageFuchsia : public OutputPresenter::Image {
+ public:
+  explicit PresenterImageFuchsia(uint32_t image_id);
+  ~PresenterImageFuchsia() override;
+
+  void BeginPresent() final;
+  void EndPresent() final;
+  int present_count() const final;
+
+  uint32_t image_id() const { return image_id_; }
+
+  void TakeSemaphores(std::vector<GrBackendSemaphore>* read_begin_semaphores,
+                      std::vector<GrBackendSemaphore>* read_end_semaphores);
+
+ private:
+  const uint32_t image_id_;
+
+  int present_count_ = 0;
+
+  std::unique_ptr<gpu::SharedImageRepresentationSkia::ScopedReadAccess>
+      read_access_;
+
+  std::vector<GrBackendSemaphore> read_begin_semaphores_;
+  std::vector<GrBackendSemaphore> read_end_semaphores_;
+};
+
+PresenterImageFuchsia::PresenterImageFuchsia(uint32_t image_id)
+    : image_id_(image_id) {}
+
+PresenterImageFuchsia::~PresenterImageFuchsia() {
+  DCHECK(read_begin_semaphores_.empty());
+  DCHECK(read_end_semaphores_.empty());
+}
+
+void PresenterImageFuchsia::BeginPresent() {
+  ++present_count_;
+
+  if (present_count_ == 1) {
+    DCHECK(!read_access_);
+    DCHECK(read_begin_semaphores_.empty());
+    DCHECK(read_end_semaphores_.empty());
+    read_access_ = skia_representation()->BeginScopedReadAccess(
+        &read_begin_semaphores_, &read_end_semaphores_);
+  }
+}
+
+void PresenterImageFuchsia::EndPresent() {
+  DCHECK(present_count_);
+  --present_count_;
+  if (!present_count_)
+    read_access_.reset();
+}
+
+int PresenterImageFuchsia::present_count() const {
+  return present_count_;
+}
+
+void PresenterImageFuchsia::TakeSemaphores(
+    std::vector<GrBackendSemaphore>* read_begin_semaphores,
+    std::vector<GrBackendSemaphore>* read_end_semaphores) {
+  DCHECK(read_begin_semaphores->empty());
+  std::swap(*read_begin_semaphores, read_begin_semaphores_);
+
+  DCHECK(read_end_semaphores->empty());
+  std::swap(*read_end_semaphores, read_end_semaphores_);
+}
+
+}  // namespace
+
+OutputPresenterFuchsia::PendingFrame::PendingFrame() = default;
+OutputPresenterFuchsia::PendingFrame::~PendingFrame() = default;
+
+OutputPresenterFuchsia::PendingFrame::PendingFrame(PendingFrame&&) = default;
+OutputPresenterFuchsia::PendingFrame&
+OutputPresenterFuchsia::PendingFrame::operator=(PendingFrame&&) = default;
+
+// static
+std::unique_ptr<OutputPresenterFuchsia> OutputPresenterFuchsia::Create(
+    ui::PlatformWindowSurface* window_surface,
+    SkiaOutputSurfaceDependency* deps,
+    gpu::MemoryTracker* memory_tracker) {
+  auto* inspector = base::ComponentInspectorForProcess();
+
+  if (!base::FeatureList::IsEnabled(
+          features::kUseSkiaOutputDeviceBufferQueue)) {
+    inspector->root().CreateString("output_presenter", "swapchain", inspector);
+    return {};
+  }
+
+  inspector->root().CreateString("output_presenter",
+                                 "SkiaOutputDeviceBufferQueue", inspector);
+
+  // SetTextureToNewImagePipe() will call ScenicSession::Present() to send
+  // CreateImagePipe2Cmd creation command, but it will be processed only after
+  // vsync, which will delay buffer allocation of buffers in AllocateImages(),
+  // but that shouldn't cause any issues.
+  fuchsia::images::ImagePipe2Ptr image_pipe;
+  if (!window_surface->SetTextureToNewImagePipe(image_pipe.NewRequest()))
+    return {};
+
+  return std::make_unique<OutputPresenterFuchsia>(std::move(image_pipe), deps,
+                                                  memory_tracker);
+}
+
+OutputPresenterFuchsia::OutputPresenterFuchsia(
+    fuchsia::images::ImagePipe2Ptr image_pipe,
+    SkiaOutputSurfaceDependency* deps,
+    gpu::MemoryTracker* memory_tracker)
+    : image_pipe_(std::move(image_pipe)),
+      dependency_(deps),
+      shared_image_factory_(deps->GetGpuPreferences(),
+                            deps->GetGpuDriverBugWorkarounds(),
+                            deps->GetGpuFeatureInfo(),
+                            deps->GetSharedContextState().get(),
+                            deps->GetMailboxManager(),
+                            deps->GetSharedImageManager(),
+                            deps->GetGpuImageFactory(),
+                            memory_tracker,
+                            true),
+      shared_image_representation_factory_(deps->GetSharedImageManager(),
+                                           memory_tracker) {
+  sysmem_allocator_ = base::ComponentContextForProcess()
+                          ->svc()
+                          ->Connect<fuchsia::sysmem::Allocator>();
+
+  image_pipe_.set_error_handler([this](zx_status_t status) {
+    ZX_LOG(ERROR, status) << "ImagePipe disconnected";
+
+    for (auto& frame : pending_frames_) {
+      std::move(frame.completion_callback)
+          .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED));
+    }
+    pending_frames_.clear();
+  });
+}
+
+OutputPresenterFuchsia::~OutputPresenterFuchsia() {}
+
+void OutputPresenterFuchsia::InitializeCapabilities(
+    OutputSurface::Capabilities* capabilities) {
+  // We expect origin of buffers is at top left.
+  capabilities->output_surface_origin = gfx::SurfaceOrigin::kTopLeft;
+  capabilities->supports_post_sub_buffer = false;
+  capabilities->supports_commit_overlay_planes = false;
+
+  capabilities->sk_color_type = kRGBA_8888_SkColorType;
+  capabilities->gr_backend_format =
+      dependency_->GetSharedContextState()->gr_context()->defaultBackendFormat(
+          capabilities->sk_color_type, GrRenderable::kYes);
+}
+
+bool OutputPresenterFuchsia::Reshape(const gfx::Size& size,
+                                     float device_scale_factor,
+                                     const gfx::ColorSpace& color_space,
+                                     gfx::BufferFormat format,
+                                     gfx::OverlayTransform transform) {
+  if (!image_pipe_)
+    return false;
+
+  frame_size_ = size;
+
+  return true;
+}
+
+std::vector<std::unique_ptr<OutputPresenter::Image>>
+OutputPresenterFuchsia::AllocateImages(gfx::ColorSpace color_space,
+                                       gfx::Size image_size,
+                                       size_t num_images) {
+  if (!image_pipe_)
+    return {};
+
+  // If we already allocated buffer collection then it needs to be released.
+  if (last_buffer_collection_id_) {
+    // If there are pending frames for the old buffer collection then remove the
+    // collection only after that frame is presented. Otherwise remove it now.
+    if (!pending_frames_.empty() &&
+        pending_frames_.back().buffer_collection_id ==
+            last_buffer_collection_id_) {
+      DCHECK(!pending_frames_.back().remove_buffer_collection);
+      pending_frames_.back().remove_buffer_collection = true;
+    } else {
+      image_pipe_->RemoveBufferCollection(last_buffer_collection_id_);
+    }
+  }
+
+  buffer_collection_.reset();
+
+  // Create buffer collection with 2 extra tokens: one for Vulkan and one for
+  // the ImagePipe.
+  fuchsia::sysmem::BufferCollectionTokenPtr collection_token;
+  sysmem_allocator_->AllocateSharedCollection(collection_token.NewRequest());
+
+  fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>
+      token_for_scenic;
+  collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS,
+                              token_for_scenic.NewRequest());
+
+  fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>
+      token_for_vulkan;
+  collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS,
+                              token_for_vulkan.NewRequest());
+
+  fuchsia::sysmem::BufferCollectionSyncPtr collection;
+  sysmem_allocator_->BindSharedCollection(std::move(collection_token),
+                                          collection.NewRequest());
+
+  zx_status_t status = collection->Sync();
+  if (status != ZX_OK) {
+    ZX_DLOG(ERROR, status) << "fuchsia.sysmem.BufferCollection.Sync()";
+    return {};
+  }
+
+  auto* vulkan =
+      dependency_->GetVulkanContextProvider()->GetVulkanImplementation();
+
+  // Set constraints for the new collection.
+  fuchsia::sysmem::BufferCollectionConstraints constraints;
+  constraints.min_buffer_count = num_images;
+  constraints.usage.none = fuchsia::sysmem::noneUsage;
+  constraints.image_format_constraints_count = 1;
+  constraints.image_format_constraints[0].pixel_format.type =
+      fuchsia::sysmem::PixelFormatType::R8G8B8A8;
+  constraints.image_format_constraints[0].min_coded_width = frame_size_.width();
+  constraints.image_format_constraints[0].min_coded_height =
+      frame_size_.height();
+  constraints.image_format_constraints[0].color_spaces_count = 1;
+  constraints.image_format_constraints[0].color_space[0].type =
+      fuchsia::sysmem::ColorSpaceType::SRGB;
+  collection->SetConstraints(true, constraints);
+
+  // Register the new buffer collection with the ImagePipe.
+  last_buffer_collection_id_++;
+  image_pipe_->AddBufferCollection(last_buffer_collection_id_,
+                                   std::move(token_for_scenic));
+
+  // Register the new buffer collection with Vulkan.
+  gfx::SysmemBufferCollectionId buffer_collection_id =
+      gfx::SysmemBufferCollectionId::Create();
+
+  VkDevice vk_device = dependency_->GetVulkanContextProvider()
+                           ->GetDeviceQueue()
+                           ->GetVulkanDevice();
+  buffer_collection_ = vulkan->RegisterSysmemBufferCollection(
+      vk_device, buffer_collection_id, token_for_vulkan.TakeChannel(),
+      buffer_format_, gfx::BufferUsage::SCANOUT);
+
+  // Wait for the images to be allocated.
+  zx_status_t wait_status;
+  fuchsia::sysmem::BufferCollectionInfo_2 buffers_info;
+  status = collection->WaitForBuffersAllocated(&wait_status, &buffers_info);
+  if (status != ZX_OK) {
+    ZX_DLOG(ERROR, status) << "fuchsia.sysmem.BufferCollection failed";
+    return {};
+  }
+
+  if (wait_status != ZX_OK) {
+    ZX_DLOG(ERROR, wait_status)
+        << "Sysmem buffer collection allocation failed.";
+    return {};
+  }
+
+  DCHECK_GE(buffers_info.buffer_count, num_images);
+
+  // We no longer need the BufferCollection connection. Close it to ensure
+  // ImagePipe can still use the collection after BufferCollection connection
+  // is dropped below.
+  collection->Close();
+
+  // Create PresenterImageFuchsia for each buffer in the collection.
+  uint32_t image_usage = gpu::SHARED_IMAGE_USAGE_RASTER;
+  if (vulkan->enforce_protected_memory())
+    image_usage |= gpu::SHARED_IMAGE_USAGE_PROTECTED;
+
+  std::vector<std::unique_ptr<OutputPresenter::Image>> images;
+  images.reserve(num_images);
+
+  fuchsia::sysmem::ImageFormat_2 image_format;
+  image_format.coded_width = frame_size_.width();
+  image_format.coded_height = frame_size_.height();
+
+  // Create an image for each buffer in the collection.
+  for (size_t i = 0; i < num_images; ++i) {
+    last_image_id_++;
+    image_pipe_->AddImage(last_image_id_, last_buffer_collection_id_, i,
+                          image_format);
+
+    gfx::GpuMemoryBufferHandle gmb_handle;
+    gmb_handle.type = gfx::GpuMemoryBufferType::NATIVE_PIXMAP;
+    gmb_handle.native_pixmap_handle.buffer_collection_id = buffer_collection_id;
+    gmb_handle.native_pixmap_handle.buffer_index = i;
+
+    auto mailbox = gpu::Mailbox::GenerateForSharedImage();
+    if (!shared_image_factory_.CreateSharedImage(
+            mailbox, gpu::kInProcessCommandBufferClientId,
+            std::move(gmb_handle), buffer_format_, gpu::kNullSurfaceHandle,
+            frame_size_, color_space, image_usage)) {
+      return {};
+    }
+
+    auto image = std::make_unique<PresenterImageFuchsia>(last_image_id_);
+    if (!image->Initialize(&shared_image_factory_,
+                           &shared_image_representation_factory_, mailbox,
+                           dependency_)) {
+      return {};
+    }
+    images.push_back(std::move(image));
+  }
+
+  return images;
+}
+
+void OutputPresenterFuchsia::SwapBuffers(
+    SwapCompletionCallback completion_callback,
+    BufferPresentedCallback presentation_callback) {
+  if (!image_pipe_) {
+    std::move(completion_callback)
+        .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED));
+    return;
+  }
+
+  // SwapBuffer() should be called only after SchedulePrimaryPlane().
+  DCHECK(next_frame_);
+
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
+      "viz", "OutputPresenterFuchsia::PresentQueue", TRACE_ID_LOCAL(this),
+      "image_id", next_frame_->image_id);
+
+  next_frame_->completion_callback = std::move(completion_callback);
+  next_frame_->presentation_callback = std::move(presentation_callback);
+
+  pending_frames_.push_back(std::move(next_frame_.value()));
+  next_frame_.reset();
+
+  if (!present_is_pending_)
+    PresentNextFrame();
+}
+
+void OutputPresenterFuchsia::PostSubBuffer(
+    const gfx::Rect& rect,
+    SwapCompletionCallback completion_callback,
+    BufferPresentedCallback presentation_callback) {
+  // Sub buffer presentation is not supported.
+  NOTREACHED();
+}
+
+void OutputPresenterFuchsia::CommitOverlayPlanes(
+    SwapCompletionCallback completion_callback,
+    BufferPresentedCallback presentation_callback) {
+  // Overlays are not supported yet.
+  NOTREACHED();
+}
+
+void OutputPresenterFuchsia::SchedulePrimaryPlane(
+    const OverlayProcessorInterface::OutputSurfaceOverlayPlane& plane,
+    Image* image,
+    bool is_submitted) {
+  auto* image_fuchsia = static_cast<PresenterImageFuchsia*>(image);
+
+  DCHECK(!next_frame_);
+  next_frame_ = PendingFrame();
+  next_frame_->image_id = image_fuchsia->image_id();
+  next_frame_->buffer_collection_id = last_buffer_collection_id_;
+
+  // Take semaphores for the image and covert them to zx::events that are later
+  // passed to ImagePipe::PresentImage().
+  std::vector<GrBackendSemaphore> read_begin_semaphores;
+  std::vector<GrBackendSemaphore> read_end_semaphores;
+  image_fuchsia->TakeSemaphores(&read_begin_semaphores, &read_end_semaphores);
+
+  auto* vulkan_context_provider = dependency_->GetVulkanContextProvider();
+  auto* vulkan_implementation =
+      vulkan_context_provider->GetVulkanImplementation();
+  VkDevice vk_device =
+      vulkan_context_provider->GetDeviceQueue()->GetVulkanDevice();
+
+  GrSemaphoresToZxEvents(vulkan_implementation, vk_device,
+                         read_begin_semaphores, &(next_frame_->acquire_fences));
+  GrSemaphoresToZxEvents(vulkan_implementation, vk_device, read_end_semaphores,
+                         &(next_frame_->release_fences));
+
+  // Destroy |read_begin_semaphores|, but not |read_end_semaphores|, since
+  // SharedImageRepresentationSkia::BeginScopedReadAccess() keeps ownership of
+  // the end_semaphores.
+  for (auto& semaphore : read_begin_semaphores) {
+    vkDestroySemaphore(vk_device, semaphore.vkSemaphore(), nullptr);
+  }
+}
+
+std::vector<OutputPresenter::OverlayData>
+OutputPresenterFuchsia::ScheduleOverlays(
+    SkiaOutputSurface::OverlayList overlays) {
+  // Overlays are not supported yet.
+  NOTREACHED();
+  return {};
+}
+
+void OutputPresenterFuchsia::PresentNextFrame() {
+  DCHECK(!present_is_pending_);
+  DCHECK(!pending_frames_.empty());
+
+  TRACE_EVENT_NESTABLE_ASYNC_END1("viz", "OutputPresenterFuchsia::PresentQueue",
+                                  TRACE_ID_LOCAL(this), "image_id",
+                                  pending_frames_.front().image_id);
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
+      "viz", "OutputPresenterFuchsia::PresentFrame", TRACE_ID_LOCAL(this),
+      "image_id", pending_frames_.front().image_id);
+
+  present_is_pending_ = true;
+  uint64_t target_presentation_time = zx_clock_get_monotonic();
+  image_pipe_->PresentImage(
+      pending_frames_.front().image_id, target_presentation_time,
+      std::move(pending_frames_.front().acquire_fences),
+      std::move(pending_frames_.front().release_fences),
+      fit::bind_member(this, &OutputPresenterFuchsia::OnPresentComplete));
+}
+
+void OutputPresenterFuchsia::OnPresentComplete(
+    fuchsia::images::PresentationInfo presentation_info) {
+  DCHECK(present_is_pending_);
+  present_is_pending_ = false;
+
+  TRACE_EVENT_NESTABLE_ASYNC_END1("viz", "OutputPresenterFuchsia::PresentFrame",
+                                  TRACE_ID_LOCAL(this), "image_id",
+                                  pending_frames_.front().image_id);
+
+  std::move(pending_frames_.front().completion_callback)
+      .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK));
+  std::move(pending_frames_.front().presentation_callback)
+      .Run(gfx::PresentationFeedback(
+          base::TimeTicks::FromZxTime(presentation_info.presentation_time),
+          base::TimeDelta::FromZxDuration(
+              presentation_info.presentation_interval),
+          gfx::PresentationFeedback::kVSync));
+
+  if (pending_frames_.front().remove_buffer_collection) {
+    image_pipe_->RemoveBufferCollection(
+        pending_frames_.front().buffer_collection_id);
+  }
+
+  pending_frames_.pop_front();
+  if (!pending_frames_.empty())
+    PresentNextFrame();
+}
+
+}  // namespace viz
diff --git a/components/viz/service/display_embedder/output_presenter_fuchsia.h b/components/viz/service/display_embedder/output_presenter_fuchsia.h
new file mode 100644
index 0000000..bfee93d
--- /dev/null
+++ b/components/viz/service/display_embedder/output_presenter_fuchsia.h
@@ -0,0 +1,115 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_FUCHSIA_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_FUCHSIA_H_
+
+#include <fuchsia/images/cpp/fidl.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/containers/circular_deque.h"
+#include "components/viz/service/display_embedder/output_presenter.h"
+#include "components/viz/service/viz_service_export.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
+#include "gpu/command_buffer/service/shared_image_factory.h"
+
+namespace ui {
+class PlatformWindowSurface;
+}  // namespace ui
+
+namespace viz {
+
+class VIZ_SERVICE_EXPORT OutputPresenterFuchsia : public OutputPresenter {
+ public:
+  static std::unique_ptr<OutputPresenterFuchsia> Create(
+      ui::PlatformWindowSurface* window_surface,
+      SkiaOutputSurfaceDependency* deps,
+      gpu::MemoryTracker* memory_tracker);
+
+  OutputPresenterFuchsia(fuchsia::images::ImagePipe2Ptr image_pipe,
+                         SkiaOutputSurfaceDependency* deps,
+                         gpu::MemoryTracker* memory_tracker);
+  ~OutputPresenterFuchsia() override;
+
+  // OutputPresenter implementation:
+  void InitializeCapabilities(OutputSurface::Capabilities* capabilities) final;
+  bool Reshape(const gfx::Size& size,
+               float device_scale_factor,
+               const gfx::ColorSpace& color_space,
+               gfx::BufferFormat format,
+               gfx::OverlayTransform transform) final;
+  std::vector<std::unique_ptr<Image>> AllocateImages(
+      gfx::ColorSpace color_space,
+      gfx::Size image_size,
+      size_t num_images) final;
+  void SwapBuffers(SwapCompletionCallback completion_callback,
+                   BufferPresentedCallback presentation_callback) final;
+  void PostSubBuffer(const gfx::Rect& rect,
+                     SwapCompletionCallback completion_callback,
+                     BufferPresentedCallback presentation_callback) final;
+  void CommitOverlayPlanes(SwapCompletionCallback completion_callback,
+                           BufferPresentedCallback presentation_callback) final;
+  void SchedulePrimaryPlane(
+      const OverlayProcessorInterface::OutputSurfaceOverlayPlane& plane,
+      Image* image,
+      bool is_submitted) final;
+  std::vector<OverlayData> ScheduleOverlays(
+      SkiaOutputSurface::OverlayList overlays) final;
+
+ private:
+  struct PendingFrame {
+    PendingFrame();
+    ~PendingFrame();
+
+    PendingFrame(PendingFrame&&);
+    PendingFrame& operator=(PendingFrame&&);
+
+    uint32_t buffer_collection_id;
+    uint32_t image_id;
+
+    std::vector<zx::event> acquire_fences;
+    std::vector<zx::event> release_fences;
+
+    SwapCompletionCallback completion_callback;
+    BufferPresentedCallback presentation_callback;
+
+    // Indicates that this is the last frame for this buffer collection and that
+    // the collection can be removed after the frame is presented.
+    bool remove_buffer_collection = false;
+  };
+
+  void PresentNextFrame();
+  void OnPresentComplete(fuchsia::images::PresentationInfo presentation_info);
+
+  fuchsia::sysmem::AllocatorPtr sysmem_allocator_;
+  fuchsia::images::ImagePipe2Ptr image_pipe_;
+  SkiaOutputSurfaceDependency* const dependency_;
+  gpu::SharedImageFactory shared_image_factory_;
+  gpu::SharedImageRepresentationFactory shared_image_representation_factory_;
+
+  gfx::Size frame_size_;
+  gfx::BufferFormat buffer_format_ = gfx::BufferFormat::RGBA_8888;
+
+  // Last buffer collection ID for the ImagePipe. Incremented every time buffers
+  // are reallocated.
+  uint32_t last_buffer_collection_id_ = 0;
+
+  // Counter to generate image IDs for the ImagePipe.
+  uint32_t last_image_id_ = 0;
+
+  std::unique_ptr<gpu::SysmemBufferCollection> buffer_collection_;
+
+  // The next frame to be submitted by SwapBuffers().
+  base::Optional<PendingFrame> next_frame_;
+
+  base::circular_deque<PendingFrame> pending_frames_;
+
+  bool present_is_pending_ = false;
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_FUCHSIA_H_
diff --git a/components/viz/service/display_embedder/output_presenter_gl.cc b/components/viz/service/display_embedder/output_presenter_gl.cc
index 5d62d552..289ab589 100644
--- a/components/viz/service/display_embedder/output_presenter_gl.cc
+++ b/components/viz/service/display_embedder/output_presenter_gl.cc
@@ -47,7 +47,7 @@
 
   void BeginPresent() final;
   void EndPresent() final;
-  int present_count() final;
+  int present_count() const final;
 
   gl::GLImage* GetGLImage(std::unique_ptr<gfx::GpuFence>* fence);
 
@@ -128,7 +128,7 @@
   scoped_gl_read_access_.reset();
 }
 
-int PresenterImageGL::present_count() {
+int PresenterImageGL::present_count() const {
   return present_count_;
 }
 
@@ -333,8 +333,7 @@
 
 void OutputPresenterGL::CommitOverlayPlanes(
     SwapCompletionCallback completion_callback,
-    BufferPresentedCallback presentation_callback,
-    std::vector<ui::LatencyInfo> latency_info) {
+    BufferPresentedCallback presentation_callback) {
   if (supports_async_swap_) {
     gl_surface_->CommitOverlayPlanesAsync(std::move(completion_callback),
                                           std::move(presentation_callback));
diff --git a/components/viz/service/display_embedder/output_presenter_gl.h b/components/viz/service/display_embedder/output_presenter_gl.h
index cf4c2b0..bfbf5199 100644
--- a/components/viz/service/display_embedder/output_presenter_gl.h
+++ b/components/viz/service/display_embedder/output_presenter_gl.h
@@ -5,6 +5,9 @@
 #ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_GL_H_
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_PRESENTER_GL_H_
 
+#include <memory>
+#include <vector>
+
 #include "components/viz/service/display_embedder/output_presenter.h"
 #include "components/viz/service/viz_service_export.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
@@ -49,8 +52,7 @@
                      SwapCompletionCallback completion_callback,
                      BufferPresentedCallback presentation_callback) final;
   void CommitOverlayPlanes(SwapCompletionCallback completion_callback,
-                           BufferPresentedCallback presentation_callback,
-                           std::vector<ui::LatencyInfo> latency_info) final;
+                           BufferPresentedCallback presentation_callback) final;
   void SchedulePrimaryPlane(
       const OverlayProcessorInterface::OutputSurfaceOverlayPlane& plane,
       Image* image,
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
index 49d8bc5e4..bb57dc9d 100644
--- a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
+++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
@@ -211,7 +211,7 @@
           submitted_image_ ? submitted_image_->GetWeakPtr() : nullptr,
           std::move(committed_overlays_))));
   presenter_->CommitOverlayPlanes(swap_completion_callbacks_.back()->callback(),
-                                  std::move(feedback), std::move(latency_info));
+                                  std::move(feedback));
 
   committed_overlays_.clear();
   std::swap(committed_overlays_, pending_overlays_);
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index f39d858..d4f1e78 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -93,6 +93,10 @@
 #include "ui/base/ui_base_features.h"
 #endif
 
+#if defined(OS_FUCHSIA)
+#include "components/viz/service/display_embedder/output_presenter_fuchsia.h"
+#endif
+
 namespace viz {
 
 namespace {
@@ -1021,6 +1025,7 @@
   }
   DCHECK(output_device_);
 
+  ResetStateOfImages();
   gr_context()->submit();
   promise_image_access_helper_.EndAccess();
   scoped_output_device_paint_.reset();
@@ -1066,6 +1071,7 @@
     base::OnceCallback<bool()> deferred_framebuffer_draw_closure) {
   if (deferred_framebuffer_draw_closure)
     std::move(deferred_framebuffer_draw_closure).Run();
+  ResetStateOfImages();
   gr_context()->submit();
   promise_image_access_helper_.EndAccess();
   // Perform cleanup that would have otherwise happened in SwapBuffers().
@@ -1409,14 +1415,29 @@
       context->BeginAccessIfNecessary(
           context_state_.get(), shared_image_representation_factory_.get(),
           dependency_->GetMailboxManager(), begin_semaphores, end_semaphores);
+      if (context->end_access_state())
+        image_contexts_with_end_access_state_.emplace(context);
     }
   }
 }
 
+void SkiaOutputSurfaceImplOnGpu::ResetStateOfImages() {
+  for (auto* context : image_contexts_with_end_access_state_) {
+    DCHECK(context->end_access_state());
+    if (!gr_context()->setBackendTextureState(
+            context->promise_image_texture()->backendTexture(),
+            *context->end_access_state())) {
+      DLOG(ERROR) << "setBackendTextureState() failed.";
+    }
+  }
+  image_contexts_with_end_access_state_.clear();
+}
+
 void SkiaOutputSurfaceImplOnGpu::EndAccessImages(
     const base::flat_set<ImageContextImpl*>& image_contexts) {
   TRACE_EVENT0("viz", "SkiaOutputSurfaceImplOnGpu::EndAccessImages");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(image_contexts_with_end_access_state_.empty());
   for (auto* context : image_contexts)
     context->EndAccessIfNecessary();
 }
@@ -1609,11 +1630,18 @@
     }
 #endif
     if (!output_device_) {
+#if defined(OS_FUCHSIA)
+      auto output_presenter = OutputPresenterFuchsia::Create(
+          window_surface_.get(), dependency_, memory_tracker_.get());
+#else
       auto output_presenter =
           OutputPresenterGL::Create(dependency_, memory_tracker_.get());
       if (output_presenter) {
         // TODO(https://crbug.com/1012401): don't depend on GL.
         gl_surface_ = output_presenter->gl_surface();
+      }
+#endif
+      if (output_presenter) {
         output_device_ = std::make_unique<SkiaOutputDeviceBufferQueue>(
             std::move(output_presenter), dependency_, memory_tracker_.get(),
             GetDidSwapBuffersCompleteCallback());
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index 831b318..a2d41831 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -159,6 +159,7 @@
   void BeginAccessImages(const std::vector<ImageContextImpl*>& image_contexts,
                          std::vector<GrBackendSemaphore>* begin_semaphores,
                          std::vector<GrBackendSemaphore>* end_semaphores);
+  void ResetStateOfImages();
   void EndAccessImages(const base::flat_set<ImageContextImpl*>& image_contexts);
 
   sk_sp<GrContextThreadSafeProxy> GetGrContextThreadSafeProxy();
@@ -328,6 +329,7 @@
     DISALLOW_COPY_AND_ASSIGN(PromiseImageAccessHelper);
   };
   PromiseImageAccessHelper promise_image_access_helper_{this};
+  base::flat_set<ImageContextImpl*> image_contexts_with_end_access_state_;
 
   std::unique_ptr<SkiaOutputDevice> output_device_;
   base::Optional<SkiaOutputDevice::ScopedPaint> scoped_output_device_paint_;
diff --git a/components/web_package/BUILD.gn b/components/web_package/BUILD.gn
new file mode 100644
index 0000000..670a64e
--- /dev/null
+++ b/components/web_package/BUILD.gn
@@ -0,0 +1,61 @@
+# 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.
+
+import("//testing/libfuzzer/fuzzer_test.gni")
+
+static_library("web_package") {
+  sources = [
+    "web_bundle_parser.cc",
+    "web_bundle_parser.h",
+    "web_bundle_parser_factory.cc",
+    "web_bundle_parser_factory.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/cbor",
+    "//mojo/public/cpp/bindings",
+    "//net",
+  ]
+
+  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_parser_factory_unittest.cc",
+    "web_bundle_parser_unittest.cc",
+  ]
+  deps = [
+    ":test_support",
+    ":web_package",
+    "//base/test:test_support",
+    "//components/cbor",
+    "//testing/gtest",
+  ]
+}
+
+fuzzer_test("web_bundle_parser_fuzzer") {
+  sources = [ "web_bundle_parser_fuzzer.cc" ]
+  deps = [
+    ":web_package",
+    "//base",
+    "//mojo/core/embedder",
+  ]
+  seed_corpus = "//components/test/data/web_package"
+}
diff --git a/components/web_package/DEPS b/components/web_package/DEPS
new file mode 100644
index 0000000..599f061f
--- /dev/null
+++ b/components/web_package/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+  "+components/cbor",
+  "+mojo/public/cpp",
+  "+net",
+]
+
+specific_include_rules = {
+  "web_bundle_parser_fuzzer\.cc": [
+    "+mojo/core/embedder/embedder.h",
+  ],
+}
diff --git a/components/web_package/OWNERS b/components/web_package/OWNERS
new file mode 100644
index 0000000..25537e1
--- /dev/null
+++ b/components/web_package/OWNERS
@@ -0,0 +1,6 @@
+horo@chromium.org
+kinuko@chromium.org
+ksakamoto@chromium.org
+
+# COMPONENT: Blink>Loader>WebPackaging
+# TEAM: webpackage-dev@chromium.org
diff --git a/components/web_package/README.md b/components/web_package/README.md
new file mode 100644
index 0000000..37c2061
--- /dev/null
+++ b/components/web_package/README.md
@@ -0,0 +1,3 @@
+This directory contains shared code for
+[Web Packaging](https://github.com/WICG/webpackage), used from the utility
+process (by the data_decoder service) and the renderer process (blink).
diff --git a/components/web_package/mojom/BUILD.gn b/components/web_package/mojom/BUILD.gn
new file mode 100644
index 0000000..3b4fbd06
--- /dev/null
+++ b/components/web_package/mojom/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright (c) 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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+  sources = [ "web_bundle_parser.mojom" ]
+
+  public_deps = [
+    "//mojo/public/mojom/base",
+    "//url/mojom:url_mojom_gurl",
+  ]
+
+  if (!is_ios) {
+    export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
+    export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
+    export_header_blink = "third_party/blink/public/platform/web_common.h"
+  }
+}
diff --git a/components/web_package/mojom/OWNERS b/components/web_package/mojom/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/components/web_package/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/services/data_decoder/public/mojom/web_bundle_parser.mojom b/components/web_package/mojom/web_bundle_parser.mojom
similarity index 98%
rename from services/data_decoder/public/mojom/web_bundle_parser.mojom
rename to components/web_package/mojom/web_bundle_parser.mojom
index ca4c1be..ce98234 100644
--- a/services/data_decoder/public/mojom/web_bundle_parser.mojom
+++ b/components/web_package/mojom/web_bundle_parser.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-module data_decoder.mojom;
+module web_package.mojom;
 
 import "mojo/public/mojom/base/file.mojom";
 import "url/mojom/url.mojom";
diff --git a/services/data_decoder/public/cpp/test_support/web_bundle_builder.cc b/components/web_package/test_support/web_bundle_builder.cc
similarity index 97%
rename from services/data_decoder/public/cpp/test_support/web_bundle_builder.cc
rename to components/web_package/test_support/web_bundle_builder.cc
index 8155e97..4913f24 100644
--- a/services/data_decoder/public/cpp/test_support/web_bundle_builder.cc
+++ b/components/web_package/test_support/web_bundle_builder.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/data_decoder/public/cpp/test_support/web_bundle_builder.h"
+#include "components/web_package/test_support/web_bundle_builder.h"
 
-namespace data_decoder {
+namespace web_package {
 namespace test {
 
 namespace {
@@ -146,4 +146,4 @@
 }
 
 }  // namespace test
-}  // namespace data_decoder
+}  // namespace web_package
diff --git a/services/data_decoder/public/cpp/test_support/web_bundle_builder.h b/components/web_package/test_support/web_bundle_builder.h
similarity index 89%
rename from services/data_decoder/public/cpp/test_support/web_bundle_builder.h
rename to components/web_package/test_support/web_bundle_builder.h
index 71267ac..d23e547 100644
--- a/services/data_decoder/public/cpp/test_support/web_bundle_builder.h
+++ b/components/web_package/test_support/web_bundle_builder.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_DATA_DECODER_PUBLIC_CPP_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_
-#define SERVICES_DATA_DECODER_PUBLIC_CPP_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_
+#ifndef COMPONENTS_WEB_PACKAGE_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_
+#define COMPONENTS_WEB_PACKAGE_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_
 
 #include <string>
 #include <utility>
@@ -12,7 +12,7 @@
 #include "base/strings/string_piece.h"
 #include "components/cbor/writer.h"
 
-namespace data_decoder {
+namespace web_package {
 namespace test {
 
 // This class can be used to create a Web Bundle binary in tests.
@@ -75,6 +75,6 @@
 };
 
 }  // namespace test
-}  // namespace data_decoder
+}  // namespace web_package
 
-#endif  // SERVICES_DATA_DECODER_PUBLIC_CPP_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_
+#endif  // COMPONENTS_WEB_PACKAGE_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_
diff --git a/services/data_decoder/web_bundle_parser.cc b/components/web_package/web_bundle_parser.cc
similarity index 99%
rename from services/data_decoder/web_bundle_parser.cc
rename to components/web_package/web_bundle_parser.cc
index e354823..4fcda57 100644
--- a/services/data_decoder/web_bundle_parser.cc
+++ b/components/web_package/web_bundle_parser.cc
@@ -2,7 +2,7 @@
 // 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_parser.h"
+#include "components/web_package/web_bundle_parser.h"
 
 #include <algorithm>
 
@@ -18,7 +18,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/http/http_util.h"
 
-namespace data_decoder {
+namespace web_package {
 
 namespace {
 
@@ -1346,4 +1346,4 @@
   parser->Start();
 }
 
-}  // namespace data_decoder
+}  // namespace web_package
diff --git a/services/data_decoder/web_bundle_parser.h b/components/web_package/web_bundle_parser.h
similarity index 86%
rename from services/data_decoder/web_bundle_parser.h
rename to components/web_package/web_bundle_parser.h
index 81fe780..5cfe22d6 100644
--- a/services/data_decoder/web_bundle_parser.h
+++ b/components/web_package/web_bundle_parser.h
@@ -2,19 +2,19 @@
 // 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_PARSER_H_
-#define SERVICES_DATA_DECODER_WEB_BUNDLE_PARSER_H_
+#ifndef COMPONENTS_WEB_PACKAGE_WEB_BUNDLE_PARSER_H_
+#define COMPONENTS_WEB_PACKAGE_WEB_BUNDLE_PARSER_H_
 
 #include <memory>
 
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 
-namespace data_decoder {
+namespace web_package {
 
 class WebBundleParser : public mojom::WebBundleParser {
  public:
@@ -66,6 +66,6 @@
   DISALLOW_COPY_AND_ASSIGN(WebBundleParser);
 };
 
-}  // namespace data_decoder
+}  // namespace web_package
 
-#endif  // SERVICES_DATA_DECODER_WEB_BUNDLE_PARSER_H_
+#endif  // COMPONENTS_WEB_PACKAGE_WEB_BUNDLE_PARSER_H_
diff --git a/services/data_decoder/web_bundle_parser_factory.cc b/components/web_package/web_bundle_parser_factory.cc
similarity index 93%
rename from services/data_decoder/web_bundle_parser_factory.cc
rename to components/web_package/web_bundle_parser_factory.cc
index 1ead65e..8041050 100644
--- a/services/data_decoder/web_bundle_parser_factory.cc
+++ b/components/web_package/web_bundle_parser_factory.cc
@@ -2,14 +2,14 @@
 // 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_parser_factory.h"
+#include "components/web_package/web_bundle_parser_factory.h"
 
 #include "base/bind_helpers.h"
+#include "components/web_package/web_bundle_parser.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/http/http_util.h"
-#include "services/data_decoder/web_bundle_parser.h"
 
-namespace data_decoder {
+namespace web_package {
 
 namespace {
 
@@ -76,4 +76,4 @@
   parser.release();
 }
 
-}  // namespace data_decoder
+}  // namespace web_package
diff --git a/services/data_decoder/web_bundle_parser_factory.h b/components/web_package/web_bundle_parser_factory.h
similarity index 77%
rename from services/data_decoder/web_bundle_parser_factory.h
rename to components/web_package/web_bundle_parser_factory.h
index 5430b5b9..3c4d806 100644
--- a/services/data_decoder/web_bundle_parser_factory.h
+++ b/components/web_package/web_bundle_parser_factory.h
@@ -2,18 +2,18 @@
 // 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_PARSER_FACTORY_H_
-#define SERVICES_DATA_DECODER_WEB_BUNDLE_PARSER_FACTORY_H_
+#ifndef COMPONENTS_WEB_PACKAGE_WEB_BUNDLE_PARSER_FACTORY_H_
+#define COMPONENTS_WEB_PACKAGE_WEB_BUNDLE_PARSER_FACTORY_H_
 
 #include <memory>
 
 #include "base/files/file.h"
 #include "base/macros.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 
-namespace data_decoder {
+namespace web_package {
 
 class WebBundleParserFactory : public mojom::WebBundleParserFactory {
  public:
@@ -35,6 +35,6 @@
   DISALLOW_COPY_AND_ASSIGN(WebBundleParserFactory);
 };
 
-}  // namespace data_decoder
+}  // namespace web_package
 
-#endif  // SERVICES_DATA_DECODER_WEB_BUNDLE_PARSER_FACTORY_H_
+#endif  // COMPONENTS_WEB_PACKAGE_WEB_BUNDLE_PARSER_FACTORY_H_
diff --git a/services/data_decoder/web_bundle_parser_factory_unittest.cc b/components/web_package/web_bundle_parser_factory_unittest.cc
similarity index 96%
rename from services/data_decoder/web_bundle_parser_factory_unittest.cc
rename to components/web_package/web_bundle_parser_factory_unittest.cc
index c6cecfa..d16db82 100644
--- a/services/data_decoder/web_bundle_parser_factory_unittest.cc
+++ b/components/web_package/web_bundle_parser_factory_unittest.cc
@@ -2,7 +2,7 @@
 // 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_parser_factory.h"
+#include "components/web_package/web_bundle_parser_factory.h"
 
 #include "base/files/file.h"
 #include "base/files/file_path.h"
@@ -16,7 +16,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace data_decoder {
+namespace web_package {
 
 namespace {
 
@@ -24,7 +24,7 @@
   base::FilePath test_path;
   base::PathService::Get(base::DIR_SOURCE_ROOT, &test_path);
   test_path = test_path.Append(
-      base::FilePath(FILE_PATH_LITERAL("services/test/data/web_bundle")));
+      base::FilePath(FILE_PATH_LITERAL("components/test/data/web_package")));
   return test_path.Append(path);
 }
 
@@ -187,4 +187,4 @@
   EXPECT_TRUE(responses["https://test.example.org/script.js"]);
 }
 
-}  // namespace data_decoder
+}  // namespace web_package
diff --git a/services/data_decoder/web_bundle_parser_fuzzer.cc b/components/web_package/web_bundle_parser_fuzzer.cc
similarity index 76%
rename from services/data_decoder/web_bundle_parser_fuzzer.cc
rename to components/web_package/web_bundle_parser_fuzzer.cc
index df56a87..116ecc66 100644
--- a/services/data_decoder/web_bundle_parser_fuzzer.cc
+++ b/components/web_package/web_bundle_parser_fuzzer.cc
@@ -12,14 +12,14 @@
 #include "base/i18n/icu_util.h"
 #include "base/run_loop.h"
 #include "base/task/single_thread_task_executor.h"
+#include "components/web_package/web_bundle_parser.h"
+#include "components/web_package/web_bundle_parser_factory.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
-#include "services/data_decoder/web_bundle_parser.h"
-#include "services/data_decoder/web_bundle_parser_factory.h"
 
 namespace {
 
-class DataSource : public data_decoder::mojom::BundleDataSource {
+class DataSource : public web_package::mojom::BundleDataSource {
  public:
   DataSource(const uint8_t* data, size_t size) : data_(data), size_(size) {}
 
@@ -34,14 +34,14 @@
   }
 
   void AddReceiver(
-      mojo::PendingReceiver<data_decoder::mojom::BundleDataSource> receiver) {
+      mojo::PendingReceiver<web_package::mojom::BundleDataSource> receiver) {
     receivers_.Add(this, std::move(receiver));
   }
 
  private:
   const uint8_t* data_;
   size_t size_;
-  mojo::ReceiverSet<data_decoder::mojom::BundleDataSource> receivers_;
+  mojo::ReceiverSet<web_package::mojom::BundleDataSource> receivers_;
 };
 
 class WebBundleParserFuzzer {
@@ -50,13 +50,13 @@
       : data_source_(data, size) {}
 
   void FuzzBundle(base::RunLoop* run_loop) {
-    mojo::PendingRemote<data_decoder::mojom::BundleDataSource>
+    mojo::PendingRemote<web_package::mojom::BundleDataSource>
         data_source_remote;
     data_source_.AddReceiver(
         data_source_remote.InitWithNewPipeAndPassReceiver());
 
-    data_decoder::WebBundleParserFactory factory_impl;
-    data_decoder::mojom::WebBundleParserFactory& factory = factory_impl;
+    web_package::WebBundleParserFactory factory_impl;
+    web_package::mojom::WebBundleParserFactory& factory = factory_impl;
     factory.GetParserForDataSource(parser_.BindNewPipeAndPassReceiver(),
                                    std::move(data_source_remote));
 
@@ -65,8 +65,8 @@
         &WebBundleParserFuzzer::OnParseMetadata, base::Unretained(this)));
   }
 
-  void OnParseMetadata(data_decoder::mojom::BundleMetadataPtr metadata,
-                       data_decoder::mojom::BundleMetadataParseErrorPtr error) {
+  void OnParseMetadata(web_package::mojom::BundleMetadataPtr metadata,
+                       web_package::mojom::BundleMetadataParseErrorPtr error) {
     if (!metadata) {
       std::move(quit_loop_).Run();
       return;
@@ -91,16 +91,16 @@
   }
 
   void OnParseResponse(size_t index,
-                       data_decoder::mojom::BundleResponsePtr response,
-                       data_decoder::mojom::BundleResponseParseErrorPtr error) {
+                       web_package::mojom::BundleResponsePtr response,
+                       web_package::mojom::BundleResponseParseErrorPtr error) {
     ParseResponses(index + 1);
   }
 
  private:
-  mojo::Remote<data_decoder::mojom::WebBundleParser> parser_;
+  mojo::Remote<web_package::mojom::WebBundleParser> parser_;
   DataSource data_source_;
   base::OnceClosure quit_loop_;
-  std::vector<data_decoder::mojom::BundleResponseLocationPtr> locations_;
+  std::vector<web_package::mojom::BundleResponseLocationPtr> locations_;
 };
 
 struct Environment {
diff --git a/services/data_decoder/web_bundle_parser_unittest.cc b/components/web_package/web_bundle_parser_unittest.cc
similarity index 98%
rename from services/data_decoder/web_bundle_parser_unittest.cc
rename to components/web_package/web_bundle_parser_unittest.cc
index eb637ba..1bc60d3 100644
--- a/services/data_decoder/web_bundle_parser_unittest.cc
+++ b/components/web_package/web_bundle_parser_unittest.cc
@@ -2,7 +2,7 @@
 // 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_parser.h"
+#include "components/web_package/web_bundle_parser.h"
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -11,11 +11,11 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/task_environment.h"
 #include "components/cbor/writer.h"
+#include "components/web_package/test_support/web_bundle_builder.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
-#include "services/data_decoder/public/cpp/test_support/web_bundle_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace data_decoder {
+namespace web_package {
 
 namespace {
 
@@ -30,7 +30,7 @@
   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("services/test/data/web_bundle")));
+      base::FilePath(FILE_PATH_LITERAL("components/test/data/web_package")));
 
   std::string contents;
   EXPECT_TRUE(base::ReadFileToString(test_data_dir.Append(path), &contents));
@@ -78,7 +78,7 @@
   mojo::PendingRemote<mojom::WebBundleParser> parser_remote;
   WebBundleParser parser_impl(parser_remote.InitWithNewPipeAndPassReceiver(),
                               std::move(source_remote));
-  data_decoder::mojom::WebBundleParser& parser = parser_impl;
+  mojom::WebBundleParser& parser = parser_impl;
 
   base::RunLoop run_loop;
   ParseBundleResult result;
@@ -126,7 +126,7 @@
   mojo::PendingRemote<mojom::WebBundleParser> parser_remote;
   WebBundleParser parser_impl(parser_remote.InitWithNewPipeAndPassReceiver(),
                               std::move(source_remote));
-  data_decoder::mojom::WebBundleParser& parser = parser_impl;
+  mojom::WebBundleParser& parser = parser_impl;
 
   base::RunLoop run_loop;
   mojom::BundleResponsePtr result;
@@ -697,4 +697,4 @@
 // TODO(crbug.com/969596): Add a test case that loads a wbn file with variants,
 // once gen-bundle supports variants.
 
-}  // namespace data_decoder
+}  // namespace web_package
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 292f850..81c84f4 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -88,6 +88,7 @@
     "//components/viz/client",
     "//components/viz/common",
     "//components/viz/host",
+    "//components/web_package",
     "//content:content_resources",
     "//content:dev_ui_content_resources",
     "//content/app/resources",
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 761a111..b921101 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -37,6 +37,7 @@
   "+components/variations/variations_http_header_provider.h",
   "+components/variations/net",
   "+components/viz",
+  "+components/web_package",
   "+components/cbor",
   "+components/os_crypt/os_crypt_switches.h",
   # *********************************** NOTE ***********************************
diff --git a/content/browser/accessibility/accessibility_event_recorder_win.cc b/content/browser/accessibility/accessibility_event_recorder_win.cc
index 73c7d62..486a414 100644
--- a/content/browser/accessibility/accessibility_event_recorder_win.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_win.cc
@@ -41,7 +41,7 @@
 
 HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) {
   Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf());
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
   return SUCCEEDED(hr)
              ? service_provider->QueryService(IID_IAccessible2, accessible2)
              : hr;
@@ -50,7 +50,7 @@
 HRESULT QueryIAccessibleText(IAccessible* accessible,
                              IAccessibleText** accessible_text) {
   Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf());
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
   return SUCCEEDED(hr) ? service_provider->QueryService(IID_IAccessibleText,
                                                         accessible_text)
                        : hr;
@@ -199,8 +199,7 @@
                                                    DWORD event_time) {
   Microsoft::WRL::ComPtr<IAccessible> browser_accessible;
   HRESULT hr = AccessibleObjectFromWindowWrapper(
-      hwnd, obj_id, IID_IAccessible,
-      reinterpret_cast<void**>(browser_accessible.GetAddressOf()));
+      hwnd, obj_id, IID_PPV_ARGS(&browser_accessible));
   if (FAILED(hr)) {
     // Note: our event hook will pick up some superfluous events we
     // don't care about, so it's safe to just ignore these failures.
@@ -211,8 +210,7 @@
 
   base::win::ScopedVariant childid_variant(child_id);
   Microsoft::WRL::ComPtr<IDispatch> dispatch;
-  hr = browser_accessible->get_accChild(childid_variant,
-                                        dispatch.GetAddressOf());
+  hr = browser_accessible->get_accChild(childid_variant, &dispatch);
   if (hr != S_OK || !dispatch) {
     VLOG(1) << "Ignoring result " << hr << " and result " << dispatch.Get()
             << " from get_accChild";
@@ -220,7 +218,7 @@
   }
 
   Microsoft::WRL::ComPtr<IAccessible> iaccessible;
-  hr = dispatch.CopyTo(iaccessible.GetAddressOf());
+  hr = dispatch.As(&iaccessible);
   if (FAILED(hr)) {
     VLOG(1) << "Ignoring result " << hr << " from QueryInterface";
     return;
@@ -257,13 +255,13 @@
       return;
 
     Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-    hr = iaccessible->QueryInterface(service_provider.GetAddressOf());
+    hr = iaccessible->QueryInterface(IID_PPV_ARGS(&service_provider));
     if (FAILED(hr))
       return;
 
     Microsoft::WRL::ComPtr<IAccessible> content_document;
     hr = service_provider->QueryService(GUID_IAccessibleContentDocument,
-                                        content_document.GetAddressOf());
+                                        IID_PPV_ARGS(&content_document));
     if (FAILED(hr))
       return;
   }
@@ -291,7 +289,7 @@
 
   AccessibleStates ia2_state = 0;
   Microsoft::WRL::ComPtr<IAccessible2> iaccessible2;
-  hr = QueryIAccessible2(iaccessible.Get(), iaccessible2.GetAddressOf());
+  hr = QueryIAccessible2(iaccessible.Get(), &iaccessible2);
   bool has_ia2 = SUCCEEDED(hr) && iaccessible2;
 
   base::string16 html_tag;
@@ -367,7 +365,7 @@
   // For TEXT_REMOVED and TEXT_INSERTED events, query the text that was
   // inserted or removed and include that in the log.
   Microsoft::WRL::ComPtr<IAccessibleText> accessible_text;
-  hr = QueryIAccessibleText(iaccessible.Get(), accessible_text.GetAddressOf());
+  hr = QueryIAccessibleText(iaccessible.Get(), &accessible_text);
   if (SUCCEEDED(hr)) {
     if (event == IA2_EVENT_TEXT_REMOVED) {
       IA2TextSegment old_text;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc
index 6b8e3a3..cf876bc 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -184,7 +184,7 @@
   // IA2 Spec dictates that IServiceProvider should be used instead of
   // QueryInterface when retrieving IAccessible2.
   Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf());
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
   if (FAILED(hr))
     return hr;
   return service_provider->QueryService(IID_ISimpleDOMNode, simple_dom_node);
@@ -195,7 +195,7 @@
   // IA2 Spec dictates that IServiceProvider should be used instead of
   // QueryInterface when retrieving IAccessible2.
   Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf());
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
   if (FAILED(hr))
     return hr;
   return service_provider->QueryService(IID_IAccessible2, accessible2);
@@ -206,7 +206,7 @@
   // IA2 Spec dictates that IServiceProvider should be used instead of
   // QueryInterface when retrieving alternate interfaces.
   Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf());
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
   if (FAILED(hr))
     return hr;
 
@@ -220,7 +220,7 @@
   // IA2 Spec dictates that IServiceProvider should be used instead of
   // QueryInterface when retrieving alternate interfaces.
   Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf());
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
   if (FAILED(hr))
     return hr;
   return service_provider->QueryService(IID_IAccessibleHypertext,
@@ -232,7 +232,7 @@
   // IA2 Spec dictates that IServiceProvider should be used instead of
   // QueryInterface when retrieving alternate interfaces.
   Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf());
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
   if (FAILED(hr))
     return hr;
   return service_provider->QueryService(IID_IAccessibleTable, accessibleTable);
@@ -244,7 +244,7 @@
   // IA2 Spec dictates that IServiceProvider should be used instead of
   // QueryInterface when retrieving alternate interfaces.
   Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf());
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
   if (FAILED(hr))
     return hr;
   return service_provider->QueryService(IID_IAccessibleTableCell,
@@ -256,7 +256,7 @@
   // IA2 Spec dictates that IServiceProvider should be used instead of
   // QueryInterface when retrieving alternate interfaces.
   Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf());
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
   if (FAILED(hr))
     return hr;
   return service_provider->QueryService(IID_IAccessibleText, accessibleText);
@@ -267,7 +267,7 @@
   // IA2 Spec dictates that IServiceProvider should be used instead of
   // QueryInterface when retrieving alternate interfaces.
   Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(service_provider.GetAddressOf());
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
   if (FAILED(hr))
     return hr;
   return service_provider->QueryService(IID_IAccessibleValue, accessibleValue);
@@ -372,7 +372,7 @@
     if (child_variant.type() == VT_DISPATCH) {
       dispatch = V_DISPATCH(child_variant.ptr());
     } else if (child_variant.type() == VT_I4) {
-      hr = node->get_accChild(child_variant, dispatch.GetAddressOf());
+      hr = node->get_accChild(child_variant, &dispatch);
       if (FAILED(hr)) {
         child_dict->SetString("error",
                               base::ASCIIToUTF16("[Error retrieving child]"));
@@ -496,12 +496,11 @@
   temp_bstr.Reset();
 
   Microsoft::WRL::ComPtr<IDispatch> parent_dispatch;
-  if (SUCCEEDED(node->get_accParent(parent_dispatch.GetAddressOf()))) {
+  if (SUCCEEDED(node->get_accParent(&parent_dispatch))) {
     Microsoft::WRL::ComPtr<IAccessible> parent_accessible;
     if (!parent_dispatch) {
       dict->SetString("parent", "[null]");
-    } else if (SUCCEEDED(
-                   parent_dispatch.CopyTo(parent_accessible.GetAddressOf()))) {
+    } else if (SUCCEEDED(parent_dispatch.As(&parent_accessible))) {
       base::win::ScopedVariant parent_ia_role_variant;
       if (SUCCEEDED(parent_accessible->get_accRole(
               variant_self, parent_ia_role_variant.Receive())))
@@ -587,7 +586,7 @@
     base::DictionaryValue* dict) {
   Microsoft::WRL::ComPtr<ISimpleDOMNode> simple_dom_node;
 
-  if (S_OK != QuerySimpleDOMNode(node.Get(), simple_dom_node.GetAddressOf()))
+  if (S_OK != QuerySimpleDOMNode(node.Get(), &simple_dom_node))
     return;  // No IA2Value, we are finished with this node.
 
   base::win::ScopedBstr temp_bstr;
@@ -603,7 +602,7 @@
     const Microsoft::WRL::ComPtr<IAccessible> node,
     base::DictionaryValue* dict) {
   Microsoft::WRL::ComPtr<IAccessible2> ia2;
-  if (S_OK != QueryIAccessible2(node.Get(), ia2.GetAddressOf()))
+  if (S_OK != QueryIAccessible2(node.Get(), &ia2))
     return false;  // No IA2, we are finished with this node.
 
   LONG ia2_role = 0;
@@ -671,7 +670,7 @@
     const Microsoft::WRL::ComPtr<IAccessible> node,
     base::DictionaryValue* dict) {
   Microsoft::WRL::ComPtr<IAccessibleAction> ia2action;
-  if (S_OK != QueryIAccessibleAction(node.Get(), ia2action.GetAddressOf()))
+  if (S_OK != QueryIAccessibleAction(node.Get(), &ia2action))
     return;  // No IA2Value, we are finished with this node.
 
   base::win::ScopedBstr temp_bstr;
@@ -688,7 +687,7 @@
     Microsoft::WRL::ComPtr<IAccessible> node,
     base::DictionaryValue* dict) {
   Microsoft::WRL::ComPtr<IAccessibleHypertext> ia2hyper;
-  if (S_OK != QueryIAccessibleHypertext(node.Get(), ia2hyper.GetAddressOf()))
+  if (S_OK != QueryIAccessibleHypertext(node.Get(), &ia2hyper))
     return;  // No IA2, we are finished with this node
 
   base::win::ScopedBstr text_bstr;
@@ -725,11 +724,10 @@
       if (hr == S_OK) {
         DCHECK_GE(index_of_embed, 0);
         Microsoft::WRL::ComPtr<IAccessibleHyperlink> embedded_object;
-        hr = ia2hyper->get_hyperlink(index_of_embed,
-                                     embedded_object.GetAddressOf());
+        hr = ia2hyper->get_hyperlink(index_of_embed, &embedded_object);
         DCHECK(SUCCEEDED(hr));
         Microsoft::WRL::ComPtr<IAccessible2> ax_embed;
-        hr = embedded_object.CopyTo(ax_embed.GetAddressOf());
+        hr = embedded_object.As(&ax_embed);
         DCHECK(SUCCEEDED(hr));
         hr = ax_embed->get_indexInParent(&child_index);
         DCHECK(SUCCEEDED(hr));
@@ -757,7 +755,7 @@
     const Microsoft::WRL::ComPtr<IAccessible> node,
     base::DictionaryValue* dict) {
   Microsoft::WRL::ComPtr<IAccessibleTable> ia2table;
-  if (S_OK != QueryIAccessibleTable(node.Get(), ia2table.GetAddressOf()))
+  if (S_OK != QueryIAccessibleTable(node.Get(), &ia2table))
     return;  // No IA2Text, we are finished with this node.
 
   LONG table_rows;
@@ -780,7 +778,7 @@
     related_accessibles_string += index > 0 ? L"," : L"<";
     Microsoft::WRL::ComPtr<IUnknown> unknown = accessibles[index];
     Microsoft::WRL::ComPtr<IAccessible> accessible;
-    if (SUCCEEDED(unknown.CopyTo(accessible.GetAddressOf()))) {
+    if (SUCCEEDED(unknown.As(&accessible))) {
       base::win::ScopedBstr temp_bstr;
       if (S_OK == accessible->get_accName(variant_self, temp_bstr.Receive()))
         related_accessibles_string += temp_bstr.Get();
@@ -796,7 +794,7 @@
     const Microsoft::WRL::ComPtr<IAccessible> node,
     base::DictionaryValue* dict) {
   Microsoft::WRL::ComPtr<IAccessibleTableCell> ia2cell;
-  if (S_OK != QueryIAccessibleTableCell(node.Get(), ia2cell.GetAddressOf()))
+  if (S_OK != QueryIAccessibleTableCell(node.Get(), &ia2cell))
     return;  // No IA2Text, we are finished with this node.
 
   LONG n_row_header_cells;
@@ -826,7 +824,7 @@
     const Microsoft::WRL::ComPtr<IAccessible> node,
     base::DictionaryValue* dict) {
   Microsoft::WRL::ComPtr<IAccessibleText> ia2text;
-  if (S_OK != QueryIAccessibleText(node.Get(), ia2text.GetAddressOf()))
+  if (S_OK != QueryIAccessibleText(node.Get(), &ia2text))
     return;  // No IA2Text, we are finished with this node.
 
   LONG n_characters;
@@ -892,7 +890,7 @@
     const Microsoft::WRL::ComPtr<IAccessible> node,
     base::DictionaryValue* dict) {
   Microsoft::WRL::ComPtr<IAccessibleValue> ia2value;
-  if (S_OK != QueryIAccessibleValue(node.Get(), ia2value.GetAddressOf()))
+  if (S_OK != QueryIAccessibleValue(node.Get(), &ia2value))
     return;  // No IA2Value, we are finished with this node.
 
   base::win::ScopedVariant current_value;
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index 1c33a05f..923f732 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -2697,19 +2697,16 @@
   ranges = nullptr;
   n_ranges = 0;
 
-  // For native plain text fields, e.g. input and textarea, anchor and active
-  // offsets are always swapped to be in ascending order by the renderer. The
-  // selection's directionality is lost.
   hr = ax_input->get_selectionRanges(&ranges, &n_ranges);
   EXPECT_EQ(S_OK, hr);
   EXPECT_EQ(1, n_ranges);
   ASSERT_NE(nullptr, ranges);
   ASSERT_NE(nullptr, ranges[0].anchor);
   EXPECT_EQ(ax_input.Get(), ranges[0].anchor);
-  EXPECT_EQ(1, ranges[0].anchorOffset);
+  EXPECT_EQ(contents_string_length, ranges[0].anchorOffset);
   ASSERT_NE(nullptr, ranges[0].active);
   EXPECT_EQ(ax_input.Get(), ranges[0].active);
-  EXPECT_EQ(contents_string_length, ranges[0].activeOffset);
+  EXPECT_EQ(1, ranges[0].activeOffset);
 
   ranges[0].anchor->Release();
   ranges[0].active->Release();
@@ -2816,19 +2813,16 @@
   ranges = nullptr;
   n_ranges = 0;
 
-  // For native plain text fields, e.g. input and textarea, anchor and active
-  // offsets are always swapped to be in ascending order by the renderer. The
-  // selection's directionality is lost.
   hr = ax_textarea->get_selectionRanges(&ranges, &n_ranges);
   EXPECT_EQ(S_OK, hr);
   EXPECT_EQ(1, n_ranges);
   ASSERT_NE(nullptr, ranges);
   ASSERT_NE(nullptr, ranges[0].anchor);
   EXPECT_EQ(ax_textarea.Get(), ranges[0].anchor);
-  EXPECT_EQ(0, ranges[0].anchorOffset);
+  EXPECT_EQ(contents_string_length - 1, ranges[0].anchorOffset);
   ASSERT_NE(nullptr, ranges[0].active);
   EXPECT_EQ(ax_textarea.Get(), ranges[0].active);
-  EXPECT_EQ(contents_string_length - 1, ranges[0].activeOffset);
+  EXPECT_EQ(0, ranges[0].activeOffset);
 
   ranges[0].anchor->Release();
   ranges[0].active->Release();
diff --git a/content/browser/accessibility/browser_accessibility_win_unittest.cc b/content/browser/accessibility/browser_accessibility_win_unittest.cc
index ca9831ee..31e97934 100644
--- a/content/browser/accessibility/browser_accessibility_win_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_win_unittest.cc
@@ -214,11 +214,11 @@
   Microsoft::WRL::ComPtr<IDispatch> text_dispatch;
   HRESULT hr = ToBrowserAccessibilityWin(manager->GetRoot())
                    ->GetCOM()
-                   ->get_accChild(one, text_dispatch.GetAddressOf());
+                   ->get_accChild(one, &text_dispatch);
   ASSERT_EQ(S_OK, hr);
 
   Microsoft::WRL::ComPtr<IAccessible> text_accessible;
-  hr = text_dispatch.CopyTo(text_accessible.GetAddressOf());
+  hr = text_dispatch.As(&text_accessible);
   ASSERT_EQ(S_OK, hr);
 
   base::win::ScopedVariant childid_self(CHILDID_SELF);
@@ -245,10 +245,10 @@
   // as its value.
   hr = ToBrowserAccessibilityWin(manager->GetRoot())
            ->GetCOM()
-           ->get_accChild(one, text_dispatch.GetAddressOf());
+           ->get_accChild(one, &text_dispatch);
   ASSERT_EQ(S_OK, hr);
 
-  hr = text_dispatch.CopyTo(text_accessible.GetAddressOf());
+  hr = text_dispatch.As(&text_accessible);
   ASSERT_EQ(S_OK, hr);
 
   hr = text_accessible->get_accName(childid_self, name.Receive());
@@ -641,13 +641,11 @@
   EXPECT_EQ(0, hyperlink_count);
 
   Microsoft::WRL::ComPtr<IAccessibleHyperlink> hyperlink;
+  EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, &hyperlink));
+  EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(0, &hyperlink));
+  EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(text_name_len, &hyperlink));
   EXPECT_EQ(E_INVALIDARG,
-            root_obj->get_hyperlink(-1, hyperlink.GetAddressOf()));
-  EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(0, hyperlink.GetAddressOf()));
-  EXPECT_EQ(E_INVALIDARG,
-            root_obj->get_hyperlink(text_name_len, hyperlink.GetAddressOf()));
-  EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(text_name_len + 1,
-                                                  hyperlink.GetAddressOf()));
+            root_obj->get_hyperlink(text_name_len + 1, &hyperlink));
 
   LONG hyperlink_index;
   EXPECT_EQ(S_FALSE, root_obj->get_hyperlinkIndex(0, &hyperlink_index));
@@ -762,14 +760,13 @@
 
   Microsoft::WRL::ComPtr<IAccessibleHyperlink> hyperlink;
   Microsoft::WRL::ComPtr<IAccessibleText> hypertext;
-  EXPECT_EQ(E_INVALIDARG,
-            root_obj->get_hyperlink(-1, hyperlink.GetAddressOf()));
-  EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(4, hyperlink.GetAddressOf()));
+  EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, &hyperlink));
+  EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(4, &hyperlink));
 
   // Get the text of the combo box.
   // It should be its value.
-  EXPECT_EQ(S_OK, root_obj->get_hyperlink(0, hyperlink.GetAddressOf()));
-  EXPECT_EQ(S_OK, hyperlink.CopyTo(hypertext.GetAddressOf()));
+  EXPECT_EQ(S_OK, root_obj->get_hyperlink(0, &hyperlink));
+  EXPECT_EQ(S_OK, hyperlink.As(&hypertext));
   EXPECT_EQ(S_OK,
             hypertext->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive()));
   EXPECT_STREQ(combo_box_value.c_str(), text.Get());
@@ -779,8 +776,8 @@
 
   // Get the text of the check box.
   // It should be its name.
-  EXPECT_EQ(S_OK, root_obj->get_hyperlink(1, hyperlink.GetAddressOf()));
-  EXPECT_EQ(S_OK, hyperlink.CopyTo(hypertext.GetAddressOf()));
+  EXPECT_EQ(S_OK, root_obj->get_hyperlink(1, &hyperlink));
+  EXPECT_EQ(S_OK, hyperlink.As(&hypertext));
   EXPECT_EQ(S_OK,
             hypertext->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive()));
   EXPECT_STREQ(check_box_name.c_str(), text.Get());
@@ -789,8 +786,8 @@
   hypertext.Reset();
 
   // Get the text of the button.
-  EXPECT_EQ(S_OK, root_obj->get_hyperlink(2, hyperlink.GetAddressOf()));
-  EXPECT_EQ(S_OK, hyperlink.CopyTo(hypertext.GetAddressOf()));
+  EXPECT_EQ(S_OK, root_obj->get_hyperlink(2, &hyperlink));
+  EXPECT_EQ(S_OK, hyperlink.As(&hypertext));
   EXPECT_EQ(S_OK,
             hypertext->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive()));
   EXPECT_STREQ(button_text_name.c_str(), text.Get());
@@ -799,8 +796,8 @@
   hypertext.Reset();
 
   // Get the text of the link.
-  EXPECT_EQ(S_OK, root_obj->get_hyperlink(3, hyperlink.GetAddressOf()));
-  EXPECT_EQ(S_OK, hyperlink.CopyTo(hypertext.GetAddressOf()));
+  EXPECT_EQ(S_OK, root_obj->get_hyperlink(3, &hyperlink));
+  EXPECT_EQ(S_OK, hyperlink.As(&hypertext));
   EXPECT_EQ(S_OK, hypertext->get_text(0, 4, text.Receive()));
   EXPECT_STREQ(link_text_name.c_str(), text.Get());
   text.Reset();
@@ -1385,12 +1382,10 @@
 
   Microsoft::WRL::ComPtr<IAccessibleText> textarea_object;
   EXPECT_HRESULT_SUCCEEDED(textarea_accessible->GetCOM()->QueryInterface(
-      IID_IAccessibleText,
-      reinterpret_cast<void**>(textarea_object.GetAddressOf())));
+      IID_PPV_ARGS(&textarea_object)));
   Microsoft::WRL::ComPtr<IAccessibleText> text_field_object;
   EXPECT_HRESULT_SUCCEEDED(text_field_accessible->GetCOM()->QueryInterface(
-      IID_IAccessibleText,
-      reinterpret_cast<void**>(text_field_object.GetAddressOf())));
+      IID_PPV_ARGS(&text_field_object)));
 
   LONG offset = 0;
   while (offset < static_cast<LONG>(text.length())) {
@@ -3046,26 +3041,26 @@
   base::win::ScopedVariant old_root_variant(-root_unique_id);
   Microsoft::WRL::ComPtr<IDispatch> old_root_dispatch;
   HRESULT hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild(
-      old_root_variant, old_root_dispatch.GetAddressOf());
+      old_root_variant, &old_root_dispatch);
   EXPECT_EQ(E_INVALIDARG, hr);
 
   base::win::ScopedVariant old_child_variant(-child_unique_id);
   Microsoft::WRL::ComPtr<IDispatch> old_child_dispatch;
   hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild(
-      old_child_variant, old_child_dispatch.GetAddressOf());
+      old_child_variant, &old_child_dispatch);
   EXPECT_EQ(E_INVALIDARG, hr);
 
   // Trying to access the unique IDs of the new objects should succeed.
   base::win::ScopedVariant new_root_variant(-root_unique_id_2);
   Microsoft::WRL::ComPtr<IDispatch> new_root_dispatch;
   hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild(
-      new_root_variant, new_root_dispatch.GetAddressOf());
+      new_root_variant, &new_root_dispatch);
   EXPECT_EQ(S_OK, hr);
 
   base::win::ScopedVariant new_child_variant(-child_unique_id_2);
   Microsoft::WRL::ComPtr<IDispatch> new_child_dispatch;
   hr = ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild(
-      new_child_variant, new_child_dispatch.GetAddressOf());
+      new_child_variant, &new_child_dispatch);
   EXPECT_EQ(S_OK, hr);
 }
 
@@ -3090,11 +3085,11 @@
   Microsoft::WRL::ComPtr<IDispatch> result;
   EXPECT_EQ(E_INVALIDARG,
             ToBrowserAccessibilityWin(child)->GetCOM()->get_accChild(
-                root_unique_id_variant, result.GetAddressOf()));
+                root_unique_id_variant, &result));
 
   base::win::ScopedVariant child_unique_id_variant(-GetUniqueId(child));
   EXPECT_EQ(S_OK, ToBrowserAccessibilityWin(root)->GetCOM()->get_accChild(
-                      child_unique_id_variant, result.GetAddressOf()));
+                      child_unique_id_variant, &result));
 }
 
 // TODO(crbug.com/929563): Disabled due to flakiness.
@@ -3145,7 +3140,7 @@
   EXPECT_EQ(1, n_relations);
 
   EXPECT_HRESULT_SUCCEEDED(
-      ax_root->GetCOM()->get_relation(0, describedby_relation.GetAddressOf()));
+      ax_root->GetCOM()->get_relation(0, &describedby_relation));
   EXPECT_HRESULT_SUCCEEDED(
       describedby_relation->get_relationType(relation_type.Receive()));
   EXPECT_EQ(L"describedBy", base::string16(relation_type.Get()));
@@ -3154,17 +3149,15 @@
   EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_nTargets(&n_targets));
   EXPECT_EQ(2, n_targets);
 
-  EXPECT_HRESULT_SUCCEEDED(
-      describedby_relation->get_target(0, target.GetAddressOf()));
-  target.CopyTo(ax_target.GetAddressOf());
+  EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_target(0, &target));
+  target.As(&ax_target);
   EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id));
   EXPECT_EQ(-GetUniqueId(ax_child1), unique_id);
   ax_target.Reset();
   target.Reset();
 
-  EXPECT_HRESULT_SUCCEEDED(
-      describedby_relation->get_target(1, target.GetAddressOf()));
-  target.CopyTo(ax_target.GetAddressOf());
+  EXPECT_HRESULT_SUCCEEDED(describedby_relation->get_target(1, &target));
+  target.As(&ax_target);
   EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id));
   EXPECT_EQ(-GetUniqueId(ax_child2), unique_id);
   ax_target.Reset();
@@ -3175,8 +3168,8 @@
   EXPECT_HRESULT_SUCCEEDED(ax_child1->GetCOM()->get_nRelations(&n_relations));
   EXPECT_EQ(1, n_relations);
 
-  EXPECT_HRESULT_SUCCEEDED(ax_child1->GetCOM()->get_relation(
-      0, description_for_relation.GetAddressOf()));
+  EXPECT_HRESULT_SUCCEEDED(
+      ax_child1->GetCOM()->get_relation(0, &description_for_relation));
   EXPECT_HRESULT_SUCCEEDED(
       description_for_relation->get_relationType(relation_type.Receive()));
   EXPECT_EQ(L"descriptionFor", base::string16(relation_type.Get()));
@@ -3185,9 +3178,8 @@
   EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_nTargets(&n_targets));
   EXPECT_EQ(1, n_targets);
 
-  EXPECT_HRESULT_SUCCEEDED(
-      description_for_relation->get_target(0, target.GetAddressOf()));
-  target.CopyTo(ax_target.GetAddressOf());
+  EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_target(0, &target));
+  target.As(&ax_target);
   EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id));
   EXPECT_EQ(-GetUniqueId(ax_root), unique_id);
   ax_target.Reset();
@@ -3197,8 +3189,8 @@
   EXPECT_HRESULT_SUCCEEDED(ax_child2->GetCOM()->get_nRelations(&n_relations));
   EXPECT_EQ(1, n_relations);
 
-  EXPECT_HRESULT_SUCCEEDED(ax_child2->GetCOM()->get_relation(
-      0, description_for_relation.GetAddressOf()));
+  EXPECT_HRESULT_SUCCEEDED(
+      ax_child2->GetCOM()->get_relation(0, &description_for_relation));
   EXPECT_HRESULT_SUCCEEDED(
       description_for_relation->get_relationType(relation_type.Receive()));
   EXPECT_EQ(L"descriptionFor", base::string16(relation_type.Get()));
@@ -3207,9 +3199,8 @@
   EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_nTargets(&n_targets));
   EXPECT_EQ(1, n_targets);
 
-  EXPECT_HRESULT_SUCCEEDED(
-      description_for_relation->get_target(0, target.GetAddressOf()));
-  target.CopyTo(ax_target.GetAddressOf());
+  EXPECT_HRESULT_SUCCEEDED(description_for_relation->get_target(0, &target));
+  target.As(&ax_target);
   EXPECT_HRESULT_SUCCEEDED(ax_target->get_uniqueID(&unique_id));
   EXPECT_EQ(-GetUniqueId(ax_root), unique_id);
   ax_target.Reset();
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index c93d4267..d2866960 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -551,8 +551,16 @@
   // make network requests on behalf of the real origin).
   //
   // TODO(lukasza): Cover MHTML main frames here.
-  if (navigation_request->IsForMhtmlSubframe())
-    return url::Origin();
+  if (navigation_request->IsForMhtmlSubframe()) {
+    // TODO(lukasza): https://crbug.com/1056949: Stop using
+    // mhtml.subframe.invalid as the precursor (once https://crbug.com/1056949
+    // is debugged OR once network::VerifyRequestInitiatorLock starts to compare
+    // precursors).
+    url::Origin mhtml_subframe_origin =
+        url::Origin::Create(GURL("https://mhtml.subframe.invalid"))
+            .DeriveNewOpaqueOrigin();
+    return mhtml_subframe_origin;
+  }
 
   // Srcdoc subframes need to inherit their origin from their parent frame.
   if (navigation_request->GetURL().IsAboutSrcdoc()) {
@@ -564,11 +572,19 @@
     return parent->GetLastCommittedOrigin();
   }
 
+  // TODO(lukasza): https://crbug.com/1056949: Stop using
+  // browser.initiated.invalid as the precursor (once https://crbug.com/1056949
+  // is debugged OR once network::VerifyRequestInitiatorLock starts to compare
+  // precursors).
+  url::Origin browser_initiated_fallback_origin =
+      url::Origin::Create(GURL("https://browser.initiated.invalid"))
+          .DeriveNewOpaqueOrigin();
+
   // In cases not covered above, URLLoaderFactory should be associated with the
   // origin of |common_params.url| and/or |common_params.initiator_origin|.
-  return url::Origin::Resolve(
-      common_params.url,
-      common_params.initiator_origin.value_or(url::Origin()));
+  return url::Origin::Resolve(common_params.url,
+                              common_params.initiator_origin.value_or(
+                                  browser_initiated_fallback_origin));
 }
 
 url::Origin GetOriginForURLLoaderFactory(
@@ -588,7 +604,12 @@
 
   // Check that |result| origin is allowed to be accessed from the process that
   // is the target of this navigation.
-  if (ShouldBypassChecksForErrorPage(target_frame, navigation_request))
+  //
+  // TODO(lukasza): https://crbug.com/1056949: Stop excluding opaque origins
+  // from the security checks once the *.invalid diagnostics are no longer
+  // needed.
+  if (ShouldBypassChecksForErrorPage(target_frame, navigation_request) ||
+      result.opaque())
     return result;
   int process_id = target_frame->GetProcess()->GetID();
   auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
@@ -9068,7 +9089,17 @@
   if (active_sandbox_flags_ == *active_sandbox_flags_control_)
     return;
 
+  base::debug::ScopedCrashKeyString scoped_url(
+      base::debug::AllocateCrashKeyString("url",
+                                          base::debug::CrashKeySize::Size256),
+      GetLastCommittedURL().possibly_invalid_spec());
+  base::debug::ScopedCrashKeyString scoped_sandbox(
+      base::debug::AllocateCrashKeyString("sandbox",
+                                          base::debug::CrashKeySize::Size256),
+      base::StringPrintf("%u, %u", uint32_t(active_sandbox_flags_),
+                         uint32_t(*active_sandbox_flags_control_)));
   DCHECK(false);
+  base::debug::DumpWithoutCrashing();
 }
 
 void RenderFrameHostImpl::SetEmbeddingToken(
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index 94ee23f..98ad53fe 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -585,19 +585,22 @@
         database_id, object_store_id, index_id, range.upper());
     cursor_options->high_open = range.upper_open();
 
-    std::string found_high_key;
-    // Seek to the *last* key in the set of non-unique keys
-    if (!FindGreatestKeyLessThanOrEqual(transaction, cursor_options->high_key,
-                                        &found_high_key, status))
-      return false;
+    if (!cursor_options->forward) {
+      // For reverse cursors, we need a key that exists.
+      std::string found_high_key;
+      // Seek to the *last* key in the set of non-unique keys
+      if (!FindGreatestKeyLessThanOrEqual(transaction, cursor_options->high_key,
+                                          &found_high_key, status))
+        return false;
 
-    // If the target key should not be included, but we end up with a smaller
-    // key, we should include that.
-    if (cursor_options->high_open &&
-        CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
-      cursor_options->high_open = false;
+      // If the target key should not be included, but we end up with a smaller
+      // key, we should include that.
+      if (cursor_options->high_open &&
+          CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
+        cursor_options->high_open = false;
 
-    cursor_options->high_key = found_high_key;
+      cursor_options->high_key = found_high_key;
+    }
   }
 
   return true;
diff --git a/content/browser/renderer_host/frame_token_message_queue.cc b/content/browser/renderer_host/frame_token_message_queue.cc
index 08cadbc..72f07c45 100644
--- a/content/browser/renderer_host/frame_token_message_queue.cc
+++ b/content/browser/renderer_host/frame_token_message_queue.cc
@@ -5,9 +5,6 @@
 #include "content/browser/renderer_host/frame_token_message_queue.h"
 
 #include "base/bind.h"
-#include "base/debug/crash_logging.h"
-#include "base/debug/dump_without_crashing.h"
-#include "base/strings/string_number_conversions.h"
 #include "ipc/ipc_message.h"
 
 namespace content {
@@ -21,28 +18,22 @@
 }
 
 void FrameTokenMessageQueue::DidProcessFrame(uint32_t frame_token) {
-  // Frame tokens always increase.
-  if (frame_token <= last_received_frame_token_) {
-    // TODO(samans): Remove this once the investigation into
-    // https://crbug.com/1045372 is over.
-    static auto* last_frame_token = base::debug::AllocateCrashKeyString(
-        "last_frame_token", base::debug::CrashKeySize::Size32);
-    static auto* new_frame_token = base::debug::AllocateCrashKeyString(
-        "new_frame_token", base::debug::CrashKeySize::Size32);
-    base::debug::SetCrashKeyString(
-        last_frame_token, base::NumberToString(last_received_frame_token_));
-    base::debug::SetCrashKeyString(new_frame_token,
-                                   base::NumberToString(frame_token));
-    base::debug::DumpWithoutCrashing();
+  // The queue will be cleared if the Renderer has been Reset. Do not enforce
+  // token order, as ACKs for old frames may still be in flight from Viz.
+  if (callback_map_.empty()) {
+    last_received_frame_token_ = frame_token;
+    return;
+  }
 
-    // TODO(jonross): Remove this once root cause of flaking tests is found.
-    // (crbug.com/1087744)
-    DCHECK(false) << "FrameTokenMessageQueue invalid order of acknowledged "
-                     "token. Current: "
-                  << frame_token
-                  << " Last Received: " << last_received_frame_token_
-                  << " Last Before Reset: " << last_received_frame_token_reset_
-                  << " queue size " << callback_map_.size();
+  // Frame tokens always increase. However when a Reset occurs old tokens can
+  // arrive. Do not enforce token order if we are seeing the ACK for the
+  // previous frame.
+  // TODO(jonross): we should consider updating LocalSurfaceId to also track
+  // frame_token. So that we could properly differentiate between origins of
+  // frame. As we cannot enforce ordering between Reset Renderers.
+  if ((frame_token <= last_received_frame_token_) &&
+      !(last_received_frame_token_reset_ &&
+        last_received_frame_token_reset_ != frame_token)) {
     client_->OnInvalidFrameToken(frame_token);
     return;
   }
@@ -65,17 +56,6 @@
 void FrameTokenMessageQueue::EnqueueOrRunFrameTokenCallback(
     uint32_t frame_token,
     base::OnceClosure callback) {
-  // Zero token is invalid.
-  if (!frame_token) {
-    // TODO(jonross): Remove this once root cause of flaking tests is found.
-    // (crbug.com/1087744)
-    DCHECK(false) << "FrameTokenMessageQueue invalid enqueued frame token. "
-                     "Last Received: "
-                  << last_received_frame_token_;
-    client_->OnInvalidFrameToken(frame_token);
-    return;
-  }
-
   if (frame_token <= last_received_frame_token_) {
     std::move(callback).Run();
     return;
@@ -86,11 +66,6 @@
 void FrameTokenMessageQueue::OnFrameSwapMessagesReceived(
     uint32_t frame_token,
     std::vector<IPC::Message> messages) {
-  // TODO(samans): Remove this once the investigation into
-  // https://crbug.com/1045372 is over.
-  if (!frame_token) {
-    base::debug::DumpWithoutCrashing();
-  }
   EnqueueOrRunFrameTokenCallback(
       frame_token, base::BindOnce(&FrameTokenMessageQueue::ProcessSwapMessages,
                                   base::Unretained(this), std::move(messages)));
diff --git a/content/browser/renderer_host/frame_token_message_queue_unittest.cc b/content/browser/renderer_host/frame_token_message_queue_unittest.cc
index 815ce5a..da59028c 100644
--- a/content/browser/renderer_host/frame_token_message_queue_unittest.cc
+++ b/content/browser/renderer_host/frame_token_message_queue_unittest.cc
@@ -476,27 +476,6 @@
   EXPECT_TRUE(second_enqueuer.frame_token_callback_called());
 }
 
-// TODO(jonross): Re-enable this once the DCHECKs have been removed.
-// (crbug.com/1087744)
-// An empty frame token is considered invalid, so this tests that attempting to
-// enqueue for that is rejected.
-TEST_F(FrameTokenMessageQueueTest, DISABLED_EmptyTokenForIPCMessageIsRejected) {
-  FrameTokenMessageQueue* queue = frame_token_message_queue();
-  TestFrameTokenMessageQueueClient* client = test_client();
-  ASSERT_EQ(0u, queue->size());
-
-  const uint32_t invalid_frame_token = 0;
-  IPC::Message msg(0, 1, IPC::Message::PRIORITY_NORMAL);
-  std::vector<IPC::Message> messages;
-  messages.push_back(msg);
-
-  // Adding to the queue with a new frame token should not cause processing.
-  queue->OnFrameSwapMessagesReceived(invalid_frame_token, std::move(messages));
-  EXPECT_EQ(0u, queue->size());
-  EXPECT_TRUE(client->invalid_frame_token_called());
-  EXPECT_EQ(invalid_frame_token, client->invalid_frame_token());
-  EXPECT_EQ(0, client->on_process_swap_message_count());
-}
 
 // Tests that when adding an IPC::Message for an earlier frame token, that it is
 // enqueued.
@@ -528,12 +507,9 @@
   EXPECT_EQ(0, client->on_process_swap_message_count());
 }
 
-// TODO(jonross): Re-enable this once the DCHECKs have been removed.
-// (crbug.com/1087744)
 // Tests that if DidProcessFrame is called with an invalid token, that it is
 // rejected, and that no callbacks are processed.
-TEST_F(FrameTokenMessageQueueTest,
-       DISABLED_InvalidDidProcessFrameTokenNotProcessed) {
+TEST_F(FrameTokenMessageQueueTest, InvalidDidProcessFrameTokenNotProcessed) {
   FrameTokenMessageQueue* queue = frame_token_message_queue();
   TestFrameTokenMessageQueueClient* client = test_client();
   TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer();
@@ -566,12 +542,9 @@
   EXPECT_FALSE(enqueuer->frame_token_callback_called());
 }
 
-// TODO(jonross): Re-enable this once the DCHECKs have been removed.
-// (crbug.com/1087744)
 // Test that if DidProcessFrame is called with an earlier frame token, that it
 // is rejected, and that no callbacks are processed.
-TEST_F(FrameTokenMessageQueueTest,
-       DISABLED_EarlierTokenForDidProcessFrameRejected) {
+TEST_F(FrameTokenMessageQueueTest, EarlierTokenForDidProcessFrameRejected) {
   FrameTokenMessageQueue* queue = frame_token_message_queue();
   TestFrameTokenMessageQueueClient* client = test_client();
   TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer();
diff --git a/content/browser/renderer_host/input/web_input_event_util_unittest.cc b/content/browser/renderer_host/input/web_input_event_util_unittest.cc
index f0d9983..8dc349c3 100644
--- a/content/browser/renderer_host/input/web_input_event_util_unittest.cc
+++ b/content/browser/renderer_host/input/web_input_event_util_unittest.cc
@@ -14,7 +14,6 @@
 #include "ui/events/gesture_detection/motion_event_generic.h"
 #include "ui/events/gesture_event_details.h"
 #include "ui/events/types/event_type.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 
 using blink::WebInputEvent;
 using blink::WebTouchEvent;
diff --git a/content/browser/service_worker/service_worker_cache_writer.cc b/content/browser/service_worker/service_worker_cache_writer.cc
index 65345bca1..3cfd4a07 100644
--- a/content/browser/service_worker/service_worker_cache_writer.cc
+++ b/content/browser/service_worker/service_worker_cache_writer.cc
@@ -164,7 +164,7 @@
 ServiceWorkerCacheWriter::ServiceWorkerCacheWriter(
     mojo::Remote<storage::mojom::ServiceWorkerResourceReader> compare_reader,
     mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader,
-    std::unique_ptr<ServiceWorkerResponseWriter> writer,
+    mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer,
     int64_t writer_resource_id,
     bool pause_when_not_identical)
     : state_(STATE_START),
@@ -181,7 +181,7 @@
 std::unique_ptr<ServiceWorkerCacheWriter>
 ServiceWorkerCacheWriter::CreateForCopy(
     mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader,
-    std::unique_ptr<ServiceWorkerResponseWriter> writer,
+    mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer,
     int64_t writer_resource_id) {
   DCHECK(copy_reader);
   DCHECK(writer);
@@ -194,7 +194,7 @@
 
 std::unique_ptr<ServiceWorkerCacheWriter>
 ServiceWorkerCacheWriter::CreateForWriteBack(
-    std::unique_ptr<ServiceWorkerResponseWriter> writer,
+    mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer,
     int64_t writer_resource_id) {
   DCHECK(writer);
   return base::WrapUnique(new ServiceWorkerCacheWriter(
@@ -207,7 +207,7 @@
 ServiceWorkerCacheWriter::CreateForComparison(
     mojo::Remote<storage::mojom::ServiceWorkerResourceReader> compare_reader,
     mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader,
-    std::unique_ptr<ServiceWorkerResponseWriter> writer,
+    mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer,
     int64_t writer_resource_id,
     bool pause_when_not_identical) {
   // |compare_reader| reads data for the comparison. |copy_reader| reads
@@ -720,15 +720,15 @@
 }
 
 int ServiceWorkerCacheWriter::WriteResponseHeadToResponseWriter(
-    const network::mojom::URLResponseHead& response_head,
-    int response_data_size) {
+    const network::mojom::URLResponseHead& response_head) {
   did_replace_ = true;
   net::CompletionOnceCallback run_callback = base::BindOnce(
       &ServiceWorkerCacheWriter::AsyncDoLoop, weak_factory_.GetWeakPtr());
   scoped_refptr<AsyncOnlyCompletionCallbackAdaptor> adaptor(
       new AsyncOnlyCompletionCallbackAdaptor(std::move(run_callback)));
+  // TODO(crbug.com/1055677): Avoid copying |response_head| if possible.
   writer_->WriteResponseHead(
-      response_head, response_data_size,
+      response_head.Clone(),
       base::BindOnce(&AsyncOnlyCompletionCallbackAdaptor::WrappedCallback,
                      adaptor));
   adaptor->set_async(true);
@@ -745,8 +745,7 @@
       return result;
     }
   }
-  return WriteResponseHeadToResponseWriter(response_head,
-                                           response_head.content_length);
+  return WriteResponseHeadToResponseWriter(response_head);
 }
 
 int ServiceWorkerCacheWriter::WriteDataToResponseWriter(
@@ -756,8 +755,10 @@
       &ServiceWorkerCacheWriter::AsyncDoLoop, weak_factory_.GetWeakPtr());
   scoped_refptr<AsyncOnlyCompletionCallbackAdaptor> adaptor(
       new AsyncOnlyCompletionCallbackAdaptor(std::move(run_callback)));
+  mojo_base::BigBuffer big_buffer(
+      base::as_bytes(base::make_span(data->data(), length)));
   writer_->WriteData(
-      data.get(), length,
+      std::move(big_buffer),
       base::BindOnce(&AsyncOnlyCompletionCallbackAdaptor::WrappedCallback,
                      adaptor));
   adaptor->set_async(true);
diff --git a/content/browser/service_worker/service_worker_cache_writer.h b/content/browser/service_worker/service_worker_cache_writer.h
index 318ec15..af7dbc1 100644
--- a/content/browser/service_worker/service_worker_cache_writer.h
+++ b/content/browser/service_worker/service_worker_cache_writer.h
@@ -22,8 +22,6 @@
 
 namespace content {
 
-class ServiceWorkerResponseWriter;
-
 // This class is responsible for possibly updating the ServiceWorker script
 // cache for an installed ServiceWorker main script. If there is no existing
 // cache entry, this class always writes supplied data back to the cache; if
@@ -72,13 +70,13 @@
   // script is read by |copy_reader|.
   static std::unique_ptr<ServiceWorkerCacheWriter> CreateForCopy(
       mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader,
-      std::unique_ptr<ServiceWorkerResponseWriter> writer,
+      mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer,
       int64_t writer_resource_id);
 
   // Create a cache writer instance that unconditionally write back data
   // supplied to |MaybeWriteHeaders| and |MaybeWriteData| to storage.
   static std::unique_ptr<ServiceWorkerCacheWriter> CreateForWriteBack(
-      std::unique_ptr<ServiceWorkerResponseWriter> writer,
+      mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer,
       int64_t writer_resource_id);
 
   // Create a cache writer that compares between a script in storage and data
@@ -94,7 +92,7 @@
   static std::unique_ptr<ServiceWorkerCacheWriter> CreateForComparison(
       mojo::Remote<storage::mojom::ServiceWorkerResourceReader> compare_reader,
       mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader,
-      std::unique_ptr<ServiceWorkerResponseWriter> writer,
+      mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer,
       int64_t writer_resource_id,
       bool pause_when_not_identical);
 
@@ -214,7 +212,7 @@
   ServiceWorkerCacheWriter(
       mojo::Remote<storage::mojom::ServiceWorkerResourceReader> compare_reader,
       mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader,
-      std::unique_ptr<ServiceWorkerResponseWriter> writer,
+      mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer,
       int64_t writer_resource_id,
       bool pause_when_not_identical);
 
@@ -270,8 +268,7 @@
   int WriteResponseHead(const network::mojom::URLResponseHead& response_head);
   int WriteData(scoped_refptr<net::IOBuffer> data, int length);
   int WriteResponseHeadToResponseWriter(
-      const network::mojom::URLResponseHead& response_head,
-      int response_data_size);
+      const network::mojom::URLResponseHead& response_head);
   int WriteDataToResponseWriter(scoped_refptr<net::IOBuffer> data, int length);
 
   // Called when |write_observer_| finishes its WillWriteData() operation.
@@ -334,7 +331,7 @@
   mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader_;
   std::unique_ptr<DataPipeReader> copy_data_pipe_reader_;
 
-  std::unique_ptr<ServiceWorkerResponseWriter> writer_;
+  mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer_;
   const int64_t writer_resource_id_ =
       blink::mojom::kInvalidServiceWorkerResourceId;
 
diff --git a/content/browser/service_worker/service_worker_cache_writer_unittest.cc b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
index 2fb1adf4..b61d74f 100644
--- a/content/browser/service_worker/service_worker_cache_writer_unittest.cc
+++ b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
@@ -133,13 +133,17 @@
     return remote;
   }
 
-  std::unique_ptr<ServiceWorkerResponseWriter> CreateWriter() {
+  mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> CreateWriter() {
+    mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> remote;
     if (writers_.empty())
-      return base::WrapUnique<ServiceWorkerResponseWriter>(nullptr);
-    std::unique_ptr<ServiceWorkerResponseWriter> writer(
-        std::move(writers_.front()));
+      return remote;
+    auto* writer_rawptr = writers_.front().get();
+    remote.Bind(writer_rawptr->BindNewPipeAndPassRemote(
+        // Keep the instance alive until the connection is destroyed.
+        base::BindOnce([](std::unique_ptr<MockServiceWorkerResponseWriter>) {},
+                       std::move(writers_.front()))));
     writers_.pop_front();
-    return writer;
+    return remote;
   }
 
   ServiceWorkerCacheWriter::OnWriteCompleteCallback CreateWriteCallback() {
diff --git a/content/browser/service_worker/service_worker_new_script_loader.cc b/content/browser/service_worker/service_worker_new_script_loader.cc
index fad3ddbd6..7be5c9b3 100644
--- a/content/browser/service_worker/service_worker_new_script_loader.cc
+++ b/content/browser/service_worker/service_worker_new_script_loader.cc
@@ -119,9 +119,15 @@
     resource_request.load_flags |= net::LOAD_VALIDATE_CACHE;
   }
 
-  ServiceWorkerStorage* storage = version_->context()->storage();
+  mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer;
+  version_->context()
+      ->registry()
+      ->GetRemoteStorageControl()
+      ->CreateResourceWriter(cache_resource_id,
+                             writer.BindNewPipeAndPassReceiver());
+
   cache_writer_ = ServiceWorkerCacheWriter::CreateForWriteBack(
-      storage->CreateResponseWriter(cache_resource_id), cache_resource_id);
+      std::move(writer), cache_resource_id);
 
   version_->script_cache_map()->NotifyStartedCaching(request_url_,
                                                      cache_resource_id);
diff --git a/content/browser/service_worker/service_worker_script_loader_factory.cc b/content/browser/service_worker/service_worker_script_loader_factory.cc
index a948f7164..24fecfdb 100644
--- a/content/browser/service_worker/service_worker_script_loader_factory.cc
+++ b/content/browser/service_worker/service_worker_script_loader_factory.cc
@@ -199,11 +199,12 @@
   mojo::Remote<storage::mojom::ServiceWorkerResourceReader> reader;
   context_->registry()->GetRemoteStorageControl()->CreateResourceReader(
       resource_id, reader.BindNewPipeAndPassReceiver());
+  mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer;
+  context_->registry()->GetRemoteStorageControl()->CreateResourceWriter(
+      new_resource_id, writer.BindNewPipeAndPassReceiver());
 
   cache_writer_ = ServiceWorkerCacheWriter::CreateForCopy(
-      std::move(reader),
-      context_->storage()->CreateResponseWriter(new_resource_id),
-      new_resource_id);
+      std::move(reader), std::move(writer), new_resource_id);
 
   scoped_refptr<ServiceWorkerVersion> version = worker_host_->version();
   version->script_cache_map()->NotifyStartedCaching(url, new_resource_id);
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.cc b/content/browser/service_worker/service_worker_single_script_update_checker.cc
index 3a432b1..0c23898d 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.cc
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.cc
@@ -108,7 +108,7 @@
     scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
     mojo::Remote<storage::mojom::ServiceWorkerResourceReader> compare_reader,
     mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader,
-    std::unique_ptr<ServiceWorkerResponseWriter> writer,
+    mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer,
     int64_t writer_resource_id,
     ResultCallback callback)
     : script_url_(script_url),
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.h b/content/browser/service_worker/service_worker_single_script_update_checker.h
index 65f23c1..bad97e5 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.h
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.h
@@ -123,7 +123,7 @@
       scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
       mojo::Remote<storage::mojom::ServiceWorkerResourceReader> compare_reader,
       mojo::Remote<storage::mojom::ServiceWorkerResourceReader> copy_reader,
-      std::unique_ptr<ServiceWorkerResponseWriter> writer,
+      mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer,
       int64_t write_resource_id,
       ResultCallback callback);
 
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc b/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
index 242b7fd2..92d5534 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
+++ b/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
@@ -92,7 +92,7 @@
       const GURL& scope,
       std::unique_ptr<MockServiceWorkerResponseReader> compare_reader,
       std::unique_ptr<MockServiceWorkerResponseReader> copy_reader,
-      std::unique_ptr<ServiceWorkerResponseWriter> writer,
+      std::unique_ptr<MockServiceWorkerResponseWriter> writer,
       network::TestURLLoaderFactory* loader_factory,
       base::Optional<CheckResult>* out_check_result) {
     return CreateSingleScriptUpdateChecker(
@@ -114,6 +114,17 @@
     return remote;
   }
 
+  mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> WrapWriter(
+      std::unique_ptr<MockServiceWorkerResponseWriter> writer) {
+    mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> remote;
+    remote.Bind(writer->BindNewPipeAndPassRemote(base::BindOnce(
+        [](std::unique_ptr<MockServiceWorkerResponseWriter>) {
+          // Keep |writer| until mojo connection is destroyed.
+        },
+        std::move(writer))));
+    return remote;
+  }
+
   // Note that |loader_factory| should be alive as long as the single script
   // update checker is running.
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker>
@@ -126,7 +137,7 @@
       base::TimeDelta time_since_last_check,
       std::unique_ptr<MockServiceWorkerResponseReader> compare_reader,
       std::unique_ptr<MockServiceWorkerResponseReader> copy_reader,
-      std::unique_ptr<ServiceWorkerResponseWriter> writer,
+      std::unique_ptr<MockServiceWorkerResponseWriter> writer,
       network::TestURLLoaderFactory* loader_factory,
       base::Optional<CheckResult>* out_check_result) {
     auto fetch_client_settings_object =
@@ -143,7 +154,7 @@
         base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
             loader_factory),
         WrapReader(std::move(compare_reader)),
-        WrapReader(std::move(copy_reader)), std::move(writer),
+        WrapReader(std::move(copy_reader)), WrapWriter(std::move(writer)),
         /*writer_resource_id=*/0,
         base::BindOnce(
             [](base::Optional<CheckResult>* out_check_result_param,
diff --git a/content/browser/service_worker/service_worker_test_utils.cc b/content/browser/service_worker/service_worker_test_utils.cc
index cf5230d3..b883eb4 100644
--- a/content/browser/service_worker/service_worker_test_utils.cc
+++ b/content/browser/service_worker/service_worker_test_utils.cc
@@ -691,6 +691,14 @@
 
 MockServiceWorkerResponseWriter::~MockServiceWorkerResponseWriter() = default;
 
+mojo::PendingRemote<storage::mojom::ServiceWorkerResourceWriter>
+MockServiceWorkerResponseWriter::BindNewPipeAndPassRemote(
+    base::OnceClosure disconnect_handler) {
+  auto remote = receiver_.BindNewPipeAndPassRemote();
+  receiver_.set_disconnect_handler(std::move(disconnect_handler));
+  return remote;
+}
+
 void MockServiceWorkerResponseWriter::WriteInfo(
     HttpResponseInfoIOBuffer* info_buf,
     net::CompletionOnceCallback callback) {
@@ -718,6 +726,31 @@
   pending_callback_ = std::move(callback);
 }
 
+void MockServiceWorkerResponseWriter::WriteResponseHead(
+    network::mojom::URLResponseHeadPtr response_head,
+    WriteResponseHeadCallback callback) {
+  DCHECK(!expected_writes_.empty());
+  ExpectedWrite write = expected_writes_.front();
+  EXPECT_TRUE(write.is_info);
+  if (write.result > 0) {
+    EXPECT_EQ(write.length, static_cast<size_t>(response_head->content_length));
+    info_written_ += response_head->content_length;
+  }
+  pending_callback_ = std::move(callback);
+}
+
+void MockServiceWorkerResponseWriter::WriteData(mojo_base::BigBuffer data,
+                                                WriteDataCallback callback) {
+  DCHECK(!expected_writes_.empty());
+  ExpectedWrite write = expected_writes_.front();
+  EXPECT_FALSE(write.is_info);
+  if (write.result > 0) {
+    EXPECT_EQ(write.length, data.size());
+    data_written_ += data.size();
+  }
+  pending_callback_ = std::move(callback);
+}
+
 void MockServiceWorkerResponseWriter::ExpectWriteInfoOk(size_t length) {
   ExpectWriteInfo(length, length);
 }
@@ -741,10 +774,16 @@
 }
 
 void MockServiceWorkerResponseWriter::CompletePendingWrite() {
+  // Make sure that all messages are received at this point.
+  receiver_.FlushForTesting();
+
   DCHECK(!expected_writes_.empty());
+  DCHECK(pending_callback_);
   ExpectedWrite write = expected_writes_.front();
   expected_writes_.pop();
   std::move(pending_callback_).Run(write.result);
+  // Wait until |pending_callback_| finishes.
+  base::RunLoop().RunUntilIdle();
 }
 
 ServiceWorkerUpdateCheckTestUtils::ServiceWorkerUpdateCheckTestUtils() =
@@ -775,10 +814,15 @@
       ->CreateResourceReader(old_resource_id,
                              copy_reader.BindNewPipeAndPassReceiver());
 
+  mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer;
+  worker_test_helper->context()
+      ->registry()
+      ->GetRemoteStorageControl()
+      ->CreateResourceWriter(new_resource_id,
+                             writer.BindNewPipeAndPassReceiver());
+
   auto cache_writer = ServiceWorkerCacheWriter::CreateForComparison(
-      std::move(compare_reader), std::move(copy_reader),
-      worker_test_helper->context()->storage()->CreateResponseWriter(
-          new_resource_id),
+      std::move(compare_reader), std::move(copy_reader), std::move(writer),
       new_resource_id, true /* pause_when_not_identical */);
   cache_writer->response_head_to_write_ =
       network::mojom::URLResponseHead::New();
diff --git a/content/browser/service_worker/service_worker_test_utils.h b/content/browser/service_worker/service_worker_test_utils.h
index 7833980..598248d5 100644
--- a/content/browser/service_worker/service_worker_test_utils.h
+++ b/content/browser/service_worker/service_worker_test_utils.h
@@ -328,11 +328,16 @@
 // A convenience method AllExpectedWritesDone() is exposed so tests can ensure
 // that all expected writes have been consumed by matching calls to WriteInfo()
 // or WriteData().
-class MockServiceWorkerResponseWriter : public ServiceWorkerResponseWriter {
+class MockServiceWorkerResponseWriter
+    : public ServiceWorkerResponseWriter,
+      public storage::mojom::ServiceWorkerResourceWriter {
  public:
   MockServiceWorkerResponseWriter();
   ~MockServiceWorkerResponseWriter() override;
 
+  mojo::PendingRemote<storage::mojom::ServiceWorkerResourceWriter>
+  BindNewPipeAndPassRemote(base::OnceClosure disconnect_handler);
+
   // ServiceWorkerResponseWriter overrides
   void WriteInfo(HttpResponseInfoIOBuffer* info_buf,
                  net::CompletionOnceCallback callback) override;
@@ -340,6 +345,12 @@
                  int buf_len,
                  net::CompletionOnceCallback callback) override;
 
+  // ServiceWorkerResourceWriter overrides:
+  void WriteResponseHead(network::mojom::URLResponseHeadPtr response_head,
+                         WriteResponseHeadCallback callback) override;
+  void WriteData(mojo_base::BigBuffer data,
+                 WriteDataCallback callback) override;
+
   // Enqueue expected writes.
   void ExpectWriteInfoOk(size_t len);
   void ExpectWriteInfo(size_t len, int result);
@@ -370,6 +381,8 @@
 
   net::CompletionOnceCallback pending_callback_;
 
+  mojo::Receiver<storage::mojom::ServiceWorkerResourceWriter> receiver_{this};
+
   DISALLOW_COPY_AND_ASSIGN(MockServiceWorkerResponseWriter);
 };
 
diff --git a/content/browser/service_worker/service_worker_update_checker.cc b/content/browser/service_worker/service_worker_update_checker.cc
index 28c5c821..72418237 100644
--- a/content/browser/service_worker/service_worker_update_checker.cc
+++ b/content/browser/service_worker/service_worker_update_checker.cc
@@ -265,7 +265,6 @@
   // cache map and it doesn't issue network request.
   const bool is_main_script = url == main_script_url_;
 
-  ServiceWorkerStorage* storage = version_to_update_->context()->storage();
   ServiceWorkerRegistry* registry = version_to_update_->context()->registry();
 
   // We need two identical readers for comparing and reading the resource for
@@ -277,7 +276,10 @@
   registry->GetRemoteStorageControl()->CreateResourceReader(
       resource_id, copy_reader.BindNewPipeAndPassReceiver());
 
-  auto writer = storage->CreateResponseWriter(new_resource_id);
+  mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer;
+  registry->GetRemoteStorageControl()->CreateResourceWriter(
+      new_resource_id, writer.BindNewPipeAndPassReceiver());
+
   running_checker_ = std::make_unique<ServiceWorkerSingleScriptUpdateChecker>(
       url, is_main_script, main_script_url_, version_to_update_->scope(),
       force_bypass_cache_, update_via_cache_, fetch_client_settings_object_,
diff --git a/content/browser/speech/tts_win.cc b/content/browser/speech/tts_win.cc
index 816ae50..a99e986 100644
--- a/content/browser/speech/tts_win.cc
+++ b/content/browser/speech/tts_win.cc
@@ -224,8 +224,7 @@
 void TtsPlatformImplWin::GetVoices(std::vector<VoiceData>* out_voices) {
   Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
   unsigned long voice_count;
-  if (S_OK !=
-      SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf()))
+  if (S_OK != SpEnumTokens(SPCAT_VOICES, NULL, NULL, &voice_tokens))
     return;
   if (S_OK != voice_tokens->GetCount(&voice_count))
     return;
@@ -234,7 +233,7 @@
     VoiceData voice;
 
     Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
-    if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL))
+    if (S_OK != voice_tokens->Next(1, &voice_token, NULL))
       return;
 
     base::win::ScopedCoMem<WCHAR> description;
@@ -243,7 +242,7 @@
     voice.name = base::WideToUTF8(description.get());
 
     Microsoft::WRL::ComPtr<ISpDataKey> attributes;
-    if (S_OK != voice_token->OpenKey(kAttributesKey, attributes.GetAddressOf()))
+    if (S_OK != voice_token->OpenKey(kAttributesKey, &attributes))
       continue;
 
     base::win::ScopedCoMem<WCHAR> language;
@@ -314,15 +313,14 @@
 
   Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
   unsigned long voice_count;
-  if (S_OK !=
-      SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf()))
+  if (S_OK != SpEnumTokens(SPCAT_VOICES, NULL, NULL, &voice_tokens))
     return;
   if (S_OK != voice_tokens->GetCount(&voice_count))
     return;
 
   for (unsigned i = 0; i < voice_count; i++) {
     Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
-    if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL))
+    if (S_OK != voice_tokens->Next(1, &voice_token, NULL))
       return;
 
     base::win::ScopedCoMem<WCHAR> description;
diff --git a/content/browser/web_package/mock_web_bundle_reader_factory.cc b/content/browser/web_package/mock_web_bundle_reader_factory.cc
index 2f8f1d2..c61c05a 100644
--- a/content/browser/web_package/mock_web_bundle_reader_factory.cc
+++ b/content/browser/web_package/mock_web_bundle_reader_factory.cc
@@ -14,6 +14,7 @@
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "content/browser/web_package/web_bundle_reader.h"
 #include "content/browser/web_package/web_bundle_source.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -22,24 +23,23 @@
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/base/filename_util.h"
 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
 
 namespace {
 
-class MockParser final : public data_decoder::mojom::WebBundleParser {
+class MockParser final : public web_package::mojom::WebBundleParser {
  public:
-  MockParser(
-      mojo::PendingReceiver<data_decoder::mojom::WebBundleParser> receiver)
+  explicit MockParser(
+      mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver)
       : receiver_(this, std::move(receiver)) {}
   ~MockParser() override = default;
 
-  void RunMetadataCallback(data_decoder::mojom::BundleMetadataPtr metadata) {
+  void RunMetadataCallback(web_package::mojom::BundleMetadataPtr metadata) {
     std::move(metadata_callback_).Run(std::move(metadata), nullptr);
   }
-  void RunResponseCallback(data_decoder::mojom::BundleResponsePtr response) {
+  void RunResponseCallback(web_package::mojom::BundleResponsePtr response) {
     std::move(response_callback_).Run(std::move(response), nullptr);
   }
 
@@ -51,7 +51,7 @@
   }
 
   void WaitUntilParseResponseCalled(
-      base::OnceCallback<void(data_decoder::mojom::BundleResponseLocationPtr)>
+      base::OnceCallback<void(web_package::mojom::BundleResponseLocationPtr)>
           callback) {
     if (response_callback_.is_null())
       wait_parse_response_callback_ = std::move(callback);
@@ -60,7 +60,7 @@
   }
 
  private:
-  // data_decoder::mojom::WebBundleParser implementation.
+  // web_package::mojom::WebBundleParser implementation.
   void ParseMetadata(ParseMetadataCallback callback) override {
     metadata_callback_ = std::move(callback);
     if (!wait_parse_metadata_callback_.is_null())
@@ -70,7 +70,7 @@
                      uint64_t response_length,
                      ParseResponseCallback callback) override {
     response_callback_ = std::move(callback);
-    parse_response_args_ = data_decoder::mojom::BundleResponseLocation::New(
+    parse_response_args_ = web_package::mojom::BundleResponseLocation::New(
         response_offset, response_length);
     if (!wait_parse_response_callback_.is_null()) {
       std::move(wait_parse_response_callback_)
@@ -78,26 +78,26 @@
     }
   }
 
-  mojo::Receiver<data_decoder::mojom::WebBundleParser> receiver_;
+  mojo::Receiver<web_package::mojom::WebBundleParser> receiver_;
 
   ParseMetadataCallback metadata_callback_;
   ParseResponseCallback response_callback_;
-  data_decoder::mojom::BundleResponseLocationPtr parse_response_args_;
+  web_package::mojom::BundleResponseLocationPtr parse_response_args_;
   base::OnceClosure wait_parse_metadata_callback_;
-  base::OnceCallback<void(data_decoder::mojom::BundleResponseLocationPtr)>
+  base::OnceCallback<void(web_package::mojom::BundleResponseLocationPtr)>
       wait_parse_response_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(MockParser);
 };
 
 class MockParserFactory final
-    : public data_decoder::mojom::WebBundleParserFactory {
+    : public web_package::mojom::WebBundleParserFactory {
  public:
   MockParserFactory() {}
   ~MockParserFactory() override = default;
 
   void AddReceiver(
-      mojo::PendingReceiver<data_decoder::mojom::WebBundleParserFactory>
+      mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>
           receiver) {
     receivers_.Add(this, std::move(receiver));
   }
@@ -109,7 +109,7 @@
       wait_parse_metadata_callback_ = std::move(closure);
   }
 
-  void RunMetadataCallback(data_decoder::mojom::BundleMetadataPtr metadata) {
+  void RunMetadataCallback(web_package::mojom::BundleMetadataPtr metadata) {
     base::RunLoop run_loop;
     WaitUntilParseMetadataCalled(run_loop.QuitClosure());
     run_loop.Run();
@@ -119,13 +119,13 @@
   }
 
   void RunResponseCallback(
-      data_decoder::mojom::BundleResponseLocationPtr expected_parse_args,
-      data_decoder::mojom::BundleResponsePtr response) {
+      web_package::mojom::BundleResponseLocationPtr expected_parse_args,
+      web_package::mojom::BundleResponsePtr response) {
     ASSERT_TRUE(parser_);
     base::RunLoop run_loop;
     parser_->WaitUntilParseResponseCalled(base::BindLambdaForTesting(
         [&run_loop, &expected_parse_args](
-            data_decoder::mojom::BundleResponseLocationPtr parse_args) {
+            web_package::mojom::BundleResponseLocationPtr parse_args) {
           EXPECT_EQ(expected_parse_args->offset, parse_args->offset);
           EXPECT_EQ(expected_parse_args->length, parse_args->length);
           run_loop.Quit();
@@ -135,9 +135,9 @@
   }
 
  private:
-  // data_decoder::mojom::WebBundleParserFactory implementation.
+  // web_package::mojom::WebBundleParserFactory implementation.
   void GetParserForFile(
-      mojo::PendingReceiver<data_decoder::mojom::WebBundleParser> receiver,
+      mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver,
       base::File file) override {
     parser_ = std::make_unique<MockParser>(std::move(receiver));
     if (!wait_parse_metadata_callback_.is_null()) {
@@ -146,14 +146,14 @@
     }
   }
   void GetParserForDataSource(
-      mojo::PendingReceiver<data_decoder::mojom::WebBundleParser> receiver,
-      mojo::PendingRemote<data_decoder::mojom::BundleDataSource> data_source)
+      mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver,
+      mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source)
       override {
     NOTREACHED();
   }
 
   std::unique_ptr<MockParser> parser_;
-  mojo::ReceiverSet<data_decoder::mojom::WebBundleParserFactory> receivers_;
+  mojo::ReceiverSet<web_package::mojom::WebBundleParserFactory> receivers_;
   base::OnceClosure wait_parse_metadata_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(MockParserFactory);
@@ -191,7 +191,7 @@
 
   void ReadAndFullfillMetadata(
       WebBundleReader* reader,
-      data_decoder::mojom::BundleMetadataPtr metadata,
+      web_package::mojom::BundleMetadataPtr metadata,
       WebBundleReader::MetadataCallback callback) override {
     ASSERT_TRUE(factory_);
     DCHECK(reader);
@@ -200,7 +200,7 @@
     reader->ReadMetadata(base::BindOnce(
         [](base::OnceClosure quit_closure,
            WebBundleReader::MetadataCallback callback,
-           data_decoder::mojom::BundleMetadataParseErrorPtr error) {
+           web_package::mojom::BundleMetadataParseErrorPtr error) {
           std::move(callback).Run(std::move(error));
           std::move(quit_closure).Run();
         },
@@ -213,8 +213,8 @@
   void ReadAndFullfillResponse(
       WebBundleReader* reader,
       const network::ResourceRequest& resource_request,
-      data_decoder::mojom::BundleResponseLocationPtr expected_parse_args,
-      data_decoder::mojom::BundleResponsePtr response,
+      web_package::mojom::BundleResponseLocationPtr expected_parse_args,
+      web_package::mojom::BundleResponsePtr response,
       WebBundleReader::ResponseCallback callback) override {
     ASSERT_TRUE(factory_);
     DCHECK(reader);
@@ -225,8 +225,8 @@
         base::BindOnce(
             [](base::OnceClosure quit_closure,
                WebBundleReader::ResponseCallback callback,
-               data_decoder::mojom::BundleResponsePtr response,
-               data_decoder::mojom::BundleResponseParseErrorPtr error) {
+               web_package::mojom::BundleResponsePtr response,
+               web_package::mojom::BundleResponseParseErrorPtr error) {
               std::move(callback).Run(std::move(response), std::move(error));
               std::move(quit_closure).Run();
             },
@@ -238,8 +238,8 @@
   }
 
   void FullfillResponse(
-      data_decoder::mojom::BundleResponseLocationPtr expected_parse_args,
-      data_decoder::mojom::BundleResponsePtr response) override {
+      web_package::mojom::BundleResponseLocationPtr expected_parse_args,
+      web_package::mojom::BundleResponsePtr response) override {
     ASSERT_TRUE(factory_);
 
     factory_->RunResponseCallback(std::move(expected_parse_args),
diff --git a/content/browser/web_package/mock_web_bundle_reader_factory.h b/content/browser/web_package/mock_web_bundle_reader_factory.h
index 27b0ff91..c6a0bca 100644
--- a/content/browser/web_package/mock_web_bundle_reader_factory.h
+++ b/content/browser/web_package/mock_web_bundle_reader_factory.h
@@ -6,8 +6,8 @@
 #define CONTENT_BROWSER_WEB_PACKAGE_MOCK_WEB_BUNDLE_READER_FACTORY_H_
 
 #include "base/macros.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "content/browser/web_package/web_bundle_reader.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 
 namespace content {
 
@@ -32,7 +32,7 @@
   // |metadata| is read.
   virtual void ReadAndFullfillMetadata(
       WebBundleReader* reader,
-      data_decoder::mojom::BundleMetadataPtr metadata,
+      web_package::mojom::BundleMetadataPtr metadata,
       WebBundleReader::MetadataCallback callback) = 0;
 
   // Calls ReadResponse on |reader| with |callback|, verifies that |reader|
@@ -41,15 +41,15 @@
   virtual void ReadAndFullfillResponse(
       WebBundleReader* reader,
       const network::ResourceRequest& resource_request,
-      data_decoder::mojom::BundleResponseLocationPtr expected_parse_args,
-      data_decoder::mojom::BundleResponsePtr response,
+      web_package::mojom::BundleResponseLocationPtr expected_parse_args,
+      web_package::mojom::BundleResponsePtr response,
       WebBundleReader::ResponseCallback callback) = 0;
 
   // Sets up the mocked factory so that the created WebBundleReader instance
   // can read |response| when WebBundleReader::ReadResponse is called.
   virtual void FullfillResponse(
-      data_decoder::mojom::BundleResponseLocationPtr expected_parse_args,
-      data_decoder::mojom::BundleResponsePtr response) = 0;
+      web_package::mojom::BundleResponseLocationPtr expected_parse_args,
+      web_package::mojom::BundleResponsePtr response) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockWebBundleReaderFactory);
diff --git a/content/browser/web_package/web_bundle_blob_data_source.cc b/content/browser/web_package/web_bundle_blob_data_source.cc
index 05e98bd..0f6a455 100644
--- a/content/browser/web_package/web_bundle_blob_data_source.cc
+++ b/content/browser/web_package/web_bundle_blob_data_source.cc
@@ -43,11 +43,10 @@
   DISALLOW_COPY_AND_ASSIGN(MojoBlobReaderDelegate);
 };
 
-void OnReadComplete(
-    data_decoder::mojom::BundleDataSource::ReadCallback callback,
-    std::unique_ptr<storage::BlobReader> blob_reader,
-    scoped_refptr<net::IOBufferWithSize> io_buf,
-    int bytes_read) {
+void OnReadComplete(web_package::mojom::BundleDataSource::ReadCallback callback,
+                    std::unique_ptr<storage::BlobReader> blob_reader,
+                    scoped_refptr<net::IOBufferWithSize> io_buf,
+                    int bytes_read) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (bytes_read != io_buf->size()) {
     std::move(callback).Run(base::nullopt);
@@ -62,7 +61,7 @@
 void OnCalculateSizeComplete(
     uint64_t offset,
     uint64_t length,
-    data_decoder::mojom::BundleDataSource::ReadCallback callback,
+    web_package::mojom::BundleDataSource::ReadCallback callback,
     std::unique_ptr<storage::BlobReader> blob_reader,
     int net_error) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -129,7 +128,7 @@
 }
 
 void WebBundleBlobDataSource::AddReceiver(
-    mojo::PendingReceiver<data_decoder::mojom::BundleDataSource>
+    mojo::PendingReceiver<web_package::mojom::BundleDataSource>
         pending_receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   WaitForCore(base::BindOnce(&WebBundleBlobDataSource::AddReceiverImpl,
@@ -138,7 +137,7 @@
 }
 
 void WebBundleBlobDataSource::AddReceiverImpl(
-    mojo::PendingReceiver<data_decoder::mojom::BundleDataSource>
+    mojo::PendingReceiver<web_package::mojom::BundleDataSource>
         pending_receiver) {
   if (!core_)
     return;
@@ -273,7 +272,7 @@
 }
 
 void WebBundleBlobDataSource::BlobDataSourceCore::AddReceiver(
-    mojo::PendingReceiver<data_decoder::mojom::BundleDataSource>
+    mojo::PendingReceiver<web_package::mojom::BundleDataSource>
         pending_receiver) {
   receivers_.Add(this, std::move(pending_receiver));
 }
diff --git a/content/browser/web_package/web_bundle_blob_data_source.h b/content/browser/web_package/web_bundle_blob_data_source.h
index a68c3c4..fef93b30 100644
--- a/content/browser/web_package/web_bundle_blob_data_source.h
+++ b/content/browser/web_package/web_bundle_blob_data_source.h
@@ -9,13 +9,13 @@
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_context.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/base/net_errors.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 
 namespace storage {
@@ -40,7 +40,7 @@
       BrowserContext::BlobContextGetter blob_context_getter);
   ~WebBundleBlobDataSource();
 
-  void AddReceiver(mojo::PendingReceiver<data_decoder::mojom::BundleDataSource>
+  void AddReceiver(mojo::PendingReceiver<web_package::mojom::BundleDataSource>
                        pending_receiver);
   void ReadToDataPipe(uint64_t offset,
                       uint64_t length,
@@ -49,7 +49,7 @@
 
  private:
   // This class lives on the IO thread.
-  class BlobDataSourceCore : public data_decoder::mojom::BundleDataSource {
+  class BlobDataSourceCore : public web_package::mojom::BundleDataSource {
    public:
     BlobDataSourceCore(uint64_t length_hint,
                        network::mojom::URLLoaderClientEndpointsPtr endpoints,
@@ -57,9 +57,8 @@
     ~BlobDataSourceCore() override;
 
     void Start(mojo::ScopedDataPipeConsumerHandle outer_response_body);
-    void AddReceiver(
-        mojo::PendingReceiver<data_decoder::mojom::BundleDataSource>
-            pending_receiver);
+    void AddReceiver(mojo::PendingReceiver<web_package::mojom::BundleDataSource>
+                         pending_receiver);
 
     void ReadToDataPipe(uint64_t offset,
                         uint64_t length,
@@ -69,7 +68,7 @@
     base::WeakPtr<BlobDataSourceCore> GetWeakPtr();
 
    private:
-    // Implements data_decoder::mojom::BundleDataSource.
+    // Implements web_package::mojom::BundleDataSource.
     void Read(uint64_t offset, uint64_t length, ReadCallback callback) override;
 
     void StreamingBlobDone(storage::BlobBuilderFromStream* builder,
@@ -88,7 +87,7 @@
     const uint64_t length_hint_;
     // Used to keep the ongoing network request.
     network::mojom::URLLoaderClientEndpointsPtr endpoints_;
-    mojo::ReceiverSet<data_decoder::mojom::BundleDataSource> receivers_;
+    mojo::ReceiverSet<web_package::mojom::BundleDataSource> receivers_;
     std::unique_ptr<storage::BlobBuilderFromStream> blob_builder_from_stream_;
 
     std::unique_ptr<storage::BlobDataHandle> blob_;
@@ -123,7 +122,7 @@
                        std::unique_ptr<BlobDataSourceCore> core);
 
   void AddReceiverImpl(
-      mojo::PendingReceiver<data_decoder::mojom::BundleDataSource>
+      mojo::PendingReceiver<web_package::mojom::BundleDataSource>
           pending_receiver);
 
   // Used to call BlobDataSourceCore's method on the IO thread.
diff --git a/content/browser/web_package/web_bundle_blob_data_source_unittest.cc b/content/browser/web_package/web_bundle_blob_data_source_unittest.cc
index c63be59..e2b53cdb 100644
--- a/content/browser/web_package/web_bundle_blob_data_source_unittest.cc
+++ b/content/browser/web_package/web_bundle_blob_data_source_unittest.cc
@@ -48,7 +48,7 @@
 
   std::unique_ptr<WebBundleBlobDataSource> CreateTestDataSource(
       const std::string& test_data,
-      mojo::Remote<data_decoder::mojom::BundleDataSource>* remote_source,
+      mojo::Remote<web_package::mojom::BundleDataSource>* remote_source,
       base::Optional<int64_t> content_length = base::nullopt) {
     mojo::ScopedDataPipeProducerHandle producer;
     mojo::ScopedDataPipeConsumerHandle consumer;
@@ -81,7 +81,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, Read) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source);
 
   base::RunLoop run_loop;
@@ -106,7 +106,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, Read_EndOfSourceReached) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source);
 
   base::RunLoop run_loop;
@@ -130,7 +130,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, Read_OutOfRangeError) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source);
 
   base::RunLoop run_loop;
@@ -151,7 +151,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, Read_ContentLengthTooSmall) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source, kData.size() - 1);
 
   base::RunLoop run_loop;
@@ -174,7 +174,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, Read_ContentLengthTooLarge) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source, kData.size() + 1);
 
   base::RunLoop run_loop;
@@ -199,7 +199,7 @@
   std::string content = "Test Data";
   // Make the content larger than the disk space.
   content.resize(kTestBlobStorageMaxDiskSpace + 1, ' ');
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(content, &remote_source);
 
   base::RunLoop run_loop;
@@ -220,7 +220,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, ReadToDataPipe) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source);
 
   base::RunLoop run_loop;
@@ -250,7 +250,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, ReadToDataPipe_EndOfSourceReached) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source);
 
   base::RunLoop run_loop;
@@ -279,7 +279,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, ReadToDataPipe_OutOfRangeError) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source);
 
   base::RunLoop run_loop;
@@ -304,7 +304,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, ReadToDataPipe_ContentLengthTooSmall) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source, kData.size() - 1);
 
   base::RunLoop run_loop;
@@ -333,7 +333,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, ReadToDataPipe_ContentLengthTooLarge) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source, kData.size() + 1);
 
   base::RunLoop run_loop;
@@ -364,7 +364,7 @@
   std::string content = "Test Data";
   // Make the content larger than the disk space.
   content.resize(kTestBlobStorageMaxDiskSpace + 1, ' ');
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(content, &remote_source);
 
   base::RunLoop run_loop;
@@ -389,7 +389,7 @@
 
 TEST_F(WebBundleBlobDataSourceTest, ReadToDataPipe_Destructed) {
   const std::string kData = "Test Data";
-  mojo::Remote<data_decoder::mojom::BundleDataSource> remote_source;
+  mojo::Remote<web_package::mojom::BundleDataSource> remote_source;
   auto source = CreateTestDataSource(kData, &remote_source);
 
   base::RunLoop run_loop;
diff --git a/content/browser/web_package/web_bundle_browsertest.cc b/content/browser/web_package/web_bundle_browsertest.cc
index e6417c5..29ffeb5 100644
--- a/content/browser/web_package/web_bundle_browsertest.cc
+++ b/content/browser/web_package/web_bundle_browsertest.cc
@@ -16,6 +16,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
+#include "components/web_package/test_support/web_bundle_builder.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -39,7 +40,6 @@
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
-#include "services/data_decoder/public/cpp/test_support/web_bundle_builder.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/content_uri_utils.h"
@@ -142,13 +142,13 @@
 
 class MockParserFactory;
 
-class MockParser final : public data_decoder::mojom::WebBundleParser {
+class MockParser final : public web_package::mojom::WebBundleParser {
  public:
-  using Index = base::flat_map<GURL, data_decoder::mojom::BundleIndexValuePtr>;
+  using Index = base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr>;
 
   MockParser(
       MockParserFactory* factory,
-      mojo::PendingReceiver<data_decoder::mojom::WebBundleParser> receiver,
+      mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver,
       const Index& index,
       const GURL& primary_url,
       bool simulate_parse_metadata_crash,
@@ -162,14 +162,14 @@
   ~MockParser() override = default;
 
  private:
-  // data_decoder::mojom::WebBundleParser implementation.
+  // web_package::mojom::WebBundleParser implementation.
   void ParseMetadata(ParseMetadataCallback callback) override;
   void ParseResponse(uint64_t response_offset,
                      uint64_t response_length,
                      ParseResponseCallback callback) override;
 
   MockParserFactory* factory_;
-  mojo::Receiver<data_decoder::mojom::WebBundleParser> receiver_;
+  mojo::Receiver<web_package::mojom::WebBundleParser> receiver_;
   const Index& index_;
   const GURL primary_url_;
   const bool simulate_parse_metadata_crash_;
@@ -179,7 +179,7 @@
 };
 
 class MockParserFactory final
-    : public data_decoder::mojom::WebBundleParserFactory {
+    : public web_package::mojom::WebBundleParserFactory {
  public:
   MockParserFactory(std::vector<GURL> urls,
                     const base::FilePath& response_body_file)
@@ -189,10 +189,10 @@
     EXPECT_TRUE(
         base::GetFileSize(response_body_file, &response_body_file_size));
     for (const auto& url : urls) {
-      data_decoder::mojom::BundleIndexValuePtr item =
-          data_decoder::mojom::BundleIndexValue::New();
+      web_package::mojom::BundleIndexValuePtr item =
+          web_package::mojom::BundleIndexValue::New();
       item->response_locations.push_back(
-          data_decoder::mojom::BundleResponseLocation::New(
+          web_package::mojom::BundleResponseLocation::New(
               0u, response_body_file_size));
       index_.insert({url, std::move(item)});
     }
@@ -206,10 +206,10 @@
       : primary_url_(items[0].first) {
     uint64_t offset = 0;
     for (const auto& item : items) {
-      data_decoder::mojom::BundleIndexValuePtr index_value =
-          data_decoder::mojom::BundleIndexValue::New();
+      web_package::mojom::BundleIndexValuePtr index_value =
+          web_package::mojom::BundleIndexValue::New();
       index_value->response_locations.push_back(
-          data_decoder::mojom::BundleResponseLocation::New(
+          web_package::mojom::BundleResponseLocation::New(
               offset, item.second.length()));
       offset += item.second.length();
       index_.insert({item.first, std::move(index_value)});
@@ -228,14 +228,14 @@
 
  private:
   void BindWebBundleParserFactory(
-      mojo::PendingReceiver<data_decoder::mojom::WebBundleParserFactory>
+      mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>
           receiver) {
     receivers_.Add(this, std::move(receiver));
   }
 
-  // data_decoder::mojom::WebBundleParserFactory implementation.
+  // web_package::mojom::WebBundleParserFactory implementation.
   void GetParserForFile(
-      mojo::PendingReceiver<data_decoder::mojom::WebBundleParser> receiver,
+      mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver,
       base::File file) override {
     {
       base::ScopedAllowBlockingForTesting allow_blocking;
@@ -249,8 +249,8 @@
   }
 
   void GetParserForDataSource(
-      mojo::PendingReceiver<data_decoder::mojom::WebBundleParser> receiver,
-      mojo::PendingRemote<data_decoder::mojom::BundleDataSource> data_source)
+      mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver,
+      mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source)
       override {
     DCHECK(!parser_);
     parser_ = std::make_unique<MockParser>(
@@ -260,12 +260,12 @@
   }
 
   data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
-  mojo::ReceiverSet<data_decoder::mojom::WebBundleParserFactory> receivers_;
+  mojo::ReceiverSet<web_package::mojom::WebBundleParserFactory> receivers_;
   bool simulate_parse_metadata_crash_ = false;
   bool simulate_parse_response_crash_ = false;
   std::unique_ptr<MockParser> parser_;
   int parser_creation_count_ = 0;
-  base::flat_map<GURL, data_decoder::mojom::BundleIndexValuePtr> index_;
+  base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> index_;
   const GURL primary_url_;
 
   DISALLOW_COPY_AND_ASSIGN(MockParserFactory);
@@ -277,13 +277,13 @@
     return;
   }
 
-  base::flat_map<GURL, data_decoder::mojom::BundleIndexValuePtr> items;
+  base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> items;
   for (const auto& item : index_) {
     items.insert({item.first, item.second.Clone()});
   }
 
-  data_decoder::mojom::BundleMetadataPtr metadata =
-      data_decoder::mojom::BundleMetadata::New();
+  web_package::mojom::BundleMetadataPtr metadata =
+      web_package::mojom::BundleMetadata::New();
   metadata->primary_url = primary_url_;
   metadata->requests = std::move(items);
 
@@ -297,8 +297,8 @@
     factory_->SimulateParserDisconnect();
     return;
   }
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   response->response_code = 200;
   response->response_headers.insert({"content-type", "text/html"});
   response->payload_offset = response_offset;
@@ -460,7 +460,7 @@
 }
 
 std::string CreateSimpleWebBundle(const GURL& primary_url) {
-  data_decoder::test::WebBundleBuilder builder(primary_url.spec(), "");
+  web_package::test::WebBundleBuilder builder(primary_url.spec(), "");
   builder.AddExchange(primary_url.spec(),
                       {{":status", "200"}, {"content-type", "text/html"}},
                       "<title>Ready</title>");
@@ -468,7 +468,7 @@
   return std::string(bundle.begin(), bundle.end());
 }
 
-void AddHtmlFile(data_decoder::test::WebBundleBuilder* builder,
+void AddHtmlFile(web_package::test::WebBundleBuilder* builder,
                  const GURL& base_url,
                  const std::string& path,
                  const std::string& content) {
@@ -477,7 +477,7 @@
                        content);
 }
 
-void AddScriptFile(data_decoder::test::WebBundleBuilder* builder,
+void AddScriptFile(web_package::test::WebBundleBuilder* builder,
                    const GURL& base_url,
                    const std::string& path,
                    const std::string& content) {
@@ -489,7 +489,7 @@
 
 std::string CreatePathTestWebBundle(const GURL& base_url) {
   const std::string primary_url_path = "/web_bundle/path_test/in_scope/";
-  data_decoder::test::WebBundleBuilder builder(
+  web_package::test::WebBundleBuilder builder(
       base_url.Resolve(primary_url_path).spec(), "");
   AddHtmlFile(&builder, base_url, primary_url_path, "<title>Ready</title>");
   AddHtmlFile(
@@ -591,7 +591,7 @@
   *primary_url_origin = primary_server->GetURL("/");
   *third_party_origin = third_party_server->GetURL("/");
 
-  data_decoder::test::WebBundleBuilder builder(
+  web_package::test::WebBundleBuilder builder(
       primary_url_origin->Resolve("/top").spec(), "");
   AddHtmlFile(&builder, *primary_url_origin, "/top", R"(
     <script>
@@ -718,7 +718,7 @@
 }
 
 void AddHtmlAndScriptForNavigationTest(
-    data_decoder::test::WebBundleBuilder* builder,
+    web_package::test::WebBundleBuilder* builder,
     const GURL& base_url,
     const std::string& path,
     const std::string& additional_html) {
@@ -820,7 +820,7 @@
                                 GURL* url_origin,
                                 std::string* web_bundle_content) {
   SetUpNavigationTestServer(server, url_origin);
-  data_decoder::test::WebBundleBuilder builder(
+  web_package::test::WebBundleBuilder builder(
       url_origin->Resolve("/top-page/").spec(), "");
   for (const auto& path : pathes)
     AddHtmlAndScriptForNavigationTest(&builder, *url_origin, path, "");
@@ -1193,7 +1193,7 @@
                                GURL* url_origin,
                                std::string* web_bundle_content) {
   SetUpNavigationTestServer(server, url_origin);
-  data_decoder::test::WebBundleBuilder builder(
+  web_package::test::WebBundleBuilder builder(
       url_origin->Resolve("/top-page/").spec(), "");
   const std::vector<std::string> pathes = {"/top-page/", "/1-page/",
                                            "/2-page/"};
@@ -2338,7 +2338,7 @@
   const GURL script_url =
       embedded_test_server()->GetURL("/web_bundle/script.js");
 
-  data_decoder::test::WebBundleBuilder builder(primary_url.spec(), "");
+  web_package::test::WebBundleBuilder builder(primary_url.spec(), "");
   builder.AddExchange(primary_url.spec(),
                       {{":status", "200"}, {"content-type", "text/html"}},
                       "<script src=\"script.js\"></script>");
@@ -2431,7 +2431,7 @@
   const GURL primary_url = embedded_test_server()->GetURL(primary_url_path);
   const GURL inner_url =
       embedded_test_server()->GetURL("/web_bundle/inner.html");
-  data_decoder::test::WebBundleBuilder builder(primary_url.spec(), "");
+  web_package::test::WebBundleBuilder builder(primary_url.spec(), "");
   builder.AddExchange(inner_url.spec(),
                       {{":status", "200"}, {"content-type", "text/html"}},
                       "<title>Ready</title>");
@@ -2843,7 +2843,7 @@
   RegisterRequestHandlerForSubPageTest(embedded_test_server(), "");
   ASSERT_TRUE(embedded_test_server()->Start());
   const GURL origin = embedded_test_server()->GetURL("/");
-  data_decoder::test::WebBundleBuilder builder(
+  web_package::test::WebBundleBuilder builder(
       origin.Resolve(primary_url_path).spec(), "");
   AddHtmlFile(&builder, origin, primary_url_path, R"(
     <script>
diff --git a/content/browser/web_package/web_bundle_interceptor_for_file.cc b/content/browser/web_package/web_bundle_interceptor_for_file.cc
index f8c4191..9a14632 100644
--- a/content/browser/web_package/web_bundle_interceptor_for_file.cc
+++ b/content/browser/web_package/web_bundle_interceptor_for_file.cc
@@ -74,7 +74,7 @@
 
 void WebBundleInterceptorForFile::OnMetadataReady(
     const network::ResourceRequest& request,
-    data_decoder::mojom::BundleMetadataParseErrorPtr metadata_error) {
+    web_package::mojom::BundleMetadataParseErrorPtr metadata_error) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (metadata_error) {
     web_bundle_utils::CompleteWithInvalidWebBundleError(
diff --git a/content/browser/web_package/web_bundle_interceptor_for_file.h b/content/browser/web_package/web_bundle_interceptor_for_file.h
index b08b86e..3d8c5b09 100644
--- a/content/browser/web_package/web_bundle_interceptor_for_file.h
+++ b/content/browser/web_package/web_bundle_interceptor_for_file.h
@@ -7,10 +7,10 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/browser/web_package/web_bundle_utils.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "url/gurl.h"
 
@@ -70,7 +70,7 @@
 
   void OnMetadataReady(
       const network::ResourceRequest& request,
-      data_decoder::mojom::BundleMetadataParseErrorPtr metadata_error);
+      web_package::mojom::BundleMetadataParseErrorPtr metadata_error);
 
   void StartResponse(
       const network::ResourceRequest& resource_request,
diff --git a/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_file_or_from_trustable_file.cc b/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_file_or_from_trustable_file.cc
index 8cb9a03d..d2db7221 100644
--- a/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_file_or_from_trustable_file.cc
+++ b/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_file_or_from_trustable_file.cc
@@ -72,7 +72,7 @@
 }
 
 void WebBundleInterceptorForHistoryNavigationFromFileOrFromTrustableFile::
-    OnMetadataReady(data_decoder::mojom::BundleMetadataParseErrorPtr error) {
+    OnMetadataReady(web_package::mojom::BundleMetadataParseErrorPtr error) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!url_loader_factory_);
 
diff --git a/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_file_or_from_trustable_file.h b/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_file_or_from_trustable_file.h
index eaae458..a692f965 100644
--- a/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_file_or_from_trustable_file.h
+++ b/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_file_or_from_trustable_file.h
@@ -6,11 +6,11 @@
 #define CONTENT_BROWSER_WEB_PACKAGE_WEB_BUNDLE_INTERCEPTOR_FOR_HISTORY_NAVIGATION_FROM_FILE_OR_FROM_TRUSTABLE_FILE_H_
 
 #include "base/memory/weak_ptr.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "content/browser/web_package/web_bundle_interceptor_for_history_navigation.h"
 #include "content/browser/web_package/web_bundle_utils.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 
@@ -62,7 +62,7 @@
       mojo::PendingReceiver<network::mojom::URLLoader> receiver,
       mojo::PendingRemote<network::mojom::URLLoaderClient> client);
 
-  void OnMetadataReady(data_decoder::mojom::BundleMetadataParseErrorPtr error);
+  void OnMetadataReady(web_package::mojom::BundleMetadataParseErrorPtr error);
 
   scoped_refptr<WebBundleReader> reader_;
 
@@ -70,7 +70,7 @@
   mojo::PendingReceiver<network::mojom::URLLoader> pending_receiver_;
   mojo::PendingRemote<network::mojom::URLLoaderClient> pending_client_;
 
-  data_decoder::mojom::BundleMetadataParseErrorPtr metadata_error_;
+  web_package::mojom::BundleMetadataParseErrorPtr metadata_error_;
 
   base::WeakPtrFactory<
       WebBundleInterceptorForHistoryNavigationFromFileOrFromTrustableFile>
diff --git a/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_network.cc b/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_network.cc
index 9dfb5af1..4f3fcdcf 100644
--- a/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_network.cc
+++ b/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_network.cc
@@ -127,7 +127,7 @@
 
 void WebBundleInterceptorForHistoryNavigationFromNetwork::OnMetadataReady(
     network::ResourceRequest request,
-    data_decoder::mojom::BundleMetadataParseErrorPtr error) {
+    web_package::mojom::BundleMetadataParseErrorPtr error) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(State::kWebBundleRecieved, state_);
   state_ = State::kMetadataReady;
diff --git a/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_network.h b/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_network.h
index 9dfcb1cf..16aa29c 100644
--- a/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_network.h
+++ b/content/browser/web_package/web_bundle_interceptor_for_history_navigation_from_network.h
@@ -6,12 +6,12 @@
 #define CONTENT_BROWSER_WEB_PACKAGE_WEB_BUNDLE_INTERCEPTOR_FOR_HISTORY_NAVIGATION_FROM_NETWORK_H_
 
 #include "base/memory/weak_ptr.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "content/browser/web_package/web_bundle_interceptor_for_history_navigation.h"
 #include "content/browser/web_package/web_bundle_url_loader_factory.h"
 #include "content/browser/web_package/web_bundle_utils.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 
 class GURL;
@@ -91,7 +91,7 @@
       bool* will_return_and_handle_unsafe_redirect) override;
 
   void OnMetadataReady(network::ResourceRequest request,
-                       data_decoder::mojom::BundleMetadataParseErrorPtr error);
+                       web_package::mojom::BundleMetadataParseErrorPtr error);
 
   void StartRedirectResponse(
       const network::ResourceRequest& resource_request,
diff --git a/content/browser/web_package/web_bundle_interceptor_for_network.cc b/content/browser/web_package/web_bundle_interceptor_for_network.cc
index 9db14a3..c3f7461 100644
--- a/content/browser/web_package/web_bundle_interceptor_for_network.cc
+++ b/content/browser/web_package/web_bundle_interceptor_for_network.cc
@@ -102,7 +102,7 @@
 
 void WebBundleInterceptorForNetwork::OnMetadataReady(
     network::ResourceRequest request,
-    data_decoder::mojom::BundleMetadataParseErrorPtr error) {
+    web_package::mojom::BundleMetadataParseErrorPtr error) {
   if (error) {
     web_bundle_utils::CompleteWithInvalidWebBundleError(
         std::move(forwarding_client_), frame_tree_node_id_,
diff --git a/content/browser/web_package/web_bundle_interceptor_for_network.h b/content/browser/web_package/web_bundle_interceptor_for_network.h
index fd4ead0..ee9fab7f 100644
--- a/content/browser/web_package/web_bundle_interceptor_for_network.h
+++ b/content/browser/web_package/web_bundle_interceptor_for_network.h
@@ -68,7 +68,7 @@
       bool* will_return_and_handle_unsafe_redirect) override;
 
   void OnMetadataReady(network::ResourceRequest request,
-                       data_decoder::mojom::BundleMetadataParseErrorPtr error);
+                       web_package::mojom::BundleMetadataParseErrorPtr error);
 
   void StartResponse(
       const network::ResourceRequest& resource_request,
diff --git a/content/browser/web_package/web_bundle_interceptor_for_trustable_file.cc b/content/browser/web_package/web_bundle_interceptor_for_trustable_file.cc
index 8ae3300..71c4a3c 100644
--- a/content/browser/web_package/web_bundle_interceptor_for_trustable_file.cc
+++ b/content/browser/web_package/web_bundle_interceptor_for_trustable_file.cc
@@ -88,7 +88,7 @@
 }
 
 void WebBundleInterceptorForTrustableFile::OnMetadataReady(
-    data_decoder::mojom::BundleMetadataParseErrorPtr error) {
+    web_package::mojom::BundleMetadataParseErrorPtr error) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!url_loader_factory_);
 
diff --git a/content/browser/web_package/web_bundle_interceptor_for_trustable_file.h b/content/browser/web_package/web_bundle_interceptor_for_trustable_file.h
index 01116b0..a495e973 100644
--- a/content/browser/web_package/web_bundle_interceptor_for_trustable_file.h
+++ b/content/browser/web_package/web_bundle_interceptor_for_trustable_file.h
@@ -60,7 +60,7 @@
       mojo::PendingReceiver<network::mojom::URLLoader> receiver,
       mojo::PendingRemote<network::mojom::URLLoaderClient> client);
 
-  void OnMetadataReady(data_decoder::mojom::BundleMetadataParseErrorPtr error);
+  void OnMetadataReady(web_package::mojom::BundleMetadataParseErrorPtr error);
 
   std::unique_ptr<WebBundleSource> source_;
   scoped_refptr<WebBundleReader> reader_;
@@ -74,7 +74,7 @@
   std::unique_ptr<WebBundleURLLoaderFactory> url_loader_factory_;
 
   GURL primary_url_;
-  data_decoder::mojom::BundleMetadataParseErrorPtr metadata_error_;
+  web_package::mojom::BundleMetadataParseErrorPtr metadata_error_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/content/browser/web_package/web_bundle_reader.cc b/content/browser/web_package/web_bundle_reader.cc
index 75051285..d4bde56c 100644
--- a/content/browser/web_package/web_bundle_reader.cc
+++ b/content/browser/web_package/web_bundle_reader.cc
@@ -149,7 +149,7 @@
     : source_(std::move(source)),
       parser_(std::make_unique<data_decoder::SafeWebBundleParser>()) {
   DCHECK(source_->is_network());
-  mojo::PendingRemote<data_decoder::mojom::BundleDataSource> pending_remote;
+  mojo::PendingRemote<web_package::mojom::BundleDataSource> pending_remote;
   blob_data_source_ = std::make_unique<WebBundleBlobDataSource>(
       content_length, std::move(outer_response_body), std::move(endpoints),
       std::move(blob_context_getter));
@@ -191,12 +191,12 @@
         FROM_HERE,
         base::BindOnce(
             std::move(callback), nullptr,
-            data_decoder::mojom::BundleResponseParseError::New(
-                data_decoder::mojom::BundleParseErrorType::kParserInternalError,
+            web_package::mojom::BundleResponseParseError::New(
+                web_package::mojom::BundleParseErrorType::kParserInternalError,
                 "Not found in Web Bundle file.")));
     return;
   }
-  const data_decoder::mojom::BundleIndexValuePtr& entry = it->second;
+  const web_package::mojom::BundleIndexValuePtr& entry = it->second;
 
   size_t response_index = 0;
   if (!entry->variants_value.empty()) {
@@ -209,8 +209,8 @@
           FROM_HERE,
           base::BindOnce(
               std::move(callback), nullptr,
-              data_decoder::mojom::BundleResponseParseError::New(
-                  data_decoder::mojom::BundleParseErrorType::
+              web_package::mojom::BundleResponseParseError::New(
+                  web_package::mojom::BundleParseErrorType::
                       kParserInternalError,
                   "Cannot find a response that matches request headers.")));
       return;
@@ -232,7 +232,7 @@
 }
 
 void WebBundleReader::ReadResponseInternal(
-    data_decoder::mojom::BundleResponseLocationPtr location,
+    web_package::mojom::BundleResponseLocationPtr location,
     ResponseCallback callback) {
   parser_->ParseResponse(
       location->offset, location->length,
@@ -251,7 +251,7 @@
     return;
   }
   DCHECK(source_->is_network());
-  mojo::PendingRemote<data_decoder::mojom::BundleDataSource> pending_remote;
+  mojo::PendingRemote<web_package::mojom::BundleDataSource> pending_remote;
   blob_data_source_->AddReceiver(
       pending_remote.InitWithNewPipeAndPassReceiver());
   parser_->OpenDataSource(std::move(pending_remote));
@@ -281,8 +281,8 @@
       base::ThreadPool::PostTask(
           FROM_HERE,
           base::BindOnce(std::move(pair.second), nullptr,
-                         data_decoder::mojom::BundleResponseParseError::New(
-                             data_decoder::mojom::BundleParseErrorType::
+                         web_package::mojom::BundleResponseParseError::New(
+                             web_package::mojom::BundleParseErrorType::
                                  kParserInternalError,
                              *error)));
     }
@@ -297,7 +297,7 @@
 }
 
 void WebBundleReader::ReadResponseBody(
-    data_decoder::mojom::BundleResponsePtr response,
+    web_package::mojom::BundleResponsePtr response,
     mojo::ScopedDataPipeProducerHandle producer_handle,
     BodyCompletionCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -355,8 +355,8 @@
         FROM_HERE,
         base::BindOnce(
             std::move(callback),
-            data_decoder::mojom::BundleMetadataParseError::New(
-                data_decoder::mojom::BundleParseErrorType::kParserInternalError,
+            web_package::mojom::BundleMetadataParseError::New(
+                web_package::mojom::BundleParseErrorType::kParserInternalError,
                 GURL() /* fallback_url */, base::File::ErrorToString(error))));
   } else {
     parser_->ParseMetadata(base::BindOnce(&WebBundleReader::OnMetadataParsed,
@@ -367,8 +367,8 @@
 
 void WebBundleReader::OnMetadataParsed(
     MetadataCallback callback,
-    data_decoder::mojom::BundleMetadataPtr metadata,
-    data_decoder::mojom::BundleMetadataParseErrorPtr error) {
+    web_package::mojom::BundleMetadataPtr metadata,
+    web_package::mojom::BundleMetadataParseErrorPtr error) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(state_, State::kInitial);
 
@@ -385,8 +385,8 @@
 
 void WebBundleReader::OnResponseParsed(
     ResponseCallback callback,
-    data_decoder::mojom::BundleResponsePtr response,
-    data_decoder::mojom::BundleResponseParseErrorPtr error) {
+    web_package::mojom::BundleResponsePtr response,
+    web_package::mojom::BundleResponseParseErrorPtr error) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_NE(state_, State::kInitial);
 
diff --git a/content/browser/web_package/web_bundle_reader.h b/content/browser/web_package/web_bundle_reader.h
index f97498d..7e438a6 100644
--- a/content/browser/web_package/web_bundle_reader.h
+++ b/content/browser/web_package/web_bundle_reader.h
@@ -51,15 +51,15 @@
   // |error| is set only on failures.
   // Other methods below are only available after this |callback| invocation.
   using MetadataCallback = base::OnceCallback<void(
-      data_decoder::mojom::BundleMetadataParseErrorPtr error)>;
+      web_package::mojom::BundleMetadataParseErrorPtr error)>;
   void ReadMetadata(MetadataCallback callback);
 
-  // Gets data_decoder::mojom::BundleResponsePtr for the given |url| that
+  // Gets web_package::mojom::BundleResponsePtr for the given |url| that
   // contains response headers and range information for its body.
   // Should be called after ReadMetadata finishes.
-  using ResponseCallback = base::OnceCallback<void(
-      data_decoder::mojom::BundleResponsePtr,
-      data_decoder::mojom::BundleResponseParseErrorPtr)>;
+  using ResponseCallback =
+      base::OnceCallback<void(web_package::mojom::BundleResponsePtr,
+                              web_package::mojom::BundleResponseParseErrorPtr)>;
   void ReadResponse(const network::ResourceRequest& resource_request,
                     const std::string& accept_langs,
                     ResponseCallback callback);
@@ -68,7 +68,7 @@
   // ReadResponse above beforehand. Body will be written into |producer_handle|.
   // After all body data is written, |callback| will be invoked.
   using BodyCompletionCallback = base::OnceCallback<void(net::Error net_error)>;
-  void ReadResponseBody(data_decoder::mojom::BundleResponsePtr response,
+  void ReadResponseBody(web_package::mojom::BundleResponsePtr response,
                         mojo::ScopedDataPipeProducerHandle producer_handle,
                         BodyCompletionCallback callback);
 
@@ -123,15 +123,15 @@
 
   void ReadMetadataInternal(MetadataCallback callback, base::File file);
   void ReadResponseInternal(
-      data_decoder::mojom::BundleResponseLocationPtr location,
+      web_package::mojom::BundleResponseLocationPtr location,
       ResponseCallback callback);
 
   void OnMetadataParsed(MetadataCallback callback,
-                        data_decoder::mojom::BundleMetadataPtr metadata,
-                        data_decoder::mojom::BundleMetadataParseErrorPtr error);
+                        web_package::mojom::BundleMetadataPtr metadata,
+                        web_package::mojom::BundleMetadataParseErrorPtr error);
   void OnResponseParsed(ResponseCallback callback,
-                        data_decoder::mojom::BundleResponsePtr response,
-                        data_decoder::mojom::BundleResponseParseErrorPtr error);
+                        web_package::mojom::BundleResponsePtr response,
+                        web_package::mojom::BundleResponseParseErrorPtr error);
   void OnParserDisconnected();
   void Reconnect();
   void ReconnectForFile(base::File file);
@@ -149,9 +149,9 @@
   std::unique_ptr<WebBundleBlobDataSource> blob_data_source_;
 
   GURL primary_url_;
-  base::flat_map<GURL, data_decoder::mojom::BundleIndexValuePtr> entries_;
+  base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> entries_;
   // Accumulates ReadResponse() requests while the parser is disconnected.
-  std::vector<std::pair<data_decoder::mojom::BundleResponseLocationPtr,
+  std::vector<std::pair<web_package::mojom::BundleResponseLocationPtr,
                         ResponseCallback>>
       pending_read_responses_;
 
diff --git a/content/browser/web_package/web_bundle_reader_unittest.cc b/content/browser/web_package/web_bundle_reader_unittest.cc
index d301830..f0b64cf 100644
--- a/content/browser/web_package/web_bundle_reader_unittest.cc
+++ b/content/browser/web_package/web_bundle_reader_unittest.cc
@@ -12,10 +12,10 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/optional.h"
 #include "base/test/task_environment.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "content/browser/web_package/mock_web_bundle_reader_factory.h"
 #include "content/browser/web_package/web_bundle_source.h"
 #include "mojo/public/c/system/data_pipe.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -37,24 +37,24 @@
   }
 
   void ReadMetadata() {
-    base::flat_map<GURL, data_decoder::mojom::BundleIndexValuePtr> items;
-    data_decoder::mojom::BundleIndexValuePtr item =
-        data_decoder::mojom::BundleIndexValue::New();
+    base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> items;
+    web_package::mojom::BundleIndexValuePtr item =
+        web_package::mojom::BundleIndexValue::New();
     item->variants_value = "Accept;text/html;image/png";
     item->response_locations.push_back(
-        data_decoder::mojom::BundleResponseLocation::New(573u, 765u));
+        web_package::mojom::BundleResponseLocation::New(573u, 765u));
     item->response_locations.push_back(
-        data_decoder::mojom::BundleResponseLocation::New(333u, 222u));
+        web_package::mojom::BundleResponseLocation::New(333u, 222u));
     items.insert({primary_url_, std::move(item)});
 
-    data_decoder::mojom::BundleMetadataPtr metadata =
-        data_decoder::mojom::BundleMetadata::New();
+    web_package::mojom::BundleMetadataPtr metadata =
+        web_package::mojom::BundleMetadata::New();
     metadata->primary_url = primary_url_;
     metadata->requests = std::move(items);
     reader_factory_->ReadAndFullfillMetadata(
         reader_.get(), std::move(metadata),
         base::BindOnce(
-            [](data_decoder::mojom::BundleMetadataParseErrorPtr error) {
+            [](web_package::mojom::BundleMetadataParseErrorPtr error) {
               EXPECT_FALSE(error);
             }));
   }
@@ -85,8 +85,8 @@
   ReadMetadata();
   ASSERT_TRUE(GetReader()->HasEntry(GetPrimaryURL()));
 
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   response->response_code = 200;
   response->payload_offset = 0xdead;
   response->payload_length = 0xbeaf;
@@ -96,19 +96,18 @@
 
   GetMockFactory()->ReadAndFullfillResponse(
       GetReader(), resource_request,
-      data_decoder::mojom::BundleResponseLocation::New(573u, 765u),
+      web_package::mojom::BundleResponseLocation::New(573u, 765u),
       std::move(response),
-      base::BindOnce(
-          [](data_decoder::mojom::BundleResponsePtr response,
-             data_decoder::mojom::BundleResponseParseErrorPtr error) {
-            EXPECT_TRUE(response);
-            EXPECT_FALSE(error);
-            if (response) {
-              EXPECT_EQ(200, response->response_code);
-              EXPECT_EQ(0xdeadu, response->payload_offset);
-              EXPECT_EQ(0xbeafu, response->payload_length);
-            }
-          }));
+      base::BindOnce([](web_package::mojom::BundleResponsePtr response,
+                        web_package::mojom::BundleResponseParseErrorPtr error) {
+        EXPECT_TRUE(response);
+        EXPECT_FALSE(error);
+        if (response) {
+          EXPECT_EQ(200, response->response_code);
+          EXPECT_EQ(0xdeadu, response->payload_offset);
+          EXPECT_EQ(0xbeafu, response->payload_length);
+        }
+      }));
 }
 
 TEST_F(WebBundleReaderTest, ReadResponseForURLContainingUserAndPass) {
@@ -117,8 +116,8 @@
   ReadMetadata();
   ASSERT_TRUE(GetReader()->HasEntry(url));
 
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   response->response_code = 200;
   response->payload_offset = 0xdead;
   response->payload_length = 0xbeaf;
@@ -128,19 +127,18 @@
 
   GetMockFactory()->ReadAndFullfillResponse(
       GetReader(), resource_request,
-      data_decoder::mojom::BundleResponseLocation::New(573u, 765u),
+      web_package::mojom::BundleResponseLocation::New(573u, 765u),
       std::move(response),
-      base::BindOnce(
-          [](data_decoder::mojom::BundleResponsePtr response,
-             data_decoder::mojom::BundleResponseParseErrorPtr error) {
-            EXPECT_TRUE(response);
-            EXPECT_FALSE(error);
-            if (response) {
-              EXPECT_EQ(200, response->response_code);
-              EXPECT_EQ(0xdeadu, response->payload_offset);
-              EXPECT_EQ(0xbeafu, response->payload_length);
-            }
-          }));
+      base::BindOnce([](web_package::mojom::BundleResponsePtr response,
+                        web_package::mojom::BundleResponseParseErrorPtr error) {
+        EXPECT_TRUE(response);
+        EXPECT_FALSE(error);
+        if (response) {
+          EXPECT_EQ(200, response->response_code);
+          EXPECT_EQ(0xdeadu, response->payload_offset);
+          EXPECT_EQ(0xbeafu, response->payload_length);
+        }
+      }));
 }
 
 TEST_F(WebBundleReaderTest, ReadResponseForURLContainingFragment) {
@@ -149,8 +147,8 @@
   ReadMetadata();
   ASSERT_TRUE(GetReader()->HasEntry(url));
 
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   response->response_code = 200;
   response->payload_offset = 0xdead;
   response->payload_length = 0xbeaf;
@@ -160,27 +158,26 @@
 
   GetMockFactory()->ReadAndFullfillResponse(
       GetReader(), resource_request,
-      data_decoder::mojom::BundleResponseLocation::New(573u, 765u),
+      web_package::mojom::BundleResponseLocation::New(573u, 765u),
       std::move(response),
-      base::BindOnce(
-          [](data_decoder::mojom::BundleResponsePtr response,
-             data_decoder::mojom::BundleResponseParseErrorPtr error) {
-            EXPECT_TRUE(response);
-            EXPECT_FALSE(error);
-            if (response) {
-              EXPECT_EQ(200, response->response_code);
-              EXPECT_EQ(0xdeadu, response->payload_offset);
-              EXPECT_EQ(0xbeafu, response->payload_length);
-            }
-          }));
+      base::BindOnce([](web_package::mojom::BundleResponsePtr response,
+                        web_package::mojom::BundleResponseParseErrorPtr error) {
+        EXPECT_TRUE(response);
+        EXPECT_FALSE(error);
+        if (response) {
+          EXPECT_EQ(200, response->response_code);
+          EXPECT_EQ(0xdeadu, response->payload_offset);
+          EXPECT_EQ(0xbeafu, response->payload_length);
+        }
+      }));
 }
 
 TEST_F(WebBundleReaderTest, ReadResponseForSecondVariant) {
   ReadMetadata();
   ASSERT_TRUE(GetReader()->HasEntry(GetPrimaryURL()));
 
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   response->response_code = 200;
   response->payload_offset = 0xdead;
   response->payload_length = 0xbeaf;
@@ -191,26 +188,25 @@
 
   GetMockFactory()->ReadAndFullfillResponse(
       GetReader(), resource_request,
-      data_decoder::mojom::BundleResponseLocation::New(333u, 222u),
+      web_package::mojom::BundleResponseLocation::New(333u, 222u),
       std::move(response),
-      base::BindOnce(
-          [](data_decoder::mojom::BundleResponsePtr response,
-             data_decoder::mojom::BundleResponseParseErrorPtr error) {
-            EXPECT_TRUE(response);
-            EXPECT_FALSE(error);
-            if (response) {
-              EXPECT_EQ(200, response->response_code);
-              EXPECT_EQ(0xdeadu, response->payload_offset);
-              EXPECT_EQ(0xbeafu, response->payload_length);
-            }
-          }));
+      base::BindOnce([](web_package::mojom::BundleResponsePtr response,
+                        web_package::mojom::BundleResponseParseErrorPtr error) {
+        EXPECT_TRUE(response);
+        EXPECT_FALSE(error);
+        if (response) {
+          EXPECT_EQ(200, response->response_code);
+          EXPECT_EQ(0xdeadu, response->payload_offset);
+          EXPECT_EQ(0xbeafu, response->payload_length);
+        }
+      }));
 }
 
 TEST_F(WebBundleReaderTest, ReadResponseBody) {
   ReadMetadata();
 
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   constexpr size_t expected_offset = 4;
   const size_t expected_length = GetBody().size() - 8;
   response->payload_offset = expected_offset;
diff --git a/content/browser/web_package/web_bundle_url_loader_factory.cc b/content/browser/web_package/web_bundle_url_loader_factory.cc
index 2785564..f9359f4 100644
--- a/content/browser/web_package/web_bundle_url_loader_factory.cc
+++ b/content/browser/web_package/web_bundle_url_loader_factory.cc
@@ -33,7 +33,7 @@
 constexpr char kCrLf[] = "\r\n";
 
 network::mojom::URLResponseHeadPtr CreateResourceResponse(
-    const data_decoder::mojom::BundleResponsePtr& response) {
+    const web_package::mojom::BundleResponsePtr& response) {
   DCHECK_EQ(net::HTTP_OK, response->response_code);
 
   std::vector<std::string> header_strings;
@@ -62,7 +62,7 @@
 
 void AddResponseParseErrorMessageToConsole(
     int frame_tree_node_id,
-    const data_decoder::mojom::BundleResponseParseErrorPtr& error) {
+    const web_package::mojom::BundleResponseParseErrorPtr& error) {
   WebContents* web_contents =
       WebContents::FromFrameTreeNodeId(frame_tree_node_id);
   if (!web_contents)
@@ -121,8 +121,8 @@
   void PauseReadingBodyFromNet() override {}
   void ResumeReadingBodyFromNet() override {}
 
-  void OnResponseReady(data_decoder::mojom::BundleResponsePtr response,
-                       data_decoder::mojom::BundleResponseParseErrorPtr error) {
+  void OnResponseReady(web_package::mojom::BundleResponsePtr response,
+                       web_package::mojom::BundleResponseParseErrorPtr error) {
     if (!factory_ || !loader_client_.is_connected())
       return;
 
diff --git a/content/browser/web_package/web_bundle_url_loader_factory_unittest.cc b/content/browser/web_package/web_bundle_url_loader_factory_unittest.cc
index 3652830..c623ee24 100644
--- a/content/browser/web_package/web_bundle_url_loader_factory_unittest.cc
+++ b/content/browser/web_package/web_bundle_url_loader_factory_unittest.cc
@@ -38,15 +38,15 @@
     loader_factory_ = std::make_unique<WebBundleURLLoaderFactory>(
         std::move(reader), FrameTreeNode::kFrameTreeNodeInvalidId);
 
-    base::flat_map<GURL, data_decoder::mojom::BundleIndexValuePtr> items;
-    data_decoder::mojom::BundleIndexValuePtr item =
-        data_decoder::mojom::BundleIndexValue::New();
+    base::flat_map<GURL, web_package::mojom::BundleIndexValuePtr> items;
+    web_package::mojom::BundleIndexValuePtr item =
+        web_package::mojom::BundleIndexValue::New();
     item->response_locations.push_back(
-        data_decoder::mojom::BundleResponseLocation::New(573u, 765u));
+        web_package::mojom::BundleResponseLocation::New(573u, 765u));
     items.insert({primary_url_, std::move(item)});
 
-    data_decoder::mojom::BundleMetadataPtr metadata =
-        data_decoder::mojom::BundleMetadata::New();
+    web_package::mojom::BundleMetadataPtr metadata =
+        web_package::mojom::BundleMetadata::New();
     metadata->primary_url = primary_url_;
     metadata->requests = std::move(items);
 
@@ -55,7 +55,7 @@
         reader_, std::move(metadata),
         base::BindOnce(
             [](base::OnceClosure quit_closure,
-               data_decoder::mojom::BundleMetadataParseErrorPtr error) {
+               web_package::mojom::BundleMetadataParseErrorPtr error) {
               std::move(quit_closure).Run();
             },
             run_loop.QuitClosure()));
@@ -78,7 +78,7 @@
   // is given. |response| can contain nullptr to simulate the case ReadResponse
   // fails.
   mojo::Remote<network::mojom::URLLoader> CreateLoaderAndStart(
-      base::Optional<data_decoder::mojom::BundleResponsePtr> response,
+      base::Optional<web_package::mojom::BundleResponsePtr> response,
       bool clone = false) {
     mojo::Remote<network::mojom::URLLoader> loader;
 
@@ -102,7 +102,7 @@
 
     if (response)
       mock_factory_->FullfillResponse(
-          data_decoder::mojom::BundleResponseLocation::New(573u, 765u),
+          web_package::mojom::BundleResponseLocation::New(573u, 765u),
           std::move(*response));
     return loader;
   }
@@ -177,8 +177,8 @@
 };
 
 TEST_F(WebBundleURLLoaderFactoryTest, CreateEntryLoader) {
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   response->response_code = 200;
   response->payload_offset = 0;
   response->payload_length = GetBody().size();
@@ -189,8 +189,8 @@
 }
 
 TEST_F(WebBundleURLLoaderFactoryTest, RangeRequest) {
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   response->response_code = 200;
   response->payload_offset = 0;
   response->payload_length = GetBody().size();
@@ -210,8 +210,8 @@
 
 TEST_F(WebBundleURLLoaderFactoryTest,
        CreateEntryLoaderForURLContainingUserAndPass) {
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   response->response_code = 200;
   response->payload_offset = 0;
   response->payload_length = GetBody().size();
@@ -225,8 +225,8 @@
 
 TEST_F(WebBundleURLLoaderFactoryTest,
        CreateEntryLoaderForURLContainingFragment) {
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   response->response_code = 200;
   response->payload_offset = 0;
   response->payload_length = GetBody().size();
@@ -283,8 +283,8 @@
 }
 
 TEST_F(WebBundleURLLoaderFactoryTest, CreateByClonedFactory) {
-  data_decoder::mojom::BundleResponsePtr response =
-      data_decoder::mojom::BundleResponse::New();
+  web_package::mojom::BundleResponsePtr response =
+      web_package::mojom::BundleResponse::New();
   response->response_code = 200;
   response->payload_offset = 0;
   response->payload_length = GetBody().size();
diff --git a/content/browser/web_package/web_bundle_utils.cc b/content/browser/web_package/web_bundle_utils.cc
index 42c75b8..1003ceb4 100644
--- a/content/browser/web_package/web_bundle_utils.cc
+++ b/content/browser/web_package/web_bundle_utils.cc
@@ -143,7 +143,7 @@
 }
 
 std::string GetMetadataParseErrorMessage(
-    const data_decoder::mojom::BundleMetadataParseErrorPtr& metadata_error) {
+    const web_package::mojom::BundleMetadataParseErrorPtr& metadata_error) {
   return std::string("Failed to read metadata of Web Bundle file: ") +
          metadata_error->message;
 }
diff --git a/content/browser/web_package/web_bundle_utils.h b/content/browser/web_package/web_bundle_utils.h
index e43bb5f..65b37a8 100644
--- a/content/browser/web_package/web_bundle_utils.h
+++ b/content/browser/web_package/web_bundle_utils.h
@@ -8,10 +8,10 @@
 #include <string>
 
 #include "base/files/file_path.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "url/gurl.h"
 
@@ -55,7 +55,7 @@
     const std::string& error_message);
 
 std::string GetMetadataParseErrorMessage(
-    const data_decoder::mojom::BundleMetadataParseErrorPtr& metadata_error);
+    const web_package::mojom::BundleMetadataParseErrorPtr& metadata_error);
 
 // On Android, returns true if the url scheme is file or content. On other
 // platforms, returns true if the url scheme is file.
diff --git a/content/browser/zoom_browsertest.cc b/content/browser/zoom_browsertest.cc
index c483165..f26d1c3 100644
--- a/content/browser/zoom_browsertest.cc
+++ b/content/browser/zoom_browsertest.cc
@@ -5,6 +5,7 @@
 #include <vector>
 
 #include "base/strings/string_util.h"
+#include "build/build_config.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -433,7 +434,13 @@
   );
 }
 
-IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SiblingFramesZoom) {
+// Flaky on mac, https://crbug.com/1055282
+#if defined(OS_MACOSX)
+#define MAYBE_SiblingFramesZoom DISABLED_SiblingFramesZoom
+#else
+#define MAYBE_SiblingFramesZoom SiblingFramesZoom
+#endif
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, MAYBE_SiblingFramesZoom) {
   std::string top_level_host("a.com");
   GURL main_url(embedded_test_server()->GetURL(
       top_level_host, "/cross_site_iframe_factory.html?a(b,b)"));
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index b7d2600..a9e2d39 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -5,12 +5,14 @@
 #include "content/renderer/media/media_factory.h"
 
 #include <string>
+#include <tuple>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/task_runner_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
@@ -93,8 +95,8 @@
 
 #if BUILDFLAG(ENABLE_MEDIA_REMOTING)
 // Enable remoting sender
-#include "media/remoting/courier_renderer_factory.h"    // nogncheck
-#include "media/remoting/renderer_controller.h"         // nogncheck
+#include "media/remoting/courier_renderer_factory.h"  // nogncheck
+#include "media/remoting/renderer_controller.h"       // nogncheck
 #endif
 
 #if BUILDFLAG(IS_CHROMECAST)
@@ -157,13 +159,38 @@
 void LogRoughness(media::MediaLog* media_log,
                   int size,
                   base::TimeDelta duration,
-                  double roughness) {
+                  double roughness,
+                  int refresh_rate_hz,
+                  gfx::Size frame_size) {
   // This function can be called from any thread. Don't do anything that assumes
   // a certain task runner.
-  double fps = size / duration.InSecondsF();
+  double fps = std::round(size / duration.InSecondsF());
   media_log->SetProperty<media::MediaLogProperty::kVideoPlaybackRoughness>(
       roughness);
   media_log->SetProperty<media::MediaLogProperty::kFramerate>(fps);
+
+  // TODO(eugene@chromium.org) All of this needs to be moved away from
+  // media_factory.cc once a proper channel to report roughness is found.
+  static constexpr char kRoughnessHistogramName[] = "Media.Video.Roughness";
+  const char* suffix = nullptr;
+  static std::tuple<double, const char*> fps_buckets[] = {
+      {24, "24fps"}, {25, "25fps"}, {30, "30fps"}, {50, "50fps"}, {60, "60fps"},
+  };
+  for (auto& bucket : fps_buckets) {
+    if (fps == std::get<0>(bucket)) {
+      suffix = std::get<1>(bucket);
+      break;
+    }
+  }
+
+  // Only report known FPS buckets, on 60Hz displays and at least HD quality.
+  if (suffix != nullptr && refresh_rate_hz == 60 && frame_size.height() > 700) {
+    UMA_HISTOGRAM_CUSTOM_TIMES(
+        base::JoinString({kRoughnessHistogramName, suffix}, "."),
+        base::TimeDelta::FromMillisecondsD(roughness),
+        base::TimeDelta::FromMilliseconds(1),
+        base::TimeDelta::FromMilliseconds(99), 100);
+  }
 }
 
 std::unique_ptr<media::DefaultRendererFactory> CreateDefaultRendererFactory(
diff --git a/content/renderer/render_widget_screen_metrics_emulator.cc b/content/renderer/render_widget_screen_metrics_emulator.cc
index c1dbc65..3473f54 100644
--- a/content/renderer/render_widget_screen_metrics_emulator.cc
+++ b/content/renderer/render_widget_screen_metrics_emulator.cc
@@ -6,6 +6,7 @@
 
 #include "content/public/common/untrustworthy_context_menu_params.h"
 #include "content/renderer/render_widget_screen_metrics_emulator_delegate.h"
+#include "ui/gfx/geometry/safe_integer_conversions.h"
 
 namespace content {
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 49a5323..8ecca82 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1198,6 +1198,7 @@
     "//components/url_formatter:url_formatter",
     "//components/viz/host",
     "//components/viz/test:test_support",
+    "//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/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
index 7b0f3a4f..6c49fca6 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
@@ -11,8 +11,9 @@
 import tempfile
 import unittest
 import mock
-import gpu_project_config
+import sys
 import run_gpu_integration_test
+import gpu_project_config
 
 from gpu_tests import context_lost_integration_test
 from gpu_tests import gpu_helper
@@ -98,26 +99,22 @@
     temp_file = tempfile.NamedTemporaryFile(delete=False)
     temp_file.close()
     test_argv = [
-        test_name, '--write-full-results-to=%s' % temp_file.name
+        run_gpu_integration_test.__file__, test_name,
+        '--write-full-results-to=%s' % temp_file.name
     ] + extra_args
     unittest_config = chromium_config.ChromiumConfig(
         top_level_dir=path_util.GetGpuTestDir(),
         benchmark_dirs=[
             os.path.join(path_util.GetGpuTestDir(), 'unittest_data')
         ])
-    old_manager = binary_manager._binary_manager
-    with mock.patch.object(gpu_project_config, 'CONFIG', unittest_config):
-      processed_args = run_gpu_integration_test.ProcessArgs(test_argv)
-      telemetry_args = browser_test_runner.ProcessConfig(
-          unittest_config, processed_args)
-      try:
-        binary_manager._binary_manager = None
-        run_browser_tests.RunTests(telemetry_args)
-        with open(temp_file.name) as f:
-          self._test_result = json.load(f)
-      finally:
-        binary_manager._binary_manager = old_manager
-        temp_file.close()
+    with mock.patch.object(sys, 'argv', test_argv):
+      with mock.patch.object(gpu_project_config, 'CONFIG', unittest_config):
+        try:
+          run_gpu_integration_test.main()
+          with open(temp_file.name) as f:
+            self._test_result = json.load(f)
+        finally:
+          temp_file.close()
 
   def testOverrideDefaultRetryArgumentsinRunGpuIntegrationTests(self):
     self._RunGpuIntegrationTests('run_tests_with_expectations_files',
diff --git a/content/test/gpu/run_gpu_integration_test.py b/content/test/gpu/run_gpu_integration_test.py
index 3592082..3da32242 100755
--- a/content/test/gpu/run_gpu_integration_test.py
+++ b/content/test/gpu/run_gpu_integration_test.py
@@ -58,7 +58,10 @@
       if cl.Name() == test_name:
         return cl
 
-def ProcessArgs(args):
+
+def main():
+  FailIfScreenLockedOnMac()
+  rest_args = sys.argv[1:]
   parser = argparse.ArgumentParser(
       description='Extra argument parser', add_help=False)
 
@@ -66,7 +69,7 @@
       '--write-run-test-arguments',
       action='store_true',
       help=('Write the test script arguments to the results file.'))
-  option, rest_args_filtered = parser.parse_known_args(args)
+  option, rest_args_filtered = parser.parse_known_args(rest_args)
 
   parser.add_argument('test', nargs='*', type=str, help=argparse.SUPPRESS)
   option, _ = parser.parse_known_args(rest_args_filtered)
@@ -89,11 +92,6 @@
   rest_args_filtered.extend(
       ['--repository-absolute-path',
        path_util.GetChromiumSrcDir()])
-  return rest_args_filtered
-
-def main():
-  FailIfScreenLockedOnMac()
-  rest_args_filtered = ProcessArgs(sys.argv[1:])
 
   retval = browser_test_runner.Run(gpu_project_config.CONFIG,
                                    rest_args_filtered)
diff --git a/gpu/command_buffer/service/external_vk_image_backing.cc b/gpu/command_buffer/service/external_vk_image_backing.cc
index 5e38edb1..05b277b 100644
--- a/gpu/command_buffer/service/external_vk_image_backing.cc
+++ b/gpu/command_buffer/service/external_vk_image_backing.cc
@@ -346,65 +346,6 @@
     DCHECK(!gl_reads_in_progress_);
     gl_reads_in_progress_ = 1;
   }
-
-  if (use_separate_gl_texture())
-    return true;
-
-  DCHECK(need_synchronization());
-  DCHECK(is_gl);
-
-  auto command_buffer = command_pool_->CreatePrimaryCommandBuffer();
-  {
-    ScopedSingleUseCommandBufferRecorder recorder(*command_buffer);
-    GrVkImageInfo image_info;
-    bool success = backend_texture_.getVkImageInfo(&image_info);
-    DCHECK(success);
-    auto image_layout = image_info.fImageLayout;
-    if (image_layout == VK_IMAGE_LAYOUT_UNDEFINED) {
-      // dst_image_layout cannot be VK_IMAGE_LAYOUT_UNDEFINED, so we set it to
-      // VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.
-      image_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-      command_buffer->TransitionImageLayout(
-          image_info.fImage, image_info.fImageLayout, image_layout);
-      // Update backend_texture_ image layout.
-      backend_texture_.setVkImageLayout(image_layout);
-    }
-    uint32_t vulkan_queue_index = context_state_->vk_context_provider()
-                                      ->GetDeviceQueue()
-                                      ->GetVulkanQueueIndex();
-    // Transfer image queue family ownership to external, so the image can be
-    // used by GL.
-    command_buffer->TransitionImageLayout(image_info.fImage, image_layout,
-                                          image_layout, vulkan_queue_index,
-                                          VK_QUEUE_FAMILY_EXTERNAL);
-  }
-
-  std::vector<VkSemaphore> wait_semaphores;
-  wait_semaphores.reserve(semaphore_handles->size());
-  for (auto& handle : *semaphore_handles) {
-    VkSemaphore semaphore = vulkan_implementation()->ImportSemaphoreHandle(
-        device(), std::move(handle));
-    wait_semaphores.emplace_back(semaphore);
-  }
-  semaphore_handles->clear();
-
-  VkSemaphore signal_semaphore =
-      vulkan_implementation()->CreateExternalSemaphore(device());
-  // TODO(penghuang): ask skia to do it for us to avoid this queue submission.
-  command_buffer->Submit(wait_semaphores.size(), wait_semaphores.data(), 1,
-                         &signal_semaphore);
-  auto end_access_semaphore_handle =
-      vulkan_implementation()->GetSemaphoreHandle(device(), signal_semaphore);
-  semaphore_handles->push_back(std::move(end_access_semaphore_handle));
-
-  auto* fence_helper =
-      context_state_->vk_context_provider()->GetDeviceQueue()->GetFenceHelper();
-  fence_helper->EnqueueVulkanObjectCleanupForSubmittedWork(
-      std::move(command_buffer));
-  wait_semaphores.emplace_back(signal_semaphore);
-  fence_helper->EnqueueSemaphoresCleanupForSubmittedWork(
-      std::move(wait_semaphores));
-
   return true;
 }
 
@@ -419,43 +360,6 @@
     }
   }
 
-  // Only transite image layout and queue back when it is the last gl access.
-  if (is_gl && !use_separate_gl_texture()) {
-    DCHECK(semaphore_handle.is_valid());
-    auto command_buffer = command_pool_->CreatePrimaryCommandBuffer();
-    {
-      ScopedSingleUseCommandBufferRecorder recorder(*command_buffer);
-      GrVkImageInfo image_info;
-      bool success = backend_texture_.getVkImageInfo(&image_info);
-      DCHECK(success);
-      uint32_t vulkan_queue_index = context_state_->vk_context_provider()
-                                        ->GetDeviceQueue()
-                                        ->GetVulkanQueueIndex();
-
-      // After GL accessing, transfer image queue family ownership back, so it
-      // can be used by vulkan.
-      command_buffer->TransitionImageLayout(
-          image_info.fImage, image_info.fImageLayout, image_info.fImageLayout,
-          VK_QUEUE_FAMILY_EXTERNAL, vulkan_queue_index);
-    }
-
-    VkSemaphore semaphore = vulkan_implementation()->ImportSemaphoreHandle(
-        device(), std::move(semaphore_handle));
-    VkSemaphore end_access_semaphore =
-        vulkan_implementation()->CreateExternalSemaphore(device());
-    // TODO(penghuang): ask skia to do it for us to avoid this queue submission.
-    command_buffer->Submit(1, &semaphore, 1, &end_access_semaphore);
-    semaphore_handle = vulkan_implementation()->GetSemaphoreHandle(
-        device(), end_access_semaphore);
-    auto* fence_helper = context_state_->vk_context_provider()
-                             ->GetDeviceQueue()
-                             ->GetFenceHelper();
-    fence_helper->EnqueueVulkanObjectCleanupForSubmittedWork(
-        std::move(command_buffer));
-    fence_helper->EnqueueSemaphoresCleanupForSubmittedWork(
-        {semaphore, end_access_semaphore});
-  }
-
   EndAccessInternal(readonly, std::move(semaphore_handle));
   if (!readonly) {
     if (use_separate_gl_texture()) {
@@ -879,6 +783,10 @@
   };
 
   gr_context->flush(flush_info);
+  gr_context->setBackendTextureState(
+      backend_texture_,
+      GrBackendSurfaceMutableState(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                                   VK_QUEUE_FAMILY_EXTERNAL));
   // Submit so the |end_access_semaphore| is ready for waiting.
   gr_context->submit();
 
diff --git a/gpu/command_buffer/service/external_vk_image_skia_representation.cc b/gpu/command_buffer/service/external_vk_image_skia_representation.cc
index 3211b8b..dc5c6af 100644
--- a/gpu/command_buffer/service/external_vk_image_skia_representation.cc
+++ b/gpu/command_buffer/service/external_vk_image_skia_representation.cc
@@ -34,7 +34,8 @@
     int final_msaa_count,
     const SkSurfaceProps& surface_props,
     std::vector<GrBackendSemaphore>* begin_semaphores,
-    std::vector<GrBackendSemaphore>* end_semaphores) {
+    std::vector<GrBackendSemaphore>* end_semaphores,
+    std::unique_ptr<GrBackendSurfaceMutableState>* end_state) {
   auto* gr_context = backing_impl()->context_state()->gr_context();
   if (gr_context->abandoned()) {
     LOG(ERROR) << "GrContext is abandonded.";
@@ -79,6 +80,15 @@
   ALLOW_UNUSED_LOCAL(count);
 
   access_mode_ = kWrite;
+
+  // If Vulkan and GLSet share the same memory backing, we need set |end_state|
+  // VK_QUEUE_FAMILY_EXTERNAL, and then the caller will set the VkImage to
+  // VK_QUEUE_FAMILY_EXTERNAL before calling EndAccess().
+  if (!backing_impl()->use_separate_gl_texture()) {
+    *end_state = std::make_unique<GrBackendSurfaceMutableState>(
+        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_QUEUE_FAMILY_EXTERNAL);
+  }
+
   return surface;
 }
 
@@ -97,7 +107,8 @@
 
 sk_sp<SkPromiseImageTexture> ExternalVkImageSkiaRepresentation::BeginReadAccess(
     std::vector<GrBackendSemaphore>* begin_semaphores,
-    std::vector<GrBackendSemaphore>* end_semaphores) {
+    std::vector<GrBackendSemaphore>* end_semaphores,
+    std::unique_ptr<GrBackendSurfaceMutableState>* end_state) {
   if (access_mode_ != kNone) {
     LOG(DFATAL) << "Previous access is not finished. mode=" << access_mode_;
     return nullptr;
@@ -109,6 +120,15 @@
     LOG(ERROR) << "BeginAccess failed";
     return nullptr;
   }
+
+  // If Vulkan and GLSet share the same memory backing, we need set |end_state|
+  // VK_QUEUE_FAMILY_EXTERNAL, and then the caller will set the VkImage to
+  // VK_QUEUE_FAMILY_EXTERNAL before calling EndAccess().
+  if (!backing_impl()->use_separate_gl_texture()) {
+    *end_state = std::make_unique<GrBackendSurfaceMutableState>(
+        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_QUEUE_FAMILY_EXTERNAL);
+  }
+
   access_mode_ = kRead;
   return promise_texture;
 }
diff --git a/gpu/command_buffer/service/external_vk_image_skia_representation.h b/gpu/command_buffer/service/external_vk_image_skia_representation.h
index 3f7c523..000b357 100644
--- a/gpu/command_buffer/service/external_vk_image_skia_representation.h
+++ b/gpu/command_buffer/service/external_vk_image_skia_representation.h
@@ -28,11 +28,13 @@
       int final_msaa_count,
       const SkSurfaceProps& surface_props,
       std::vector<GrBackendSemaphore>* begin_semaphores,
-      std::vector<GrBackendSemaphore>* end_semaphores) override;
+      std::vector<GrBackendSemaphore>* end_semaphores,
+      std::unique_ptr<GrBackendSurfaceMutableState>* end_state) override;
   void EndWriteAccess(sk_sp<SkSurface> surface) override;
   sk_sp<SkPromiseImageTexture> BeginReadAccess(
       std::vector<GrBackendSemaphore>* begin_semaphores,
-      std::vector<GrBackendSemaphore>* end_semaphores) override;
+      std::vector<GrBackendSemaphore>* end_semaphores,
+      std::unique_ptr<GrBackendSurfaceMutableState>* end_state) override;
   void EndReadAccess() override;
 
  private:
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 250c811b..3505969 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -10646,9 +10646,9 @@
                                                     GLuint texture_unit) {
   // Image is already in use if texture is attached to a framebuffer.
   if (texture && !texture->IsAttachedToFramebuffer()) {
-    Texture::ImageState image_state;
-    gl::GLImage* image = texture->GetLevelImage(textarget, 0, &image_state);
-    if (image && image_state == Texture::UNBOUND) {
+    Texture::ImageState old_image_state;
+    gl::GLImage* image = texture->GetLevelImage(textarget, 0, &old_image_state);
+    if (image && old_image_state == Texture::UNBOUND) {
       ScopedGLErrorSuppressor suppressor(
           "GLES2DecoderImpl::DoBindOrCopyTexImageIfNeeded", error_state_.get());
       if (texture_unit)
@@ -10657,7 +10657,7 @@
       if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
         bool rv = image->BindTexImage(textarget);
         DCHECK(rv) << "BindTexImage() failed";
-        image_state = Texture::BOUND;
+        texture->SetLevelImageState(textarget, 0, Texture::BOUND);
       } else {
         DoCopyTexImage(texture, textarget, image);
       }
diff --git a/gpu/command_buffer/service/shared_image_representation.cc b/gpu/command_buffer/service/shared_image_representation.cc
index fd2d31b5..5d77551 100644
--- a/gpu/command_buffer/service/shared_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image_representation.cc
@@ -93,8 +93,11 @@
 SharedImageRepresentationSkia::ScopedWriteAccess::ScopedWriteAccess(
     util::PassKey<SharedImageRepresentationSkia> /* pass_key */,
     SharedImageRepresentationSkia* representation,
-    sk_sp<SkSurface> surface)
-    : ScopedAccessBase(representation), surface_(std::move(surface)) {}
+    sk_sp<SkSurface> surface,
+    std::unique_ptr<GrBackendSurfaceMutableState> end_state)
+    : ScopedAccessBase(representation),
+      surface_(std::move(surface)),
+      end_state_(std::move(end_state)) {}
 
 SharedImageRepresentationSkia::ScopedWriteAccess::~ScopedWriteAccess() {
   representation()->EndWriteAccess(std::move(surface_));
@@ -112,15 +115,18 @@
     return nullptr;
   }
 
-  sk_sp<SkSurface> surface = BeginWriteAccess(final_msaa_count, surface_props,
-                                              begin_semaphores, end_semaphores);
+  std::unique_ptr<GrBackendSurfaceMutableState> end_state;
+  sk_sp<SkSurface> surface =
+      BeginWriteAccess(final_msaa_count, surface_props, begin_semaphores,
+                       end_semaphores, &end_state);
   if (!surface)
     return nullptr;
 
   backing()->OnWriteSucceeded();
 
   return std::make_unique<ScopedWriteAccess>(
-      util::PassKey<SharedImageRepresentationSkia>(), this, std::move(surface));
+      util::PassKey<SharedImageRepresentationSkia>(), this, std::move(surface),
+      std::move(end_state));
 }
 
 std::unique_ptr<SharedImageRepresentationSkia::ScopedWriteAccess>
@@ -137,9 +143,11 @@
 SharedImageRepresentationSkia::ScopedReadAccess::ScopedReadAccess(
     util::PassKey<SharedImageRepresentationSkia> /* pass_key */,
     SharedImageRepresentationSkia* representation,
-    sk_sp<SkPromiseImageTexture> promise_image_texture)
+    sk_sp<SkPromiseImageTexture> promise_image_texture,
+    std::unique_ptr<GrBackendSurfaceMutableState> end_state)
     : ScopedAccessBase(representation),
-      promise_image_texture_(std::move(promise_image_texture)) {}
+      promise_image_texture_(std::move(promise_image_texture)),
+      end_state_(std::move(end_state)) {}
 
 SharedImageRepresentationSkia::ScopedReadAccess::~ScopedReadAccess() {
   representation()->EndReadAccess();
@@ -154,8 +162,9 @@
     return nullptr;
   }
 
+  std::unique_ptr<GrBackendSurfaceMutableState> end_state;
   sk_sp<SkPromiseImageTexture> promise_image_texture =
-      BeginReadAccess(begin_semaphores, end_semaphores);
+      BeginReadAccess(begin_semaphores, end_semaphores, &end_state);
   if (!promise_image_texture)
     return nullptr;
 
@@ -163,7 +172,38 @@
 
   return std::make_unique<ScopedReadAccess>(
       util::PassKey<SharedImageRepresentationSkia>(), this,
-      std::move(promise_image_texture));
+      std::move(promise_image_texture), std::move(end_state));
+}
+
+sk_sp<SkSurface> SharedImageRepresentationSkia::BeginWriteAccess(
+    int final_msaa_count,
+    const SkSurfaceProps& surface_props,
+    std::vector<GrBackendSemaphore>* begin_semaphores,
+    std::vector<GrBackendSemaphore>* end_semaphores,
+    std::unique_ptr<GrBackendSurfaceMutableState>* end_state) {
+  return BeginWriteAccess(final_msaa_count, surface_props, begin_semaphores,
+                          end_semaphores);
+}
+
+sk_sp<SkSurface> SharedImageRepresentationSkia::BeginWriteAccess(
+    int final_msaa_count,
+    const SkSurfaceProps& surface_props,
+    std::vector<GrBackendSemaphore>* begin_semaphores,
+    std::vector<GrBackendSemaphore>* end_semaphores) {
+  return nullptr;
+}
+
+sk_sp<SkPromiseImageTexture> SharedImageRepresentationSkia::BeginReadAccess(
+    std::vector<GrBackendSemaphore>* begin_semaphores,
+    std::vector<GrBackendSemaphore>* end_semaphores,
+    std::unique_ptr<GrBackendSurfaceMutableState>* end_state) {
+  return BeginReadAccess(begin_semaphores, end_semaphores);
+}
+
+sk_sp<SkPromiseImageTexture> SharedImageRepresentationSkia::BeginReadAccess(
+    std::vector<GrBackendSemaphore>* begin_semaphores,
+    std::vector<GrBackendSemaphore>* end_semaphores) {
+  return nullptr;
 }
 
 SharedImageRepresentationOverlay::ScopedReadAccess::ScopedReadAccess(
diff --git a/gpu/command_buffer/service/shared_image_representation.h b/gpu/command_buffer/service/shared_image_representation.h
index d6ad606..2337a2c 100644
--- a/gpu/command_buffer/service/shared_image_representation.h
+++ b/gpu/command_buffer/service/shared_image_representation.h
@@ -18,6 +18,7 @@
 #include "gpu/command_buffer/service/shared_image_manager.h"
 #include "gpu/gpu_gles2_export.h"
 #include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/gpu/GrBackendSurfaceMutableState.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -227,13 +228,16 @@
    public:
     ScopedWriteAccess(util::PassKey<SharedImageRepresentationSkia> pass_key,
                       SharedImageRepresentationSkia* representation,
-                      sk_sp<SkSurface> surface);
+                      sk_sp<SkSurface> surface,
+                      std::unique_ptr<GrBackendSurfaceMutableState> end_state);
     ~ScopedWriteAccess();
 
     SkSurface* surface() const { return surface_.get(); }
+    GrBackendSurfaceMutableState* end_state() const { return end_state_.get(); }
 
    private:
     sk_sp<SkSurface> surface_;
+    std::unique_ptr<GrBackendSurfaceMutableState> end_state_;
   };
 
   class GPU_GLES2_EXPORT ScopedReadAccess
@@ -241,15 +245,18 @@
    public:
     ScopedReadAccess(util::PassKey<SharedImageRepresentationSkia> pass_key,
                      SharedImageRepresentationSkia* representation,
-                     sk_sp<SkPromiseImageTexture> promise_image_texture);
+                     sk_sp<SkPromiseImageTexture> promise_image_texture,
+                     std::unique_ptr<GrBackendSurfaceMutableState> end_state);
     ~ScopedReadAccess();
 
     SkPromiseImageTexture* promise_image_texture() const {
       return promise_image_texture_.get();
     }
+    GrBackendSurfaceMutableState* end_state() const { return end_state_.get(); }
 
    private:
     sk_sp<SkPromiseImageTexture> promise_image_texture_;
+    std::unique_ptr<GrBackendSurfaceMutableState> end_state_;
   };
 
   SharedImageRepresentationSkia(SharedImageManager* manager,
@@ -287,11 +294,19 @@
   // client must submit them with drawing operations which use the backing.
   // The ownership of end_semaphores are not passed to client. And client must
   // submit the end_semaphores before calling EndWriteAccess().
+  // The backing can assign end_state, and the caller must reset backing's state
+  // to the end_state before calling EndWriteAccess().
   virtual sk_sp<SkSurface> BeginWriteAccess(
       int final_msaa_count,
       const SkSurfaceProps& surface_props,
       std::vector<GrBackendSemaphore>* begin_semaphores,
-      std::vector<GrBackendSemaphore>* end_semaphores) = 0;
+      std::vector<GrBackendSemaphore>* end_semaphores,
+      std::unique_ptr<GrBackendSurfaceMutableState>* end_state);
+  virtual sk_sp<SkSurface> BeginWriteAccess(
+      int final_msaa_count,
+      const SkSurfaceProps& surface_props,
+      std::vector<GrBackendSemaphore>* begin_semaphores,
+      std::vector<GrBackendSemaphore>* end_semaphores);
   virtual void EndWriteAccess(sk_sp<SkSurface> surface) = 0;
 
   // Begin the read access. The implementations should insert semaphores into
@@ -301,9 +316,15 @@
   // client must submit them with drawing operations which use the backing.
   // The ownership of end_semaphores are not passed to client. And client must
   // submit the end_semaphores before calling EndReadAccess().
+  // The backing can assign end_state, and the caller must reset backing's state
+  // to the end_state before calling EndReadAccess().
   virtual sk_sp<SkPromiseImageTexture> BeginReadAccess(
       std::vector<GrBackendSemaphore>* begin_semaphores,
-      std::vector<GrBackendSemaphore>* end_semaphores) = 0;
+      std::vector<GrBackendSemaphore>* end_semaphores,
+      std::unique_ptr<GrBackendSurfaceMutableState>* end_state);
+  virtual sk_sp<SkPromiseImageTexture> BeginReadAccess(
+      std::vector<GrBackendSemaphore>* begin_semaphores,
+      std::vector<GrBackendSemaphore>* end_semaphores);
   virtual void EndReadAccess() = 0;
 };
 
diff --git a/gpu/vulkan/vulkan_image.cc b/gpu/vulkan/vulkan_image.cc
index 6f6d6715..fa199dd 100644
--- a/gpu/vulkan/vulkan_image.cc
+++ b/gpu/vulkan/vulkan_image.cc
@@ -104,7 +104,8 @@
     VkImageTiling image_tiling,
     VkDeviceSize device_size,
     uint32_t memory_type_index,
-    base::Optional<VulkanYCbCrInfo>& ycbcr_info) {
+    base::Optional<VulkanYCbCrInfo>& ycbcr_info,
+    VkImageCreateFlags flags) {
   auto image = std::make_unique<VulkanImage>(util::PassKey<VulkanImage>());
   image->device_queue_ = device_queue;
   image->image_ = vk_image;
@@ -115,6 +116,7 @@
   image->device_size_ = device_size;
   image->memory_type_index_ = memory_type_index;
   image->ycbcr_info_ = ycbcr_info;
+  image->flags_ = flags;
   return image;
 }
 
diff --git a/gpu/vulkan/vulkan_image.h b/gpu/vulkan/vulkan_image.h
index 888bf86..d48dd9b 100644
--- a/gpu/vulkan/vulkan_image.h
+++ b/gpu/vulkan/vulkan_image.h
@@ -74,7 +74,8 @@
       VkImageTiling image_tiling,
       VkDeviceSize device_size,
       uint32_t memory_type_index,
-      base::Optional<VulkanYCbCrInfo>& ycbcr_info);
+      base::Optional<VulkanYCbCrInfo>& ycbcr_info,
+      VkImageCreateFlags flags = 0);
 
   void Destroy();
 
diff --git a/infra/config/generated/luci-notify.cfg b/infra/config/generated/luci-notify.cfg
index 07f7844..0a1a1193 100644
--- a/infra/config/generated/luci-notify.cfg
+++ b/infra/config/generated/luci-notify.cfg
@@ -97,6 +97,31 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci"
+    name: "Android Release (Nexus 5X)"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "chromesec-lkgr-failures@google.com"
@@ -136,6 +161,81 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci"
+    name: "GPU Linux Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci"
+    name: "GPU Mac Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci"
+    name: "GPU Win x64 Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "chromesec-lkgr-failures@google.com"
@@ -305,6 +405,30 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci"
+    name: "Linux Release (NVIDIA)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "chromesec-lkgr-failures@google.com"
@@ -357,6 +481,54 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci"
+    name: "Mac Release (Intel)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci"
+    name: "Mac Retina Release (AMD)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "chromesec-lkgr-failures@google.com"
@@ -552,6 +724,30 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci"
+    name: "Win10 x64 Release (NVIDIA)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "cronet-bots-observer@google.com"
@@ -795,6 +991,31 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m83"
+    name: "Android Release (Nexus 5X)"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "cr-fuchsia+bot@chromium.org"
@@ -821,6 +1042,177 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m83"
+    name: "GPU Linux Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m83"
+    name: "GPU Mac Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m83"
+    name: "GPU Win x64 Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m83"
+    name: "Linux Release (NVIDIA)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m83"
+    name: "Mac Release (Intel)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m83"
+    name: "Mac Retina Release (AMD)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m83"
+    name: "Win10 x64 Release (NVIDIA)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "cronet-bots-observer@google.com"
@@ -910,6 +1302,31 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m84"
+    name: "Android Release (Nexus 5X)"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "cr-fuchsia+bot@chromium.org"
@@ -936,6 +1353,177 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m84"
+    name: "GPU Linux Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m84"
+    name: "GPU Mac Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m84"
+    name: "GPU Win x64 Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m84"
+    name: "Linux Release (NVIDIA)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m84"
+    name: "Mac Release (Intel)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m84"
+    name: "Mac Retina Release (AMD)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m84"
+    name: "Win10 x64 Release (NVIDIA)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "cronet-bots-observer@google.com"
@@ -1025,6 +1613,31 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m85"
+    name: "Android Release (Nexus 5X)"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "cr-fuchsia+bot@chromium.org"
@@ -1051,6 +1664,177 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m85"
+    name: "GPU Linux Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m85"
+    name: "GPU Mac Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m85"
+    name: "GPU Win x64 Builder"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m85"
+    name: "Linux Release (NVIDIA)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m85"
+    name: "Mac Release (Intel)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m85"
+    name: "Mac Retina Release (AMD)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+test-tree-closing-notifier@chromium.org"
+    }
+  }
+  notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+    email {
+      recipients: "orodley+gpu-test-tree-closing-notifier@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci-m85"
+    name: "Win10 x64 Release (NVIDIA)"
+  }
+  tree_closers {
+    tree_status_host: "chromium-status.appspot.com"
+    failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "cronet-bots-observer@google.com"
diff --git a/infra/config/lib/ci.star b/infra/config/lib/ci.star
index 98e8c973..57b909e 100644
--- a/infra/config/lib/ci.star
+++ b/infra/config/lib/ci.star
@@ -421,6 +421,7 @@
     main_console_view=args.DEFAULT,
     cq_mirrors_console_view=args.DEFAULT,
     console_view_entry=None,
+    tree_closing=False,
     **kwargs):
   """Define a CI builder.
 
@@ -447,14 +448,21 @@
       has no effect on creating an entry to the main console view.
     console_view_entry - A structure providing the details of the entry
       to add to the console view. See `ci.console_view_entry` for details.
+    tree_closing - If true, failed builds from this builder that meet certain
+      criteria will close the tree and email the sheriff. See the
+      'chromium-tree-closer' config in notifiers.star for the full criteria.
   """
   # Define the builder first so that any validation of luci.builder arguments
   # (e.g. bucket) occurs before we try to use it
+  notifies = kwargs.pop('notifies', [])
+  if tree_closing:
+    notifies += ['chromium-tree-closer', 'chromium-tree-closer-email']
   ret = builders.builder(
       name = name,
       resultdb_bigquery_exports = [resultdb.export_test_results(
           bq_table = 'luci-resultdb.chromium.ci_test_results',
       )],
+      notifies = notifies,
       **kwargs
   )
 
@@ -766,22 +774,27 @@
   )
 
 
-def gpu_builder(*, name, **kwargs):
+def gpu_builder(*, name, tree_closing=True, **kwargs):
+  notifies = kwargs.pop('notifies', [])
+  if tree_closing:
+    notifies.append('gpu-tree-closer-email')
   return ci.builder(
       name = name,
       goma_backend = builders.goma.backend.RBE_PROD,
       mastername = 'chromium.gpu',
+      tree_closing = tree_closing,
+      notifies = notifies,
       **kwargs
   )
 
-
 # Many of the GPU testers are thin testers, they use linux VMS regardless of the
 # actual OS that the tests are built for
-def gpu_thin_tester(*, name, **kwargs):
+def gpu_thin_tester(*, name, tree_closing=True, **kwargs):
   return gpu_builder(
       name = name,
       cores = 2,
       os = builders.os.LINUX_DEFAULT,
+      tree_closing = tree_closing,
       **kwargs
   )
 
diff --git a/infra/config/main.star b/infra/config/main.star
index b2898681..90fc892 100755
--- a/infra/config/main.star
+++ b/infra/config/main.star
@@ -16,6 +16,10 @@
 # Enable LUCI Realms support.
 lucicfg.enable_experiment('crbug.com/1085650')
 
+# Enable tree closing. Note that for now it is only in a "dry run" mode, where
+# it doesn't actually close the tree, just logs what it would have done.
+lucicfg.enable_experiment("crbug.com/1054172")
+
 # Tell lucicfg what files it is allowed to touch
 lucicfg.config(
     config_dir = 'generated',
diff --git a/infra/config/notifiers.star b/infra/config/notifiers.star
index 4d2e427c..9fd0028 100644
--- a/infra/config/notifiers.star
+++ b/infra/config/notifiers.star
@@ -39,3 +39,34 @@
     on_new_status = ['FAILURE'],
     notify_emails = ['chromium-component-mapping@google.com'],
 )
+
+TREE_CLOSING_STEPS = [
+    'bot_update',
+    'compile',
+    'gclient runhooks',
+    'runhooks',
+    'update',
+]
+
+luci.tree_closer(
+    name = 'chromium-tree-closer',
+    tree_status_host = 'chromium-status.appspot.com',
+    failed_step_regexp = TREE_CLOSING_STEPS
+)
+
+luci.notifier(
+    name = 'chromium-tree-closer-email',
+    on_occurrence = ['FAILURE'],
+    # Stand-in for the chromium build sheriff, while testing.
+    notify_emails = ['orodley+test-tree-closing-notifier@chromium.org'],
+    failed_step_regexp = TREE_CLOSING_STEPS,
+)
+
+luci.notifier(
+    name = 'gpu-tree-closer-email',
+    on_occurrence = ['FAILURE'],
+    # Stand-in for chrome-gpu-build-failures@google.com and the GPU build
+    # sheriff, while testing.
+    notify_emails = ['orodley+gpu-test-tree-closing-notifier@chromium.org'],
+    failed_step_regexp = TREE_CLOSING_STEPS,
+)
diff --git a/infra/config/subprojects/chromium/master-only/ci.star b/infra/config/subprojects/chromium/master-only/ci.star
index b1d4a27..12a1f512 100644
--- a/infra/config/subprojects/chromium/master-only/ci.star
+++ b/infra/config/subprojects/chromium/master-only/ci.star
@@ -1935,6 +1935,7 @@
     console_view_entry = ci.console_view_entry(
         category = 'Linux',
     ),
+    tree_closing = False,
 )
 
 ci.gpu_builder(
@@ -1944,6 +1945,7 @@
     ),
     cores = None,
     os = os.MAC_ANY,
+    tree_closing = False,
 )
 
 ci.gpu_builder(
@@ -1953,6 +1955,7 @@
         category = 'Windows',
     ),
     os = os.WINDOWS_ANY,
+    tree_closing = False,
 )
 
 
@@ -1962,6 +1965,7 @@
         category = 'Linux',
     ),
     triggered_by = ['GPU Linux Builder (dbg)'],
+    tree_closing = False,
 )
 
 ci.gpu_thin_tester(
@@ -1970,6 +1974,7 @@
         category = 'Mac',
     ),
     triggered_by = ['GPU Mac Builder (dbg)'],
+    tree_closing = False,
 )
 
 ci.gpu_thin_tester(
@@ -1978,6 +1983,7 @@
         category = 'Mac',
     ),
     triggered_by = ['GPU Mac Builder (dbg)'],
+    tree_closing = False,
 )
 
 ci.gpu_thin_tester(
@@ -1986,6 +1992,7 @@
         category = 'Windows',
     ),
     triggered_by = ['GPU Win x64 Builder (dbg)'],
+    tree_closing = False,
 )
 
 
diff --git a/media/renderers/BUILD.gn b/media/renderers/BUILD.gn
index 3c9967b..b4f4e8d 100644
--- a/media/renderers/BUILD.gn
+++ b/media/renderers/BUILD.gn
@@ -61,6 +61,39 @@
   if (is_fuchsia) {
     deps += [ "//fuchsia/engine:switches" ]
   }
+
+  if (is_win) {
+    sources += [
+      "win/media_engine_extension.cc",
+      "win/media_engine_extension.h",
+      "win/media_engine_notify_impl.cc",
+      "win/media_engine_notify_impl.h",
+      "win/media_foundation_audio_stream.cc",
+      "win/media_foundation_audio_stream.h",
+      "win/media_foundation_protection_manager.cc",
+      "win/media_foundation_protection_manager.h",
+      "win/media_foundation_renderer.cc",
+      "win/media_foundation_renderer.h",
+      "win/media_foundation_renderer_extension.h",
+      "win/media_foundation_renderer_factory.cc",
+      "win/media_foundation_renderer_factory.h",
+      "win/media_foundation_source_wrapper.cc",
+      "win/media_foundation_source_wrapper.h",
+      "win/media_foundation_stream_wrapper.cc",
+      "win/media_foundation_stream_wrapper.h",
+      "win/media_foundation_video_stream.cc",
+      "win/media_foundation_video_stream.h",
+    ]
+
+    deps += [ "//media/base/win:media_foundation_util" ]
+
+    # media_foundation_renderer uses Windows D3D and Media Foundation APIs.
+    libs = [
+      "d3d11.lib",
+      "mfplat.lib",
+    ]
+  }
+
   configs += [
     "//media:subcomponent_config",
 
@@ -97,48 +130,15 @@
     "//ui/gl",
     "//ui/gl:test_support",
   ]
+
   if (is_win) {
     sources += [
       "win/media_foundation_renderer_integration_test.cc",
       "win/media_foundation_renderer_unittest.cc",
     ]
-    deps += [
-      "//media/renderers:media_foundation_renderer",
-      "//media/test:pipeline_integration_test_base",
-    ]
-  }
-}
-
-if (is_win) {
-  source_set("media_foundation_renderer") {
-    sources = [
-      "win/media_engine_extension.cc",
-      "win/media_engine_extension.h",
-      "win/media_engine_notify_impl.cc",
-      "win/media_engine_notify_impl.h",
-      "win/media_foundation_audio_stream.cc",
-      "win/media_foundation_audio_stream.h",
-      "win/media_foundation_protection_manager.cc",
-      "win/media_foundation_protection_manager.h",
-      "win/media_foundation_renderer.cc",
-      "win/media_foundation_renderer.h",
-      "win/media_foundation_renderer_extension.h",
-      "win/media_foundation_source_wrapper.cc",
-      "win/media_foundation_source_wrapper.h",
-      "win/media_foundation_stream_wrapper.cc",
-      "win/media_foundation_stream_wrapper.h",
-      "win/media_foundation_video_stream.cc",
-      "win/media_foundation_video_stream.h",
-    ]
-    deps = [
-      "//media",
-      "//media/base/win:media_foundation_util",
-    ]
-
-    # media_foundation_renderer uses Windows D3D and Media Foundation APIs.
+    deps += [ "//media/test:pipeline_integration_test_base" ]
     libs = [
-      "d3d11.lib",
-      "mfplat.lib",
+      "mfuuid.lib",  # For MFMediaType_Video etc
     ]
   }
 }
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc
index 71421c8..0c1e6b03 100644
--- a/media/renderers/audio_renderer_impl.cc
+++ b/media/renderers/audio_renderer_impl.cc
@@ -48,7 +48,7 @@
     media::AudioRendererSink* sink,
     const CreateAudioDecodersCB& create_audio_decoders_cb,
     MediaLog* media_log,
-    const TranscribeAudioCallback& transcribe_audio_callback)
+    SpeechRecognitionClient* speech_recognition_client)
     : task_runner_(task_runner),
       expecting_config_changes_(false),
       sink_(sink),
@@ -70,8 +70,12 @@
       received_end_of_stream_(false),
       rendered_end_of_stream_(false),
       is_suspending_(false),
+#if defined(OS_ANDROID)
+      is_passthrough_(false) {
+#else
       is_passthrough_(false),
-      transcribe_audio_callback_(transcribe_audio_callback) {
+      speech_recognition_client_(speech_recognition_client) {
+#endif
   DCHECK(create_audio_decoders_cb_);
 
   // PowerObserver's must be added and removed from the same thread, but we
@@ -369,6 +373,14 @@
   sink_->GetOutputDeviceInfoAsync(
       base::BindOnce(&AudioRendererImpl::OnDeviceInfoReceived,
                      weak_factory_.GetWeakPtr(), demuxer_stream_, cdm_context));
+
+#if !defined(OS_ANDROID)
+  if (speech_recognition_client_) {
+    speech_recognition_client_->SetOnReadyCallback(
+        base::BindOnce(&AudioRendererImpl::EnableSpeechRecognition,
+                       weak_factory_.GetWeakPtr()));
+  }
+#endif
 }
 
 void AudioRendererImpl::OnDeviceInfoReceived(
@@ -882,8 +894,10 @@
     if (first_packet_timestamp_ == kNoTimestamp)
       first_packet_timestamp_ = buffer->timestamp();
 
-    if (!transcribe_audio_callback_.is_null())
+#if !defined(OS_ANDROID)
+    if (transcribe_audio_callback_)
       transcribe_audio_callback_.Run(buffer);
+#endif
 
     if (state_ != kUninitialized)
       algorithm_->EnqueueBuffer(std::move(buffer));
@@ -1292,4 +1306,20 @@
   algorithm_->SetChannelMask(std::move(channel_mask));
 }
 
+void AudioRendererImpl::EnableSpeechRecognition() {
+#if !defined(OS_ANDROID)
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  transcribe_audio_callback_ = base::BindRepeating(
+      &AudioRendererImpl::TranscribeAudio, weak_factory_.GetWeakPtr());
+#endif
+}
+
+void AudioRendererImpl::TranscribeAudio(
+    scoped_refptr<media::AudioBuffer> buffer) {
+#if !defined(OS_ANDROID)
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  if (speech_recognition_client_)
+    speech_recognition_client_->AddAudio(std::move(buffer));
+#endif
+}
 }  // namespace media
diff --git a/media/renderers/audio_renderer_impl.h b/media/renderers/audio_renderer_impl.h
index 61c6b34..7755d20c 100644
--- a/media/renderers/audio_renderer_impl.h
+++ b/media/renderers/audio_renderer_impl.h
@@ -49,6 +49,7 @@
 class AudioBufferConverter;
 class AudioBus;
 class AudioClock;
+class SpeechRecognitionClient;
 
 class MEDIA_EXPORT AudioRendererImpl
     : public AudioRenderer,
@@ -62,6 +63,9 @@
   using TranscribeAudioCallback =
       base::RepeatingCallback<void(scoped_refptr<media::AudioBuffer>)>;
 
+  using EnableSpeechRecognitionCallback =
+      base::OnceCallback<void(TranscribeAudioCallback)>;
+
   // |task_runner| is the thread on which AudioRendererImpl will execute.
   //
   // |sink| is used as the destination for the rendered audio.
@@ -72,7 +76,7 @@
       AudioRendererSink* sink,
       const CreateAudioDecodersCB& create_audio_decoders_cb,
       MediaLog* media_log,
-      const TranscribeAudioCallback& transcribe_audio_callback);
+      SpeechRecognitionClient* speech_recognition_client = nullptr);
   ~AudioRendererImpl() override;
 
   // TimeSource implementation.
@@ -225,6 +229,9 @@
   // changes. Expect the layout in |last_decoded_channel_layout_|.
   void ConfigureChannelMask();
 
+  void EnableSpeechRecognition();
+  void TranscribeAudio(scoped_refptr<media::AudioBuffer> buffer);
+
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   std::unique_ptr<AudioBufferConverter> buffer_converter_;
@@ -355,7 +362,10 @@
 
   // End variables which must be accessed under |lock_|. ----------------------
 
+#if !defined(OS_ANDROID)
+  SpeechRecognitionClient* speech_recognition_client_;
   TranscribeAudioCallback transcribe_audio_callback_;
+#endif
 
   // NOTE: Weak pointers must be invalidated before all other member variables.
   base::WeakPtrFactory<AudioRendererImpl> weak_factory_{this};
diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc
index dc1dcee3..39df78084 100644
--- a/media/renderers/audio_renderer_impl_unittest.cc
+++ b/media/renderers/audio_renderer_impl_unittest.cc
@@ -133,9 +133,7 @@
         main_thread_task_runner_, sink_.get(),
         base::BindRepeating(&AudioRendererImplTest::CreateAudioDecoderForTest,
                             base::Unretained(this)),
-        &media_log_,
-        base::BindRepeating(&AudioRendererImplTest::TranscribeAudioCallback,
-                            base::Unretained(this))));
+        &media_log_, nullptr));
     renderer_->tick_clock_ = &tick_clock_;
     tick_clock_.Advance(base::TimeDelta::FromSeconds(1));
   }
@@ -162,9 +160,7 @@
         main_thread_task_runner_, sink_.get(),
         base::BindRepeating(&AudioRendererImplTest::CreateAudioDecoderForTest,
                             base::Unretained(this)),
-        &media_log_,
-        base::BindRepeating(&AudioRendererImplTest::TranscribeAudioCallback,
-                            base::Unretained(this))));
+        &media_log_, nullptr));
     testing::Mock::VerifyAndClearExpectations(&demuxer_stream_);
     ConfigureDemuxerStream(false);
   }
@@ -178,9 +174,7 @@
         main_thread_task_runner_, sink_.get(),
         base::BindRepeating(&AudioRendererImplTest::CreateAudioDecoderForTest,
                             base::Unretained(this)),
-        &media_log_,
-        base::BindRepeating(&AudioRendererImplTest::TranscribeAudioCallback,
-                            base::Unretained(this))));
+        &media_log_, nullptr));
     testing::Mock::VerifyAndClearExpectations(&demuxer_stream_);
     ConfigureDemuxerStream(true);
   }
@@ -191,9 +185,7 @@
         main_thread_task_runner_, mock_sink_.get(),
         base::BindRepeating(&AudioRendererImplTest::CreateAudioDecoderForTest,
                             base::Unretained(this)),
-        &media_log_,
-        base::BindRepeating(&AudioRendererImplTest::TranscribeAudioCallback,
-                            base::Unretained(this))));
+        &media_log_, nullptr));
     testing::Mock::VerifyAndClearExpectations(&demuxer_stream_);
     ConfigureDemuxerStream(true);
   }
@@ -255,9 +247,7 @@
         main_thread_task_runner_, sink_.get(),
         base::BindRepeating(&AudioRendererImplTest::CreateAudioDecoderForTest,
                             base::Unretained(this)),
-        &media_log_,
-        base::BindRepeating(&AudioRendererImplTest::TranscribeAudioCallback,
-                            base::Unretained(this))));
+        &media_log_, nullptr));
 
     Initialize();
   }
@@ -676,7 +666,7 @@
 
 TEST_F(AudioRendererImplTest, TranscribeAudioCallback) {
   Initialize();
-  EXPECT_CALL(*this, TranscribeAudioCallback(_)).Times(testing::AtLeast(1));
+  EXPECT_CALL(*this, TranscribeAudioCallback(_)).Times(0);
 
   Preroll();
   StartTicking();
diff --git a/media/renderers/default_renderer_factory.cc b/media/renderers/default_renderer_factory.cc
index 9792eac..8596f9f 100644
--- a/media/renderers/default_renderer_factory.cc
+++ b/media/renderers/default_renderer_factory.cc
@@ -10,7 +10,6 @@
 #include "base/bind.h"
 #include "build/build_config.h"
 #include "media/base/audio_buffer.h"
-#include "media/base/bind_to_current_loop.h"
 #include "media/base/decoder_factory.h"
 #include "media/renderers/audio_renderer_impl.h"
 #include "media/renderers/renderer_impl.h"
@@ -41,12 +40,6 @@
       get_gpu_factories_cb_(get_gpu_factories_cb),
       speech_recognition_client_(std::move(speech_recognition_client)) {
   DCHECK(decoder_factory_);
-  if (speech_recognition_client_) {
-    // Unretained is safe because |this| owns the speech recognition client.
-    speech_recognition_client_->SetOnReadyCallback(media::BindToCurrentLoop(
-        base::BindOnce(&DefaultRendererFactory::EnableSpeechRecognition,
-                       base::Unretained(this))));
-  }
 }
 #endif
 
@@ -98,9 +91,11 @@
       // finishes.
       base::BindRepeating(&DefaultRendererFactory::CreateAudioDecoders,
                           base::Unretained(this), media_task_runner),
-      media_log_,
-      BindToCurrentLoop(base::BindRepeating(
-          &DefaultRendererFactory::TranscribeAudio, base::Unretained(this)))));
+#if defined(OS_ANDROID)
+      media_log_));
+#else
+      media_log_, speech_recognition_client_.get()));
+#endif
 
   GpuVideoAcceleratorFactories* gpu_factories = nullptr;
   if (get_gpu_factories_cb_)
@@ -132,21 +127,4 @@
       media_task_runner, std::move(audio_renderer), std::move(video_renderer));
 }
 
-void DefaultRendererFactory::TranscribeAudio(
-    scoped_refptr<media::AudioBuffer> buffer) {
-#if !defined(OS_ANDROID)
-  if (is_speech_recognition_available_ && speech_recognition_client_)
-    speech_recognition_client_->AddAudio(std::move(buffer));
-#endif
-}
-
-void DefaultRendererFactory::EnableSpeechRecognition() {
-#if !defined(OS_ANDROID)
-  if (speech_recognition_client_) {
-    is_speech_recognition_available_ =
-        speech_recognition_client_->IsSpeechRecognitionAvailable();
-  }
-#endif
-}
-
 }  // namespace media
diff --git a/media/renderers/default_renderer_factory.h b/media/renderers/default_renderer_factory.h
index d354c56..455ce1bb 100644
--- a/media/renderers/default_renderer_factory.h
+++ b/media/renderers/default_renderer_factory.h
@@ -61,8 +61,6 @@
       RequestOverlayInfoCB request_overlay_info_cb,
       const gfx::ColorSpace& target_color_space) final;
 
-  void TranscribeAudio(scoped_refptr<media::AudioBuffer> buffer);
-
  private:
   std::vector<std::unique_ptr<AudioDecoder>> CreateAudioDecoders(
       const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner);
@@ -72,8 +70,6 @@
       const gfx::ColorSpace& target_color_space,
       GpuVideoAcceleratorFactories* gpu_factories);
 
-  void EnableSpeechRecognition();
-
   MediaLog* media_log_;
 
   // Factory to create extra audio and video decoders.
@@ -85,7 +81,6 @@
 
 #if !defined(OS_ANDROID)
   std::unique_ptr<SpeechRecognitionClient> speech_recognition_client_;
-  bool is_speech_recognition_available_ = false;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(DefaultRendererFactory);
diff --git a/media/renderers/win/media_foundation_renderer.h b/media/renderers/win/media_foundation_renderer.h
index d71cc5d..94517c2 100644
--- a/media/renderers/win/media_foundation_renderer.h
+++ b/media/renderers/win/media_foundation_renderer.h
@@ -18,6 +18,7 @@
 #include "base/unguessable_token.h"
 #include "base/win/windows_types.h"
 #include "media/base/buffering_state.h"
+#include "media/base/media_export.h"
 #include "media/base/media_resource.h"
 #include "media/base/pipeline_status.h"
 #include "media/base/renderer.h"
@@ -33,8 +34,9 @@
 
 // MediaFoundationRenderer bridges the Renderer and Windows MFMediaEngine
 // interfaces.
-class MediaFoundationRenderer : public Renderer,
-                                public MediaFoundationRendererExtension {
+class MEDIA_EXPORT MediaFoundationRenderer
+    : public Renderer,
+      public MediaFoundationRendererExtension {
  public:
   // Whether MediaFoundationRenderer() is supported on the current device.
   static bool IsSupported();
diff --git a/media/renderers/win/media_foundation_renderer_factory.cc b/media/renderers/win/media_foundation_renderer_factory.cc
new file mode 100644
index 0000000..a891bc5
--- /dev/null
+++ b/media/renderers/win/media_foundation_renderer_factory.cc
@@ -0,0 +1,34 @@
+// 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 "media/renderers/win/media_foundation_renderer_factory.h"
+
+#include "base/single_thread_task_runner.h"
+#include "media/renderers/win/media_foundation_renderer.h"
+
+namespace media {
+
+MediaFoundationRendererFactory::MediaFoundationRendererFactory() = default;
+
+MediaFoundationRendererFactory::~MediaFoundationRendererFactory() = default;
+
+// MediaFoundationRenderer does most audio/video rendering by itself and doesn't
+// use most of the parameters passed in.
+std::unique_ptr<Renderer> MediaFoundationRendererFactory::CreateRenderer(
+    const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+    const scoped_refptr<base::TaskRunner>& /*worker_task_runner*/,
+    AudioRendererSink* /*audio_renderer_sink*/,
+    VideoRendererSink* /*video_renderer_sink*/,
+    RequestOverlayInfoCB /*request_overlay_info_cb*/,
+    const gfx::ColorSpace& /*target_color_space*/) {
+  // TODO(xhwang): Investigate how to set |muted| and update after composition
+  // is connected.
+  auto renderer = std::make_unique<MediaFoundationRenderer>(
+      /*muted=*/false, media_task_runner,
+      /*force_dcomp_mode_for_testing=*/true);
+  renderer->SetPlaybackElementId(next_playback_element_id_++);
+  return renderer;
+}
+
+}  // namespace media
\ No newline at end of file
diff --git a/media/renderers/win/media_foundation_renderer_factory.h b/media/renderers/win/media_foundation_renderer_factory.h
new file mode 100644
index 0000000..c7ca91ad
--- /dev/null
+++ b/media/renderers/win/media_foundation_renderer_factory.h
@@ -0,0 +1,40 @@
+// 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 MEDIA_RENDERERS_WIN_MEDIA_FOUNDATION_RENDERER_FACTORY_H_
+#define MEDIA_RENDERERS_WIN_MEDIA_FOUNDATION_RENDERER_FACTORY_H_
+
+#include <stdint.h>
+
+#include "media/base/media_export.h"
+#include "media/base/renderer_factory.h"
+
+namespace media {
+
+// RendererFactory to create MediaFoundationRendererFactory.
+class MEDIA_EXPORT MediaFoundationRendererFactory : public RendererFactory {
+ public:
+  MediaFoundationRendererFactory();
+  MediaFoundationRendererFactory(const MediaFoundationRendererFactory&) =
+      delete;
+  MediaFoundationRendererFactory& operator=(
+      const MediaFoundationRendererFactory&) = delete;
+  ~MediaFoundationRendererFactory() final;
+
+  // RendererFactory implementation.
+  std::unique_ptr<Renderer> CreateRenderer(
+      const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+      const scoped_refptr<base::TaskRunner>& worker_task_runner,
+      AudioRendererSink* audio_renderer_sink,
+      VideoRendererSink* video_renderer_sink,
+      RequestOverlayInfoCB request_overlay_info_cb,
+      const gfx::ColorSpace& target_color_space) final;
+
+ private:
+  uint64_t next_playback_element_id_ = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_RENDERERS_WIN_MEDIA_FOUNDATION_RENDERER_FACTORY_H_
diff --git a/media/renderers/win/media_foundation_renderer_integration_test.cc b/media/renderers/win/media_foundation_renderer_integration_test.cc
index a76833ef..184e229 100644
--- a/media/renderers/win/media_foundation_renderer_integration_test.cc
+++ b/media/renderers/win/media_foundation_renderer_integration_test.cc
@@ -6,6 +6,8 @@
 
 #include <memory>
 
+#include <mfapi.h>
+
 #include "media/test/pipeline_integration_test_base.h"
 #include "media/test/test_media_source.h"
 
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc
index 70a97d2b..2f153c38 100644
--- a/media/test/pipeline_integration_test_base.cc
+++ b/media/test/pipeline_integration_test_base.cc
@@ -529,7 +529,7 @@
       base::BindRepeating(&CreateAudioDecodersForTest, &media_log_,
                           task_environment_.GetMainThreadTaskRunner(),
                           prepend_audio_decoders_cb_),
-      &media_log_, AudioRendererImpl::TranscribeAudioCallback()));
+      &media_log_, nullptr));
   if (hashing_enabled_) {
     if (clockless_playback_)
       clockless_audio_sink_->StartAudioHashForTesting();
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index 0d2080c..27d18a16 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -1885,7 +1885,10 @@
 
     group(js_data_deps_target_name) {
       data = js_outputs
-      deps = [ ":$generator_js_target_name" ]
+      deps = []
+      foreach(generator_js_target_name, generator_js_target_names) {
+        deps += [ ":$generator_js_target_name" ]
+      }
       data_deps = []
       foreach(d, all_deps) {
         full_name = get_label_info(d, "label_no_toolchain")
diff --git a/net/base/file_stream_unittest.cc b/net/base/file_stream_unittest.cc
index 86b40b3..c8656a9 100644
--- a/net/base/file_stream_unittest.cc
+++ b/net/base/file_stream_unittest.cc
@@ -68,7 +68,7 @@
     // FileStreamContexts must be asynchronously closed on the file task runner
     // before they can be deleted. Pump the RunLoop to avoid leaks.
     base::RunLoop().RunUntilIdle();
-    EXPECT_TRUE(base::DeleteFile(temp_file_path_, false));
+    EXPECT_TRUE(base::DeleteFile(temp_file_path_));
 
     PlatformTest::TearDown();
   }
@@ -141,7 +141,7 @@
   read_stream.reset();
 
   // 2. Test writing with a file handle.
-  base::DeleteFile(temp_file_path(), false);
+  base::DeleteFile(temp_file_path());
   flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE |
           base::File::FLAG_ASYNC;
   file.Initialize(temp_file_path(), flags);
diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc
index e649007..3e98e97 100644
--- a/net/disk_cache/backend_unittest.cc
+++ b/net/disk_cache/backend_unittest.cc
@@ -781,8 +781,7 @@
 
   // Delete the index.
   base::DeleteFile(
-      cache_path_.AppendASCII("index-dir").AppendASCII("the-real-index"),
-      false);
+      cache_path_.AppendASCII("index-dir").AppendASCII("the-real-index"));
 
   // Open the cache again. The fixture will also waits for index init.
   InitCache();
@@ -798,7 +797,7 @@
 TEST_F(DiskCacheBackendTest, CreateBackend_MissingFile) {
   ASSERT_TRUE(CopyTestCache("bad_entry"));
   base::FilePath filename = cache_path_.AppendASCII("data_1");
-  base::DeleteFile(filename, false);
+  base::DeleteFile(filename);
   net::TestCompletionCallback cb;
 
   bool prev = base::ThreadRestrictions::SetIOAllowed(false);
@@ -3931,7 +3930,7 @@
   EXPECT_TRUE(file2.IsValid());
 #endif
 
-  EXPECT_TRUE(base::DeleteFile(name, false));
+  EXPECT_TRUE(base::DeleteFile(name));
 
   // We should be able to use the file.
   const int kSize = 200;
diff --git a/net/disk_cache/cache_util_posix.cc b/net/disk_cache/cache_util_posix.cc
index 951e5b2..ed57ae5 100644
--- a/net/disk_cache/cache_util_posix.cc
+++ b/net/disk_cache/cache_util_posix.cc
@@ -40,7 +40,7 @@
 }
 
 bool DeleteCacheFile(const base::FilePath& name) {
-  return base::DeleteFile(name, false);
+  return base::DeleteFile(name);
 }
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc
index 68b8319..6030b792 100644
--- a/net/disk_cache/entry_unittest.cc
+++ b/net/disk_cache/entry_unittest.cc
@@ -1556,7 +1556,7 @@
 
   disk_cache::Addr address(0x80000001);
   base::FilePath name = cache_impl_->GetFileName(address);
-  EXPECT_TRUE(base::DeleteFile(name, false));
+  EXPECT_TRUE(base::DeleteFile(name));
 
   // Attempt to read the data.
   ASSERT_THAT(OpenEntry(key, &entry), IsOk());
diff --git a/net/disk_cache/simple/simple_file_tracker_unittest.cc b/net/disk_cache/simple/simple_file_tracker_unittest.cc
index 0c34224a..0bbebdf1 100644
--- a/net/disk_cache/simple/simple_file_tracker_unittest.cc
+++ b/net/disk_cache/simple/simple_file_tracker_unittest.cc
@@ -307,7 +307,7 @@
                                      disk_cache::FD_LIMIT_FAIL_REOPEN_FILE, 0);
 
   // Delete file for [2], to cause error on its re-open.
-  EXPECT_TRUE(base::DeleteFile(names[2], false)) << names[2];
+  EXPECT_TRUE(base::DeleteFile(names[2])) << names[2];
 
   // Reacquire all the other files.
   for (int i = 0; i < kEntries - 1; ++i) {
diff --git a/net/disk_cache/simple/simple_index_file.cc b/net/disk_cache/simple/simple_index_file.cc
index 06a0fff5..789caa6 100644
--- a/net/disk_cache/simple/simple_index_file.cc
+++ b/net/disk_cache/simple/simple_index_file.cc
@@ -145,7 +145,7 @@
 
   // Cleanup any left over doomed entries.
   if (base::StartsWith(file_name, "todelete_", base::CompareCase::SENSITIVE)) {
-    base::DeleteFile(file_path, false);
+    base::DeleteFile(file_path);
     return;
   }
 
diff --git a/net/disk_cache/simple/simple_synchronous_entry.cc b/net/disk_cache/simple/simple_synchronous_entry.cc
index 140067a5..f4278dde 100644
--- a/net/disk_cache/simple/simple_synchronous_entry.cc
+++ b/net/disk_cache/simple/simple_synchronous_entry.cc
@@ -1271,10 +1271,8 @@
     // this before calling SimpleFileTracker::Close, since that would make the
     // name available to other threads.
     if (entry_file_key_.doom_generation != 0u) {
-      base::DeleteFile(
-          path_.AppendASCII(
-              GetFilenameFromEntryFileKeyAndFileIndex(entry_file_key_, index)),
-          false);
+      base::DeleteFile(path_.AppendASCII(
+          GetFilenameFromEntryFileKeyAndFileIndex(entry_file_key_, index)));
     }
     file_tracker_->Close(this, SubFileForFileIndex(index));
   }
@@ -1691,7 +1689,7 @@
                                                     const int file_index) {
   FilePath to_delete = path.AppendASCII(GetFilenameFromEntryFileKeyAndFileIndex(
       SimpleFileTracker::EntryFileKey(entry_hash), file_index));
-  return base::DeleteFile(to_delete, false);
+  return base::DeleteFile(to_delete);
 }
 
 // static
diff --git a/net/disk_cache/simple/simple_util_posix.cc b/net/disk_cache/simple/simple_util_posix.cc
index e8bb0a3..129b9ee 100644
--- a/net/disk_cache/simple/simple_util_posix.cc
+++ b/net/disk_cache/simple/simple_util_posix.cc
@@ -10,7 +10,7 @@
 namespace simple_util {
 
 bool SimpleCacheDeleteFile(const base::FilePath& path) {
-  return base::DeleteFile(path, false);
+  return base::DeleteFile(path);
 }
 
 }  // namespace simple_util
diff --git a/net/disk_cache/simple/simple_version_upgrade.cc b/net/disk_cache/simple/simple_version_upgrade.cc
index f21de84..88e65d14 100644
--- a/net/disk_cache/simple/simple_version_upgrade.cc
+++ b/net/disk_cache/simple/simple_version_upgrade.cc
@@ -107,9 +107,7 @@
 bool UpgradeIndexV5V6(const base::FilePath& cache_directory) {
   const base::FilePath old_index_file =
       cache_directory.AppendASCII(kIndexFileName);
-  if (!base::DeleteFile(old_index_file, /* recursive = */ false))
-    return false;
-  return true;
+  return base::DeleteFile(old_index_file);
 }
 
 // Some points about the Upgrade process are still not clear:
@@ -145,7 +143,7 @@
   if (!fake_index_file.IsValid()) {
     if (fake_index_file.error_details() == base::File::FILE_ERROR_NOT_FOUND) {
       if (!WriteFakeIndexFile(fake_index)) {
-        base::DeleteFile(fake_index, /* recursive = */ false);
+        base::DeleteFile(fake_index);
         LOG(ERROR) << "Failed to write a new fake index.";
         return SimpleCacheConsistencyResult::kWriteFakeIndexFileFailed;
       }
@@ -223,7 +221,7 @@
 
   const base::FilePath temp_fake_index = path.AppendASCII("upgrade-index");
   if (!WriteFakeIndexFile(temp_fake_index)) {
-    base::DeleteFile(temp_fake_index, /* recursive = */ false);
+    base::DeleteFile(temp_fake_index);
     LOG(ERROR) << "Failed to write a new fake index.";
     LogMessageFailedUpgradeFromVersion(file_header.version);
     return SimpleCacheConsistencyResult::kWriteFakeIndexFileFailed;
@@ -250,11 +248,9 @@
       continue;
     return false;
   }
-  bool deleted_fake_index =
-      base::DeleteFile(fake_index, /* recursive = */ false);
+  bool deleted_fake_index = base::DeleteFile(fake_index);
   bool deleted_index_dir = base::DeleteFileRecursively(index_dir);
-  bool deleted_legacy_index_file =
-      base::DeleteFile(legacy_index_file, /* recursive = */ false);
+  bool deleted_legacy_index_file = base::DeleteFile(legacy_index_file);
   return deleted_fake_index || deleted_index_dir || deleted_legacy_index_file;
 }
 
diff --git a/net/dns/dns_config_service_posix_unittest.cc b/net/dns/dns_config_service_posix_unittest.cc
index 8989240..30faa092 100644
--- a/net/dns/dns_config_service_posix_unittest.cc
+++ b/net/dns/dns_config_service_posix_unittest.cc
@@ -241,7 +241,7 @@
     service_.reset(new DnsConfigServicePosix());
   }
 
-  void TearDown() override { ASSERT_TRUE(base::DeleteFile(temp_file_, false)); }
+  void TearDown() override { ASSERT_TRUE(base::DeleteFile(temp_file_)); }
 
   base::test::TaskEnvironment task_environment_;
   bool seen_config_;
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index b01179e..8c08562b 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -12643,7 +12643,7 @@
 
   EXPECT_FALSE(response->headers);
 
-  base::DeleteFile(temp_file_path, false);
+  base::DeleteFile(temp_file_path);
 }
 
 TEST_F(HttpNetworkTransactionTest, UploadUnreadableFile) {
@@ -12683,7 +12683,7 @@
   rv = callback.WaitForResult();
   EXPECT_THAT(rv, IsError(ERR_ACCESS_DENIED));
 
-  base::DeleteFile(temp_file, false);
+  base::DeleteFile(temp_file);
 }
 
 TEST_F(HttpNetworkTransactionTest, CancelDuringInitRequestBody) {
diff --git a/net/log/file_net_log_observer.cc b/net/log/file_net_log_observer.cc
index 1374fecc..9829243 100644
--- a/net/log/file_net_log_observer.cc
+++ b/net/log/file_net_log_observer.cc
@@ -110,7 +110,7 @@
 
   // Now that it has been copied, delete the source file.
   source_file.reset();
-  base::DeleteFile(source_path, false);
+  base::DeleteFile(source_path);
 }
 
 base::FilePath SiblingInprogressDirectory(const base::FilePath& log_path) {
@@ -634,7 +634,7 @@
   // Only delete |final_log_file_| if it was created internally.
   // (If it was provided as a base::File by the caller, don't try to delete it).
   if (!final_log_path_.empty())
-    base::DeleteFile(final_log_path_, false);
+    base::DeleteFile(final_log_path_);
 }
 
 void FileNetLogObserver::FileWriter::FlushThenStop(
diff --git a/net/log/net_log_util.cc b/net/log/net_log_util.cc
index 91c57cc..749237f 100644
--- a/net/log/net_log_util.cc
+++ b/net/log/net_log_util.cc
@@ -353,13 +353,12 @@
   return constants_dict;
 }
 
-NET_EXPORT std::unique_ptr<base::DictionaryValue> GetNetInfo(
-    URLRequestContext* context,
-    int info_sources) {
+NET_EXPORT base::Value GetNetInfo(URLRequestContext* context,
+                                  int info_sources) {
   // May only be called on the context's thread.
   context->AssertCalledOnValidThread();
 
-  std::unique_ptr<base::DictionaryValue> net_info_dict =
+  base::Value net_info_dict =
       context->proxy_resolution_service()->GetProxyNetLogValues(info_sources);
 
   if (info_sources & NET_INFO_HOST_RESOLVER) {
@@ -367,27 +366,29 @@
     DCHECK(host_resolver);
     HostCache* cache = host_resolver->GetHostCache();
     if (cache) {
-      auto dict = std::make_unique<base::DictionaryValue>();
+      base::Value dict(base::Value::Type::DICTIONARY);
       std::unique_ptr<base::Value> dns_config =
           host_resolver->GetDnsConfigAsValue();
       if (dns_config)
-        dict->Set("dns_config", std::move(dns_config));
+        dict.SetKey("dns_config",
+                    base::Value::FromUniquePtrValue(std::move(dns_config)));
 
-      auto cache_info_dict = std::make_unique<base::DictionaryValue>();
-      auto cache_contents_list = std::make_unique<base::ListValue>();
+      base::Value cache_info_dict(base::Value::Type::DICTIONARY);
+      base::Value cache_contents_list(base::Value::Type::LIST);
 
-      cache_info_dict->SetInteger("capacity",
-                                  static_cast<int>(cache->max_entries()));
-      cache_info_dict->SetInteger("network_changes", cache->network_changes());
+      cache_info_dict.SetIntKey("capacity",
+                                static_cast<int>(cache->max_entries()));
+      cache_info_dict.SetIntKey("network_changes", cache->network_changes());
 
-      cache->GetAsListValue(cache_contents_list.get(),
-                            true /* include_staleness */,
-                            HostCache::SerializationType::kDebug);
-      cache_info_dict->Set("entries", std::move(cache_contents_list));
+      base::ListValue* list_value = nullptr;
+      if (cache_contents_list.GetAsList(&list_value))
+        cache->GetAsListValue(list_value, true /* include_staleness */,
+                              HostCache::SerializationType::kDebug);
+      cache_info_dict.SetKey("entries", std::move(cache_contents_list));
 
-      dict->Set("cache", std::move(cache_info_dict));
-      net_info_dict->Set(NetInfoSourceToString(NET_INFO_HOST_RESOLVER),
-                         std::move(dict));
+      dict.SetKey("cache", std::move(cache_info_dict));
+      net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_HOST_RESOLVER),
+                           std::move(dict));
     }
   }
 
@@ -395,20 +396,23 @@
       context->http_transaction_factory()->GetSession();
 
   if (info_sources & NET_INFO_SOCKET_POOL) {
-    net_info_dict->Set(NetInfoSourceToString(NET_INFO_SOCKET_POOL),
-                       http_network_session->SocketPoolInfoToValue());
+    net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_SOCKET_POOL),
+                         base::Value::FromUniquePtrValue(
+                             http_network_session->SocketPoolInfoToValue()));
   }
 
   if (info_sources & NET_INFO_SPDY_SESSIONS) {
-    net_info_dict->Set(NetInfoSourceToString(NET_INFO_SPDY_SESSIONS),
-                       http_network_session->SpdySessionPoolInfoToValue());
+    net_info_dict.SetKey(
+        NetInfoSourceToString(NET_INFO_SPDY_SESSIONS),
+        base::Value::FromUniquePtrValue(
+            http_network_session->SpdySessionPoolInfoToValue()));
   }
 
   if (info_sources & NET_INFO_SPDY_STATUS) {
-    auto status_dict = std::make_unique<base::DictionaryValue>();
+    base::Value status_dict(base::Value::Type::DICTIONARY);
 
-    status_dict->SetBoolean("enable_http2",
-                            http_network_session->params().enable_http2);
+    status_dict.SetBoolKey("enable_http2",
+                           http_network_session->params().enable_http2);
 
     NextProtoVector alpn_protos;
     http_network_session->GetAlpnProtos(&alpn_protos);
@@ -419,29 +423,31 @@
           next_protos_string.append(",");
         next_protos_string.append(NextProtoToString(proto));
       }
-      status_dict->SetString("alpn_protos", next_protos_string);
+      status_dict.SetStringKey("alpn_protos", next_protos_string);
     }
 
-    net_info_dict->Set(NetInfoSourceToString(NET_INFO_SPDY_STATUS),
-                       std::move(status_dict));
+    net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_SPDY_STATUS),
+                         std::move(status_dict));
   }
 
   if (info_sources & NET_INFO_ALT_SVC_MAPPINGS) {
     const HttpServerProperties& http_server_properties =
         *context->http_server_properties();
-    net_info_dict->Set(
+    net_info_dict.SetKey(
         NetInfoSourceToString(NET_INFO_ALT_SVC_MAPPINGS),
-        http_server_properties.GetAlternativeServiceInfoAsValue());
+        base::Value::FromUniquePtrValue(
+            http_server_properties.GetAlternativeServiceInfoAsValue()));
   }
 
   if (info_sources & NET_INFO_QUIC) {
-    net_info_dict->Set(NetInfoSourceToString(NET_INFO_QUIC),
-                       http_network_session->QuicInfoToValue());
+    net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_QUIC),
+                         base::Value::FromUniquePtrValue(
+                             http_network_session->QuicInfoToValue()));
   }
 
   if (info_sources & NET_INFO_HTTP_CACHE) {
-    auto info_dict = std::make_unique<base::DictionaryValue>();
-    auto stats_dict = std::make_unique<base::DictionaryValue>();
+    base::Value info_dict(base::Value::Type::DICTIONARY);
+    base::Value stats_dict(base::Value::Type::DICTIONARY);
 
     disk_cache::Backend* disk_cache = GetDiskCacheBackend(context);
 
@@ -449,14 +455,14 @@
       // Extract the statistics key/value pairs from the backend.
       base::StringPairs stats;
       disk_cache->GetStats(&stats);
-      for (size_t i = 0; i < stats.size(); ++i) {
-        stats_dict->SetKey(stats[i].first, base::Value(stats[i].second));
+      for (auto& stat : stats) {
+        stats_dict.SetKey(stat.first, base::Value(std::move(stat.second)));
       }
     }
-    info_dict->Set("stats", std::move(stats_dict));
+    info_dict.SetKey("stats", std::move(stats_dict));
 
-    net_info_dict->Set(NetInfoSourceToString(NET_INFO_HTTP_CACHE),
-                       std::move(info_dict));
+    net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_HTTP_CACHE),
+                         std::move(info_dict));
   }
 
   if (info_sources & NET_INFO_REPORTING) {
@@ -470,20 +476,20 @@
         reporting_dict.SetKey("networkErrorLogging",
                               network_error_logging_service->StatusAsValue());
       }
-      net_info_dict->SetKey(NetInfoSourceToString(NET_INFO_REPORTING),
-                            std::move(reporting_dict));
+      net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_REPORTING),
+                           std::move(reporting_dict));
     } else {
       base::Value reporting_dict(base::Value::Type::DICTIONARY);
       reporting_dict.SetKey("reportingEnabled", base::Value(false));
-      net_info_dict->SetKey(NetInfoSourceToString(NET_INFO_REPORTING),
-                            std::move(reporting_dict));
+      net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_REPORTING),
+                           std::move(reporting_dict));
     }
 
 #else   // BUILDFLAG(ENABLE_REPORTING)
     base::Value reporting_dict(base::Value::Type::DICTIONARY);
     reporting_dict.SetKey("reportingEnabled", base::Value(false));
-    net_info_dict->SetKey(NetInfoSourceToString(NET_INFO_REPORTING),
-                          std::move(reporting_dict));
+    net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_REPORTING),
+                         std::move(reporting_dict));
 #endif  // BUILDFLAG(ENABLE_REPORTING)
   }
 
diff --git a/net/log/net_log_util.h b/net/log/net_log_util.h
index b6391aa..5de85dc 100644
--- a/net/log/net_log_util.h
+++ b/net/log/net_log_util.h
@@ -11,10 +11,6 @@
 #include "net/base/net_export.h"
 #include "net/log/net_log.h"
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace net {
 
 class URLRequestContext;
@@ -42,9 +38,7 @@
 // one top-level entry to the returned dictionary.
 //
 // May only be called on |context|'s thread.
-NET_EXPORT std::unique_ptr<base::DictionaryValue> GetNetInfo(
-    URLRequestContext* context,
-    int info_sources);
+NET_EXPORT base::Value GetNetInfo(URLRequestContext* context, int info_sources);
 
 // Takes in a set of contexts and a NetLog::Observer, and passes in
 // NetLog::Entries to the observer for certain NetLogSources with pending
diff --git a/net/log/net_log_util_unittest.cc b/net/log/net_log_util_unittest.cc
index 7da26248..2189cd5 100644
--- a/net/log/net_log_util_unittest.cc
+++ b/net/log/net_log_util_unittest.cc
@@ -40,21 +40,20 @@
 
   // Get NetInfo when there's no cache backend (It's only created on first use).
   EXPECT_FALSE(http_cache->GetCurrentBackend());
-  std::unique_ptr<base::DictionaryValue> net_info_without_cache(
+  base::Value net_info_without_cache(
       GetNetInfo(&context, NET_INFO_ALL_SOURCES));
   EXPECT_FALSE(http_cache->GetCurrentBackend());
-  EXPECT_GT(net_info_without_cache->size(), 0u);
+  EXPECT_GT(net_info_without_cache.DictSize(), 0u);
 
   // Fore creation of a cache backend, and get NetInfo again.
   disk_cache::Backend* backend = nullptr;
   EXPECT_EQ(OK, context.http_transaction_factory()->GetCache()->GetBackend(
                     &backend, TestCompletionCallback().callback()));
   EXPECT_TRUE(http_cache->GetCurrentBackend());
-  std::unique_ptr<base::DictionaryValue> net_info_with_cache(
-      GetNetInfo(&context, NET_INFO_ALL_SOURCES));
-  EXPECT_GT(net_info_with_cache->size(), 0u);
+  base::Value net_info_with_cache = GetNetInfo(&context, NET_INFO_ALL_SOURCES);
+  EXPECT_GT(net_info_with_cache.DictSize(), 0u);
 
-  EXPECT_EQ(net_info_without_cache->size(), net_info_with_cache->size());
+  EXPECT_EQ(net_info_without_cache.DictSize(), net_info_with_cache.DictSize());
 }
 
 // Make sure CreateNetLogEntriesForActiveObjects works for requests from a
diff --git a/net/proxy_resolution/configured_proxy_resolution_service.cc b/net/proxy_resolution/configured_proxy_resolution_service.cc
index 68fc89b5..9533773 100644
--- a/net/proxy_resolution/configured_proxy_resolution_service.cc
+++ b/net/proxy_resolution/configured_proxy_resolution_service.cc
@@ -1348,39 +1348,38 @@
   ApplyProxyConfigIfAvailable();
 }
 
-std::unique_ptr<base::DictionaryValue>
-ConfiguredProxyResolutionService::GetProxyNetLogValues(int info_sources) {
-  std::unique_ptr<base::DictionaryValue> net_info_dict(
-      new base::DictionaryValue());
+base::Value ConfiguredProxyResolutionService::GetProxyNetLogValues(
+    int info_sources) {
+  base::Value net_info_dict(base::Value::Type::DICTIONARY);
 
   if (info_sources & NET_INFO_PROXY_SETTINGS) {
-    std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+    base::Value dict(base::Value::Type::DICTIONARY);
     if (fetched_config_)
-      dict->SetKey("original", fetched_config_->value().ToValue());
+      dict.SetKey("original", fetched_config_->value().ToValue());
     if (config_)
-      dict->SetKey("effective", config_->value().ToValue());
+      dict.SetKey("effective", config_->value().ToValue());
 
-    net_info_dict->Set(NetInfoSourceToString(NET_INFO_PROXY_SETTINGS),
-                       std::move(dict));
+    net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_PROXY_SETTINGS),
+                         std::move(dict));
   }
 
   if (info_sources & NET_INFO_BAD_PROXIES) {
-    auto list = std::make_unique<base::ListValue>();
+    base::Value list(base::Value::Type::LIST);
 
-    for (auto& it : proxy_retry_info_) {
+    for (const auto& it : proxy_retry_info_) {
       const std::string& proxy_uri = it.first;
       const ProxyRetryInfo& retry_info = it.second;
 
-      auto dict = std::make_unique<base::DictionaryValue>();
-      dict->SetString("proxy_uri", proxy_uri);
-      dict->SetString("bad_until",
-                      NetLog::TickCountToString(retry_info.bad_until));
+      base::Value dict(base::Value::Type::DICTIONARY);
+      dict.SetStringKey("proxy_uri", proxy_uri);
+      dict.SetStringKey("bad_until",
+                        NetLog::TickCountToString(retry_info.bad_until));
 
-      list->Append(std::move(dict));
+      list.Append(std::move(dict));
     }
 
-    net_info_dict->Set(NetInfoSourceToString(NET_INFO_BAD_PROXIES),
-                       std::move(list));
+    net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_BAD_PROXIES),
+                         std::move(list));
   }
 
   return net_info_dict;
diff --git a/net/proxy_resolution/configured_proxy_resolution_service.h b/net/proxy_resolution/configured_proxy_resolution_service.h
index 768ca0f7..879d194f 100644
--- a/net/proxy_resolution/configured_proxy_resolution_service.h
+++ b/net/proxy_resolution/configured_proxy_resolution_service.h
@@ -171,8 +171,7 @@
   void ForceReloadProxyConfig();
 
   // ProxyResolutionService
-  std::unique_ptr<base::DictionaryValue> GetProxyNetLogValues(
-      int info_sources) override;
+  base::Value GetProxyNetLogValues(int info_sources) override;
 
   // ProxyResolutionService
   bool CastToConfiguredProxyResolutionService(
diff --git a/net/proxy_resolution/proxy_resolution_service.h b/net/proxy_resolution/proxy_resolution_service.h
index 4a49fc8..93673f7 100644
--- a/net/proxy_resolution/proxy_resolution_service.h
+++ b/net/proxy_resolution/proxy_resolution_service.h
@@ -18,10 +18,6 @@
 #include "net/proxy_resolution/proxy_info.h"
 #include "url/gurl.h"
 
-namespace base {
-class DictionaryValue;
-}  // namespace base
-
 namespace net {
 
 class ConfiguredProxyResolutionService;
@@ -99,8 +95,7 @@
   // Returns proxy related debug information to be included in the NetLog. The
   // data should be appropriate for any capture mode. |info_sources| is a bit
   // field of NET_INFO_SOURCE.
-  virtual std::unique_ptr<base::DictionaryValue> GetProxyNetLogValues(
-      int info_sources) = 0;
+  virtual base::Value GetProxyNetLogValues(int info_sources) = 0;
 
   // Returns true if |this| is an instance of ConfiguredProxyResolutionService
   // and assigns |this| to the out parameter. Otherwise returns false and sets
diff --git a/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc b/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc
index 16fc564..080af7a 100644
--- a/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc
+++ b/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc
@@ -48,10 +48,9 @@
   return proxy_retry_info_;
 }
 
-std::unique_ptr<base::DictionaryValue>
-WindowsSystemProxyResolutionService::GetProxyNetLogValues(int info_sources) {
-  std::unique_ptr<base::DictionaryValue> net_info_dict(
-      new base::DictionaryValue());
+base::Value WindowsSystemProxyResolutionService::GetProxyNetLogValues(
+    int info_sources) {
+  base::Value net_info_dict(base::Value::Type::DICTIONARY);
   return net_info_dict;
 }
 
diff --git a/net/proxy_resolution/win/windows_system_proxy_resolution_service.h b/net/proxy_resolution/win/windows_system_proxy_resolution_service.h
index f5b820f..830ef9b 100644
--- a/net/proxy_resolution/win/windows_system_proxy_resolution_service.h
+++ b/net/proxy_resolution/win/windows_system_proxy_resolution_service.h
@@ -47,8 +47,7 @@
       const NetLogWithSource& net_log) override;
   void ClearBadProxiesCache() override;
   const ProxyRetryInfoMap& proxy_retry_info() const override;
-  std::unique_ptr<base::DictionaryValue> GetProxyNetLogValues(
-      int info_sources) override;
+  base::Value GetProxyNetLogValues(int info_sources) override;
   bool CastToConfiguredProxyResolutionService(
       ConfiguredProxyResolutionService** configured_proxy_resolution_service)
       override WARN_UNUSED_RESULT;
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 2b51ca8..162f3dc 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -279,10 +279,6 @@
     FLAGS_quic_reloadable_flag_quic_bbr2_add_ack_height_to_queueing_threshold,
     true)
 
-// If true, use idle network detector to detect handshake timeout and idle
-// network timeout.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_idle_network_detector, true)
-
 // If true, server push will be allowed in QUIC versions using HTTP/3.
 QUIC_FLAG(bool, FLAGS_quic_enable_http3_server_push, false)
 
@@ -302,24 +298,12 @@
           FLAGS_quic_reloadable_flag_quic_bbr2_fewer_startup_round_trips,
           false)
 
-// Replace the usage of ConnectionData::encryption_level in
-// quic_time_wait_list_manager with a new TimeWaitAction.
-QUIC_FLAG(bool,
-          FLAGS_quic_restart_flag_quic_replace_time_wait_list_encryption_level,
-          true)
-
 // If true, enables support for TLS resumption in QUIC.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_tls_resumption_v2, false)
 
 // When true, QUIC's BBRv2 ignores inflight_lo in PROBE_BW.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr2_ignore_inflight_lo, false)
 
-// If true, do not change ACK in PostProcessAckFrame if an ACK has been queued.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_donot_change_queued_ack, true)
-
-// If true, reject IETF QUIC connections with invalid SNI.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_tls_enforce_valid_sni, true)
-
 // If true, support for IETF QUIC 0-rtt is enabled.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_zero_rtt_for_tls, true)
 
@@ -338,17 +322,6 @@
           FLAGS_quic_restart_flag_quic_google_transport_param_send_new,
           true)
 
-// If true, check ShouldGeneratePacket for every crypto packet.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_fix_checking_should_generate_packet,
-          true)
-
-// If true, notify stream ID manager even connection disconnects.
-QUIC_FLAG(
-    bool,
-    FLAGS_quic_reloadable_flag_quic_notify_stream_id_manager_when_disconnected,
-    true)
-
 // If true, return from QuicCryptoStream::WritePendingCryptoRetransmission after
 // partial writes.
 QUIC_FLAG(
@@ -356,12 +329,6 @@
     FLAGS_quic_reloadable_flag_quic_fix_write_pending_crypto_retransmission,
     true)
 
-// If true, clear last_inflight_packets_sent_time_ of a packet number space when
-// there is no bytes in flight.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_fix_last_inflight_packets_sent_time,
-          true)
-
 // If true, QUIC will free writer-allocated packet buffer if writer->WritePacket
 // is not called.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_avoid_leak_writer_buffer, false)
@@ -464,3 +431,11 @@
 
 // If true, update packet size when the first frame gets queued.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_update_packet_size, false)
+
+// If true, use 0 as ack_delay when calculate PTO timeout for INITIAL and
+// HANDSHAKE packet number space.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_pto_timeout, true)
+
+// If true, consider frame expansion when calculating extra padding bytes to
+// meet minimum plaintext packet size required for header protection.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_extra_padding_bytes, false)
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index e1068b88..ba7eb42 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -2715,11 +2715,11 @@
 // before handshake is confirmed.  If TCP succeeds and QUIC fails on the
 // alternate network as well, QUIC is marked as broken and the brokenness will
 // not expire when default network changes.
-TEST_P(QuicNetworkTransactionTest, QuicFailsOnBothNetworksWhileTCPSucceeds) {
-  if (version_.UsesTls() ||
-      GetQuicReloadableFlag(quic_use_idle_network_detector)) {
+// TODO(fayang): Add time driven idle network detection test.
+TEST_P(QuicNetworkTransactionTest,
+       DISABLED_QuicFailsOnBothNetworksWhileTCPSucceeds) {
+  if (version_.UsesTls()) {
     // QUIC with TLS1.3 handshake doesn't support 0-rtt.
-    // TODO(fayang): Add time driven idle network detection test.
     return;
   }
   SetUpTestForRetryConnectionOnAlternateNetwork();
@@ -2825,11 +2825,11 @@
 // before handshake is confirmed. If TCP succeeds and QUIC succeeds on the
 // alternate network, QUIC is marked as broken. The brokenness will expire when
 // the default network changes.
-TEST_P(QuicNetworkTransactionTest, RetryOnAlternateNetworkWhileTCPSucceeds) {
-  if (version_.UsesTls() ||
-      GetQuicReloadableFlag(quic_use_idle_network_detector)) {
+// TODO(fayang): Add time driven idle network detection test.
+TEST_P(QuicNetworkTransactionTest,
+       DISABLED_RetryOnAlternateNetworkWhileTCPSucceeds) {
+  if (version_.UsesTls()) {
     // QUIC with TLS1.3 handshake doesn't support 0-rtt.
-    // TODO(fayang): Add time driven idle network detection test.
     return;
   }
 
@@ -2946,12 +2946,12 @@
 }
 
 // Much like above test, but verifies NetworkIsolationKeys are respected.
-TEST_P(QuicNetworkTransactionTest,
-       RetryOnAlternateNetworkWhileTCPSucceedsWithNetworkIsolationKey) {
-  if (version_.UsesTls() ||
-      GetQuicReloadableFlag(quic_use_idle_network_detector)) {
+// TODO(fayang): Add time driven idle network detection test.
+TEST_P(
+    QuicNetworkTransactionTest,
+    DISABLED_RetryOnAlternateNetworkWhileTCPSucceedsWithNetworkIsolationKey) {
+  if (version_.UsesTls()) {
     // QUIC with TLS1.3 handshake doesn't support 0-rtt.
-    // TODO(fayang): Add time driven idle network detection test.
     return;
   }
 
@@ -3095,11 +3095,11 @@
 // alternate network if the original QUIC connection fails with idle timeout
 // before handshake is confirmed. If TCP doesn't succeed but QUIC on the
 // alternative network succeeds, QUIC is not marked as broken.
-TEST_P(QuicNetworkTransactionTest, RetryOnAlternateNetworkWhileTCPHanging) {
-  if (version_.UsesTls() ||
-      GetQuicReloadableFlag(quic_use_idle_network_detector)) {
+// TODO(fayang): Add time driven idle network detection test.
+TEST_P(QuicNetworkTransactionTest,
+       DISABLED_RetryOnAlternateNetworkWhileTCPHanging) {
+  if (version_.UsesTls()) {
     // QUIC with TLS1.3 handshake doesn't support 0-rtt.
-    // TODO(fayang): Add time driven idle network detection test.
     return;
   }
 
diff --git a/net/url_request/url_fetcher_impl_unittest.cc b/net/url_request/url_fetcher_impl_unittest.cc
index 7c97419..fd81ef0 100644
--- a/net/url_request/url_fetcher_impl_unittest.cc
+++ b/net/url_request/url_fetcher_impl_unittest.cc
@@ -443,7 +443,7 @@
 
     // Cleanup.
     if (base::PathExists(out_path))
-      base::DeleteFile(out_path, false);
+      base::DeleteFile(out_path);
   }
 
   // Returns a URL that hangs on DNS resolution when using a context created by
diff --git a/services/data_decoder/BUILD.gn b/services/data_decoder/BUILD.gn
index fa455db..adb398e 100644
--- a/services/data_decoder/BUILD.gn
+++ b/services/data_decoder/BUILD.gn
@@ -10,10 +10,6 @@
     "data_decoder_service.h",
     "json_parser_impl.cc",
     "json_parser_impl.h",
-    "web_bundle_parser.cc",
-    "web_bundle_parser.h",
-    "web_bundle_parser_factory.cc",
-    "web_bundle_parser_factory.h",
     "web_bundler.cc",
     "web_bundler.h",
     "xml_parser.cc",
@@ -31,7 +27,7 @@
 
   deps = [
     "//base",
-    "//components/cbor",
+    "//components/web_package",
     "//mojo/public/cpp/bindings",
     "//net",
     "//skia",
@@ -60,8 +56,6 @@
     "public/cpp/json_sanitizer_unittest.cc",
     "public/cpp/safe_web_bundle_parser_unittest.cc",
     "public/cpp/safe_xml_parser_unittest.cc",
-    "web_bundle_parser_factory_unittest.cc",
-    "web_bundle_parser_unittest.cc",
     "xml_parser_unittest.cc",
   ]
 
@@ -73,7 +67,6 @@
     ":lib",
     "//base",
     "//base/test:test_support",
-    "//components/cbor",
     "//services/data_decoder/public/cpp",
     "//services/data_decoder/public/cpp:test_support",
     "//skia",
@@ -81,6 +74,8 @@
     "//ui/gfx",
   ]
 
+  data = [ "//components/test/data/web_package/" ]
+
   if (!is_ios) {
     sources += [ "image_decoder_impl_unittest.cc" ]
     deps += [
@@ -96,16 +91,6 @@
   }
 }
 
-fuzzer_test("web_bundle_parser_fuzzer") {
-  sources = [ "web_bundle_parser_fuzzer.cc" ]
-  deps = [
-    ":lib",
-    "//base",
-    "//mojo/core/embedder",
-  ]
-  seed_corpus = "//services/test/data/web_bundle"
-}
-
 fuzzer_test("xml_parser_fuzzer") {
   sources = [ "xml_parser_fuzzer.cc" ]
   deps = [
diff --git a/services/data_decoder/DEPS b/services/data_decoder/DEPS
index f8f08e372..bd28354 100644
--- a/services/data_decoder/DEPS
+++ b/services/data_decoder/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
-  "+components/cbor",
+  "+components/web_package",
   "+device/bluetooth/public",
   "+gin",
   "+net",
diff --git a/services/data_decoder/data_decoder_service.cc b/services/data_decoder/data_decoder_service.cc
index f841ce4..96f6a3a 100644
--- a/services/data_decoder/data_decoder_service.cc
+++ b/services/data_decoder/data_decoder_service.cc
@@ -11,11 +11,11 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/web_package/web_bundle_parser_factory.h"
 #include "mojo/public/cpp/bindings/generic_pending_receiver.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "services/data_decoder/json_parser_impl.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
-#include "services/data_decoder/web_bundle_parser_factory.h"
 #include "services/data_decoder/web_bundler.h"
 #include "services/data_decoder/xml_parser.h"
 
@@ -70,12 +70,14 @@
 }
 
 void DataDecoderService::BindWebBundleParserFactory(
-    mojo::PendingReceiver<mojom::WebBundleParserFactory> receiver) {
+    mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>
+        receiver) {
   if (web_bundle_parser_factory_binder_) {
     web_bundle_parser_factory_binder_.Run(std::move(receiver));
   } else {
-    mojo::MakeSelfOwnedReceiver(std::make_unique<WebBundleParserFactory>(),
-                                std::move(receiver));
+    mojo::MakeSelfOwnedReceiver(
+        std::make_unique<web_package::WebBundleParserFactory>(),
+        std::move(receiver));
   }
 }
 
diff --git a/services/data_decoder/data_decoder_service.h b/services/data_decoder/data_decoder_service.h
index b399900..3e079b2 100644
--- a/services/data_decoder/data_decoder_service.h
+++ b/services/data_decoder/data_decoder_service.h
@@ -8,12 +8,12 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "services/data_decoder/public/mojom/data_decoder_service.mojom.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
 #include "services/data_decoder/public/mojom/json_parser.mojom.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 #include "services/data_decoder/public/mojom/web_bundler.mojom.h"
 #include "services/data_decoder/public/mojom/xml_parser.mojom.h"
 
@@ -50,8 +50,9 @@
   // WebBundleParserFactory in subsequent
   // BindWebBundleParserFactory() calls.
   void SetWebBundleParserFactoryBinderForTesting(
-      base::RepeatingCallback<
-          void(mojo::PendingReceiver<mojom::WebBundleParserFactory>)> binder) {
+      base::RepeatingCallback<void(
+          mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>)>
+          binder) {
     web_bundle_parser_factory_binder_ = binder;
   }
 
@@ -71,7 +72,8 @@
       mojo::PendingReceiver<mojom::JsonParser> receiver) override;
   void BindXmlParser(mojo::PendingReceiver<mojom::XmlParser> receiver) override;
   void BindWebBundleParserFactory(
-      mojo::PendingReceiver<mojom::WebBundleParserFactory> receiver) override;
+      mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>
+          receiver) override;
   void BindWebBundler(
       mojo::PendingReceiver<mojom::WebBundler> receiver) override;
 
@@ -87,7 +89,7 @@
   bool drop_image_decoders_ = false;
   bool drop_json_parsers_ = false;
   base::RepeatingCallback<void(
-      mojo::PendingReceiver<mojom::WebBundleParserFactory>)>
+      mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>)>
       web_bundle_parser_factory_binder_;
   base::RepeatingCallback<void(mojo::PendingReceiver<mojom::WebBundler>)>
       web_bundler_binder_;
diff --git a/services/data_decoder/public/cpp/BUILD.gn b/services/data_decoder/public/cpp/BUILD.gn
index 4dd8989..16ef9b7 100644
--- a/services/data_decoder/public/cpp/BUILD.gn
+++ b/services/data_decoder/public/cpp/BUILD.gn
@@ -77,12 +77,8 @@
   sources = [
     "test_support/in_process_data_decoder.cc",
     "test_support/in_process_data_decoder.h",
-    "test_support/web_bundle_builder.cc",
-    "test_support/web_bundle_builder.h",
   ]
 
-  deps = [ "//components/cbor" ]
-
   public_deps = [
     ":cpp",
     "//base",
diff --git a/services/data_decoder/public/cpp/safe_web_bundle_parser.cc b/services/data_decoder/public/cpp/safe_web_bundle_parser.cc
index bd8c57d..ac1d849 100644
--- a/services/data_decoder/public/cpp/safe_web_bundle_parser.cc
+++ b/services/data_decoder/public/cpp/safe_web_bundle_parser.cc
@@ -34,7 +34,7 @@
 }
 
 void SafeWebBundleParser::OpenDataSource(
-    mojo::PendingRemote<mojom::BundleDataSource> data_source) {
+    mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source) {
   DCHECK(disconnected_);
   GetFactory()->GetParserForDataSource(parser_.BindNewPipeAndPassReceiver(),
                                        std::move(data_source));
@@ -45,14 +45,15 @@
 }
 
 void SafeWebBundleParser::ParseMetadata(
-    mojom::WebBundleParser::ParseMetadataCallback callback) {
+    web_package::mojom::WebBundleParser::ParseMetadataCallback callback) {
   // This method is designed to be called once. So, allowing only once
   // simultaneous request is fine enough.
   if (disconnected_ || !metadata_callback_.is_null()) {
     std::move(callback).Run(
-        nullptr, mojom::BundleMetadataParseError::New(
-                     mojom::BundleParseErrorType::kParserInternalError,
-                     GURL() /* fallback_url */, kConnectionError));
+        nullptr,
+        web_package::mojom::BundleMetadataParseError::New(
+            web_package::mojom::BundleParseErrorType::kParserInternalError,
+            GURL() /* fallback_url */, kConnectionError));
     return;
   }
   metadata_callback_ = std::move(callback);
@@ -63,16 +64,17 @@
 void SafeWebBundleParser::ParseResponse(
     uint64_t response_offset,
     uint64_t response_length,
-    mojom::WebBundleParser::ParseResponseCallback callback) {
+    web_package::mojom::WebBundleParser::ParseResponseCallback callback) {
   // This method allows simultaneous multiple requests. But if the unique ID
   // overflows, and the previous request that owns the same ID hasn't finished,
   // we just make the new request fail with kConnectionError.
   if (disconnected_ ||
       response_callbacks_.contains(response_callback_next_id_)) {
     std::move(callback).Run(
-        nullptr, mojom::BundleResponseParseError::New(
-                     mojom::BundleParseErrorType::kParserInternalError,
-                     kConnectionError));
+        nullptr,
+        web_package::mojom::BundleResponseParseError::New(
+            web_package::mojom::BundleParseErrorType::kParserInternalError,
+            kConnectionError));
     return;
   }
   size_t callback_id = response_callback_next_id_++;
@@ -82,7 +84,7 @@
                                         base::Unretained(this), callback_id));
 }
 
-mojom::WebBundleParserFactory* SafeWebBundleParser::GetFactory() {
+web_package::mojom::WebBundleParserFactory* SafeWebBundleParser::GetFactory() {
   if (!factory_) {
     data_decoder_.GetService()->BindWebBundleParserFactory(
         factory_.BindNewPipeAndPassReceiver());
@@ -100,30 +102,32 @@
   disconnected_ = true;
   if (!metadata_callback_.is_null())
     std::move(metadata_callback_)
-        .Run(nullptr, mojom::BundleMetadataParseError::New(
-                          mojom::BundleParseErrorType::kParserInternalError,
-                          GURL() /* fallback_url */, kConnectionError));
+        .Run(nullptr,
+             web_package::mojom::BundleMetadataParseError::New(
+                 web_package::mojom::BundleParseErrorType::kParserInternalError,
+                 GURL() /* fallback_url */, kConnectionError));
   for (auto& callback : response_callbacks_)
     std::move(callback.second)
-        .Run(nullptr, mojom::BundleResponseParseError::New(
-                          mojom::BundleParseErrorType::kParserInternalError,
-                          kConnectionError));
+        .Run(nullptr,
+             web_package::mojom::BundleResponseParseError::New(
+                 web_package::mojom::BundleParseErrorType::kParserInternalError,
+                 kConnectionError));
   response_callbacks_.clear();
   if (disconnect_callback_)
     std::move(disconnect_callback_).Run();
 }
 
 void SafeWebBundleParser::OnMetadataParsed(
-    mojom::BundleMetadataPtr metadata,
-    mojom::BundleMetadataParseErrorPtr error) {
+    web_package::mojom::BundleMetadataPtr metadata,
+    web_package::mojom::BundleMetadataParseErrorPtr error) {
   DCHECK(!metadata_callback_.is_null());
   std::move(metadata_callback_).Run(std::move(metadata), std::move(error));
 }
 
 void SafeWebBundleParser::OnResponseParsed(
     size_t callback_id,
-    mojom::BundleResponsePtr response,
-    mojom::BundleResponseParseErrorPtr error) {
+    web_package::mojom::BundleResponsePtr response,
+    web_package::mojom::BundleResponseParseErrorPtr error) {
   auto it = response_callbacks_.find(callback_id);
   DCHECK(it != response_callbacks_.end());
   auto callback = std::move(it->second);
diff --git a/services/data_decoder/public/cpp/safe_web_bundle_parser.h b/services/data_decoder/public/cpp/safe_web_bundle_parser.h
index 2ef665c..d2097aa 100644
--- a/services/data_decoder/public/cpp/safe_web_bundle_parser.h
+++ b/services/data_decoder/public/cpp/safe_web_bundle_parser.h
@@ -10,16 +10,16 @@
 #include "base/containers/flat_map.h"
 #include "base/files/file.h"
 #include "base/optional.h"
+#include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/data_decoder/public/cpp/data_decoder.h"
-#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h"
 
 namespace data_decoder {
 
-// A class to wrap remote mojom::WebBundleParserFactory and
-// mojom::WebBundleParser service.
+// A class to wrap remote web_package::mojom::WebBundleParserFactory and
+// web_package::mojom::WebBundleParser service.
 class SafeWebBundleParser {
  public:
   SafeWebBundleParser();
@@ -31,37 +31,41 @@
 
   // Binds |this| instance to the given |data_source| for subsequent parse
   // calls.
-  void OpenDataSource(mojo::PendingRemote<mojom::BundleDataSource> data_source);
+  void OpenDataSource(
+      mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source);
 
-  // Parses metadata. See mojom::WebBundleParser::ParseMetadata for
+  // Parses metadata. See web_package::mojom::WebBundleParser::ParseMetadata for
   // details. This method fails when it's called before the previous call
   // finishes.
-  void ParseMetadata(mojom::WebBundleParser::ParseMetadataCallback callback);
+  void ParseMetadata(
+      web_package::mojom::WebBundleParser::ParseMetadataCallback callback);
 
-  // Parses response. See mojom::WebBundleParser::ParseResponse for
+  // Parses response. See web_package::mojom::WebBundleParser::ParseResponse for
   // details.
-  void ParseResponse(uint64_t response_offset,
-                     uint64_t response_length,
-                     mojom::WebBundleParser::ParseResponseCallback callback);
+  void ParseResponse(
+      uint64_t response_offset,
+      uint64_t response_length,
+      web_package::mojom::WebBundleParser::ParseResponseCallback callback);
 
   // Sets a callback to be called when the data_decoder service connection is
   // terminated.
   void SetDisconnectCallback(base::OnceClosure callback);
 
  private:
-  mojom::WebBundleParserFactory* GetFactory();
+  web_package::mojom::WebBundleParserFactory* GetFactory();
   void OnDisconnect();
-  void OnMetadataParsed(mojom::BundleMetadataPtr metadata,
-                        mojom::BundleMetadataParseErrorPtr error);
+  void OnMetadataParsed(web_package::mojom::BundleMetadataPtr metadata,
+                        web_package::mojom::BundleMetadataParseErrorPtr error);
   void OnResponseParsed(size_t callback_id,
-                        mojom::BundleResponsePtr response,
-                        mojom::BundleResponseParseErrorPtr error);
+                        web_package::mojom::BundleResponsePtr response,
+                        web_package::mojom::BundleResponseParseErrorPtr error);
 
   DataDecoder data_decoder_;
-  mojo::Remote<mojom::WebBundleParserFactory> factory_;
-  mojo::Remote<mojom::WebBundleParser> parser_;
-  mojom::WebBundleParser::ParseMetadataCallback metadata_callback_;
-  base::flat_map<size_t, mojom::WebBundleParser::ParseResponseCallback>
+  mojo::Remote<web_package::mojom::WebBundleParserFactory> factory_;
+  mojo::Remote<web_package::mojom::WebBundleParser> parser_;
+  web_package::mojom::WebBundleParser::ParseMetadataCallback metadata_callback_;
+  base::flat_map<size_t,
+                 web_package::mojom::WebBundleParser::ParseResponseCallback>
       response_callbacks_;
   base::OnceClosure disconnect_callback_;
   size_t response_callback_next_id_ = 0;
diff --git a/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc b/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc
index 8709166..c6877ed 100644
--- a/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc
+++ b/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc
@@ -29,17 +29,18 @@
   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("services/test/data/web_bundle")));
+      base::FilePath(FILE_PATH_LITERAL("components/test/data/web_package")));
   test_data_dir = test_data_dir.Append(path);
   return base::File(test_data_dir,
                     base::File::FLAG_OPEN | base::File::FLAG_READ);
 }
 
-class MockFactory final : public mojom::WebBundleParserFactory {
+class MockFactory final : public web_package::mojom::WebBundleParserFactory {
  public:
-  class MockParser final : public mojom::WebBundleParser {
+  class MockParser final : public web_package::mojom::WebBundleParser {
    public:
-    MockParser(mojo::PendingReceiver<mojom::WebBundleParser> receiver)
+    explicit MockParser(
+        mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver)
         : receiver_(this, std::move(receiver)) {}
 
     bool IsParseMetadataCalled() { return !metadata_callback_.is_null(); }
@@ -48,7 +49,7 @@
     void Disconnect() { receiver_.reset(); }
 
    private:
-    // mojom::WebBundleParser implementation.
+    // web_package::mojom::WebBundleParser implementation.
     void ParseMetadata(ParseMetadataCallback callback) override {
       metadata_callback_ = std::move(callback);
     }
@@ -60,14 +61,15 @@
 
     ParseMetadataCallback metadata_callback_;
     ParseResponseCallback response_callback_;
-    mojo::Receiver<mojom::WebBundleParser> receiver_;
+    mojo::Receiver<web_package::mojom::WebBundleParser> receiver_;
 
     DISALLOW_COPY_AND_ASSIGN(MockParser);
   };
 
   MockFactory() {}
   void AddReceiver(
-      mojo::PendingReceiver<mojom::WebBundleParserFactory> receiver) {
+      mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>
+          receiver) {
     receivers_.Add(this, std::move(receiver));
   }
   MockParser* GetCreatedParser() {
@@ -77,33 +79,36 @@
   void DeleteParser() { parser_.reset(); }
 
  private:
-  // mojom::WebBundleParserFactory implementation.
-  void GetParserForFile(mojo::PendingReceiver<mojom::WebBundleParser> receiver,
-                        base::File file) override {
+  // web_package::mojom::WebBundleParserFactory implementation.
+  void GetParserForFile(
+      mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver,
+      base::File file) override {
     parser_ = std::make_unique<MockParser>(std::move(receiver));
   }
   void GetParserForDataSource(
-      mojo::PendingReceiver<mojom::WebBundleParser> receiver,
-      mojo::PendingRemote<mojom::BundleDataSource> data_source) override {
+      mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver,
+      mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source)
+      override {
     parser_ = std::make_unique<MockParser>(std::move(receiver));
   }
 
   std::unique_ptr<MockParser> parser_;
-  mojo::ReceiverSet<data_decoder::mojom::WebBundleParserFactory> receivers_;
+  mojo::ReceiverSet<web_package::mojom::WebBundleParserFactory> receivers_;
 
   DISALLOW_COPY_AND_ASSIGN(MockFactory);
 };
 
-class MockDataSource final : public mojom::BundleDataSource {
+class MockDataSource final : public web_package::mojom::BundleDataSource {
  public:
-  MockDataSource(mojo::PendingReceiver<mojom::BundleDataSource> receiver)
+  explicit MockDataSource(
+      mojo::PendingReceiver<web_package::mojom::BundleDataSource> receiver)
       : receiver_(this, std::move(receiver)) {}
 
  private:
-  // Implements mojom::BundledDataSource.
+  // Implements web_package::mojom::BundledDataSource.
   void Read(uint64_t offset, uint64_t length, ReadCallback callback) override {}
 
-  mojo::Receiver<mojom::BundleDataSource> receiver_;
+  mojo::Receiver<web_package::mojom::BundleDataSource> receiver_;
 
   DISALLOW_COPY_AND_ASSIGN(MockDataSource);
 };
@@ -135,14 +140,14 @@
       OpenTestFile(base::FilePath(FILE_PATH_LITERAL("hello.wbn")));
   ASSERT_EQ(base::File::FILE_OK, parser.OpenFile(std::move(test_file)));
 
-  mojom::BundleMetadataPtr metadata_result;
+  web_package::mojom::BundleMetadataPtr metadata_result;
   {
     base::RunLoop run_loop;
     parser.ParseMetadata(base::BindOnce(
         [](base::OnceClosure quit_closure,
-           mojom::BundleMetadataPtr* metadata_result,
-           mojom::BundleMetadataPtr metadata,
-           mojom::BundleMetadataParseErrorPtr error) {
+           web_package::mojom::BundleMetadataPtr* metadata_result,
+           web_package::mojom::BundleMetadataPtr metadata,
+           web_package::mojom::BundleMetadataParseErrorPtr error) {
           EXPECT_TRUE(metadata);
           EXPECT_FALSE(error);
           if (metadata)
@@ -156,7 +161,7 @@
   const auto& requests = metadata_result->requests;
   ASSERT_EQ(requests.size(), 4u);
 
-  std::map<std::string, mojom::BundleResponsePtr> responses;
+  std::map<std::string, web_package::mojom::BundleResponsePtr> responses;
   for (auto& entry : requests) {
     base::RunLoop run_loop;
     parser.ParseResponse(
@@ -164,9 +169,10 @@
         entry.second->response_locations[0]->length,
         base::BindOnce(
             [](base::OnceClosure quit_closure, const std::string url,
-               std::map<std::string, mojom::BundleResponsePtr>* responses,
-               mojom::BundleResponsePtr response,
-               mojom::BundleResponseParseErrorPtr error) {
+               std::map<std::string, web_package::mojom::BundleResponsePtr>*
+                   responses,
+               web_package::mojom::BundleResponsePtr response,
+               web_package::mojom::BundleResponseParseErrorPtr error) {
               EXPECT_TRUE(response);
               EXPECT_FALSE(error);
               if (response)
@@ -196,8 +202,8 @@
   SafeWebBundleParser parser;
   bool metadata_parsed = false;
   parser.ParseMetadata(base::BindOnce(
-      [](bool* metadata_parsed, mojom::BundleMetadataPtr metadata,
-         mojom::BundleMetadataParseErrorPtr error) {
+      [](bool* metadata_parsed, web_package::mojom::BundleMetadataPtr metadata,
+         web_package::mojom::BundleMetadataParseErrorPtr error) {
         EXPECT_FALSE(metadata);
         EXPECT_TRUE(error);
         if (error)
@@ -211,8 +217,9 @@
   parser.ParseResponse(
       0u, 0u,
       base::BindOnce(
-          [](bool* response_parsed, mojom::BundleResponsePtr response,
-             mojom::BundleResponseParseErrorPtr error) {
+          [](bool* response_parsed,
+             web_package::mojom::BundleResponsePtr response,
+             web_package::mojom::BundleResponseParseErrorPtr error) {
             EXPECT_FALSE(response);
             EXPECT_TRUE(error);
             if (error)
@@ -250,7 +257,7 @@
   SafeWebBundleParser parser;
   MockFactory* raw_factory = InitializeMockFactory();
 
-  mojo::PendingRemote<mojom::BundleDataSource> remote_data_source;
+  mojo::PendingRemote<web_package::mojom::BundleDataSource> remote_data_source;
   auto data_source = std::make_unique<MockDataSource>(
       remote_data_source.InitWithNewPipeAndPassReceiver());
   parser.OpenDataSource(std::move(remote_data_source));
@@ -260,8 +267,8 @@
   bool parsed = false;
   parser.ParseMetadata(base::BindOnce(
       [](base::OnceClosure quit_closure, bool* parsed,
-         mojom::BundleMetadataPtr metadata,
-         mojom::BundleMetadataParseErrorPtr error) {
+         web_package::mojom::BundleMetadataPtr metadata,
+         web_package::mojom::BundleMetadataParseErrorPtr error) {
         EXPECT_FALSE(metadata);
         EXPECT_TRUE(error);
         if (error)
diff --git a/services/data_decoder/public/mojom/BUILD.gn b/services/data_decoder/public/mojom/BUILD.gn
index ee6240f..59dac08b 100644
--- a/services/data_decoder/public/mojom/BUILD.gn
+++ b/services/data_decoder/public/mojom/BUILD.gn
@@ -9,13 +9,13 @@
     "data_decoder_service.mojom",
     "image_decoder.mojom",
     "json_parser.mojom",
-    "web_bundle_parser.mojom",
     "web_bundler.mojom",
     "xml_parser.mojom",
   ]
 
   public_deps = [
     ":mojom_resource_snapshot_for_web_bundle",
+    "//components/web_package/mojom",
     "//mojo/public/mojom/base",
     "//skia/public/mojom",
     "//ui/gfx/geometry/mojom",
diff --git a/services/data_decoder/public/mojom/data_decoder_service.mojom b/services/data_decoder/public/mojom/data_decoder_service.mojom
index 120ba37..77f2f1d4 100644
--- a/services/data_decoder/public/mojom/data_decoder_service.mojom
+++ b/services/data_decoder/public/mojom/data_decoder_service.mojom
@@ -4,10 +4,10 @@
 
 module data_decoder.mojom;
 
+import "components/web_package/mojom/web_bundle_parser.mojom";
 import "services/data_decoder/public/mojom/image_decoder.mojom";
 import "services/data_decoder/public/mojom/json_parser.mojom";
 import "services/data_decoder/public/mojom/web_bundler.mojom";
-import "services/data_decoder/public/mojom/web_bundle_parser.mojom";
 import "services/data_decoder/public/mojom/xml_parser.mojom";
 
 [EnableIf=is_chromeos]
@@ -26,7 +26,7 @@
 
   // Binds an interface which can be used to parse Web Bundles.
   BindWebBundleParserFactory(
-      pending_receiver<WebBundleParserFactory> receiver);
+      pending_receiver<web_package.mojom.WebBundleParserFactory> receiver);
 
   // Binds an interface which can be used to generate a Web Bundle.
   BindWebBundler(pending_receiver<WebBundler> receiver);
diff --git a/services/network/net_log_exporter.cc b/services/network/net_log_exporter.cc
index 717076e..965b129 100644
--- a/services/network/net_log_exporter.cc
+++ b/services/network/net_log_exporter.cc
@@ -90,13 +90,13 @@
     return;
   }
 
-  std::unique_ptr<base::DictionaryValue> net_info = net::GetNetInfo(
+  base::Value net_info = net::GetNetInfo(
       network_context_->url_request_context(), net::NET_INFO_ALL_SOURCES);
   if (polled_data)
-    net_info->MergeDictionary(polled_data);
+    net_info.MergeDictionary(polled_data);
 
   file_net_observer_->StopObserving(
-      std::move(net_info),
+      base::Value::ToUniquePtrValue(std::move(net_info)),
       base::BindOnce([](StopCallback sc) { std::move(sc).Run(net::OK); },
                      std::move(callback)));
   file_net_observer_ = nullptr;
diff --git a/testing/buildbot/buildbot_json_magic_substitutions.py b/testing/buildbot/buildbot_json_magic_substitutions.py
index 041c0460..a84523b 100644
--- a/testing/buildbot/buildbot_json_magic_substitutions.py
+++ b/testing/buildbot/buildbot_json_magic_substitutions.py
@@ -61,6 +61,41 @@
   raise RuntimeError('Unknown CrOS pool %s' % pool)
 
 
+def GPUExpectedDeviceId(test_config):
+  """Substitutes the correct expected GPU(s) for certain GPU tests.
+
+  Most configurations only need one expected GPU, but heterogeneous pools (e.g.
+  HD 630 and UHD 630 machines) require multiple.
+
+  Args:
+    test_config: A dict containing a configuration for a specific test on a
+        specific builder.
+  """
+  dimensions = test_config.get('swarming', {}).get('dimension_sets', [])
+  assert dimensions
+  gpus = []
+  for d in dimensions:
+    # Split up multiple GPU/driver combinations if the swarming OR operator is
+    # being used.
+    if 'gpu' in d:
+      gpus.extend(d['gpu'].split('|'))
+
+  # We don't specify GPU on things like Android/CrOS devices, so default to 0.
+  if not gpus:
+    return ['--expected-device-id', '0']
+
+  device_ids = set()
+  for gpu_and_driver in gpus:
+    # In the form vendor:device-driver.
+    device = gpu_and_driver.split('-')[0].split(':')[1]
+    device_ids.add(device)
+
+  retval = []
+  for device_id in device_ids:
+    retval.extend(['--expected-device-id', device_id])
+  return retval
+
+
 def TestOnlySubstitution(_):
   """Magic substitution used for unittests."""
   return ['--magic-substitution-success']
diff --git a/testing/buildbot/buildbot_json_magic_substitutions_unittest.py b/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
index 70d2af4c..9128b68 100755
--- a/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
+++ b/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
@@ -46,5 +46,64 @@
       magic_substitutions.ChromeOSTelemetryRemote(test_config)
 
 
+def CreateConfigWithGpus(gpus):
+  dimension_sets = []
+  for g in gpus:
+    dimension_sets.append({'gpu': g})
+  return {
+      'swarming': {
+          'dimension_sets': dimension_sets,
+      },
+  }
+
+
+class GPUExpectedDeviceId(unittest.TestCase):
+  def assertDeviceIdCorrectness(self, retval, device_ids):
+    self.assertEqual(len(retval), 2 * len(device_ids))
+    for i in xrange(0, len(retval), 2):
+      self.assertEqual(retval[i], '--expected-device-id')
+    for d in device_ids:
+      self.assertIn(d, retval)
+
+  def testSingleGpuSingleDimension(self):
+    test_config = CreateConfigWithGpus(['vendor:device1-driver'])
+    self.assertDeviceIdCorrectness(
+        magic_substitutions.GPUExpectedDeviceId(test_config), ['device1'])
+
+  def testSingleGpuDoubleDimension(self):
+    test_config = CreateConfigWithGpus(
+        ['vendor:device1-driver', 'vendor:device2-driver'])
+    self.assertDeviceIdCorrectness(
+        magic_substitutions.GPUExpectedDeviceId(test_config),
+        ['device1', 'device2'])
+
+  def testDoubleGpuSingleDimension(self):
+    test_config = CreateConfigWithGpus(
+        ['vendor:device1-driver|vendor:device2-driver'])
+    self.assertDeviceIdCorrectness(
+        magic_substitutions.GPUExpectedDeviceId(test_config),
+        ['device1', 'device2'])
+
+  def testDoubleGpuDoubleDimension(self):
+    test_config = CreateConfigWithGpus([
+        'vendor:device1-driver|vendor:device2-driver',
+        'vendor:device1-driver|vendor:device3-driver'
+    ])
+    self.assertDeviceIdCorrectness(
+        magic_substitutions.GPUExpectedDeviceId(test_config),
+        ['device1', 'device2', 'device3'])
+
+  def testNoGpu(self):
+    self.assertDeviceIdCorrectness(
+        magic_substitutions.GPUExpectedDeviceId(
+            {'swarming': {
+                'dimension_sets': [{}]
+            }}), ['0'])
+
+  def testNoDimensions(self):
+    with self.assertRaises(AssertionError):
+      magic_substitutions.GPUExpectedDeviceId({})
+
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/testing/buildbot/chromium.ci.json b/testing/buildbot/chromium.ci.json
index 466aa4d..4fc4111 100644
--- a/testing/buildbot/chromium.ci.json
+++ b/testing/buildbot/chromium.ci.json
@@ -1748,9 +1748,9 @@
           "--expected-vendor-id",
           "8086",
           "--expected-device-id",
-          "5912",
+          "3e92",
           "--expected-device-id",
-          "3e92"
+          "5912"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -16490,10 +16490,10 @@
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
           "--expected-vendor-id",
           "1af4",
-          "--expected-device-id",
-          "1050",
           "--remote=127.0.0.1",
-          "--remote-ssh-port=9222"
+          "--remote-ssh-port=9222",
+          "--expected-device-id",
+          "1050"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -48564,7 +48564,7 @@
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "content_browsertests",
         "test_id_prefix": "ninja://content/test:content_browsertests/"
@@ -90948,9 +90948,9 @@
           "--expected-vendor-id",
           "8086",
           "--expected-device-id",
-          "5912",
+          "3e92",
           "--expected-device-id",
-          "3e92"
+          "5912"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -134384,9 +134384,9 @@
           "--expected-vendor-id",
           "8086",
           "--expected-device-id",
-          "5912",
+          "3e92",
           "--expected-device-id",
-          "3e92"
+          "5912"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -136086,9 +136086,9 @@
           "--expected-vendor-id",
           "8086",
           "--expected-device-id",
-          "5912",
+          "3e92",
           "--expected-device-id",
-          "3e92"
+          "5912"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -194389,6 +194389,7 @@
     "gtest_tests": [
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194410,6 +194411,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194432,6 +194434,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194453,6 +194456,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194474,6 +194478,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194495,6 +194500,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194516,6 +194522,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194537,6 +194544,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194559,6 +194567,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.blink_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194580,6 +194589,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194601,6 +194611,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194622,6 +194633,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194643,6 +194655,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194664,6 +194677,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194685,6 +194699,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194706,6 +194721,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194729,6 +194745,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194751,6 +194768,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.content_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194772,6 +194790,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194793,6 +194812,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194814,6 +194834,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194835,6 +194856,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194856,6 +194878,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194877,6 +194900,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194898,6 +194922,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194919,6 +194944,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194940,6 +194966,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -194960,6 +194987,9 @@
         "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -194980,6 +195010,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.headless_browsertests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195001,6 +195032,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195022,6 +195054,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195043,6 +195076,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195064,6 +195098,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195085,6 +195120,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195106,6 +195142,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195128,6 +195165,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195149,6 +195187,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195170,6 +195209,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195191,6 +195231,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195213,6 +195254,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.services_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195234,6 +195276,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195255,6 +195298,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195277,6 +195321,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195298,6 +195343,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195320,6 +195366,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.storage_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195342,6 +195389,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.ui_base_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195363,6 +195411,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195385,6 +195434,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.viz_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195406,6 +195456,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195426,6 +195477,9 @@
         "test_id_prefix": "ninja://fuchsia/engine:web_engine_browsertests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -195445,6 +195499,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195465,6 +195520,9 @@
         "test_id_prefix": "ninja://fuchsia/engine:web_engine_unittests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -195484,6 +195542,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195512,6 +195571,7 @@
     "gtest_tests": [
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195533,6 +195593,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195555,6 +195616,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195576,6 +195638,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195597,6 +195660,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195618,6 +195682,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195639,6 +195704,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195660,6 +195726,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195682,6 +195749,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.blink_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195703,6 +195771,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195724,6 +195793,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195745,6 +195815,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195766,6 +195837,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195787,6 +195859,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195808,6 +195881,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195829,6 +195903,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195852,6 +195927,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195874,6 +195950,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.content_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195895,6 +195972,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195916,6 +195994,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195937,6 +196016,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195958,6 +196038,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -195979,6 +196060,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196000,6 +196082,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196021,6 +196104,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196042,6 +196126,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196063,6 +196148,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196083,6 +196169,9 @@
         "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -196103,6 +196192,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.headless_browsertests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196124,6 +196214,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196145,6 +196236,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196166,6 +196258,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196187,6 +196280,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196208,6 +196302,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196229,6 +196324,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196251,6 +196347,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196272,6 +196369,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196294,6 +196392,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196316,6 +196415,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196337,6 +196437,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196359,6 +196460,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.services_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196380,6 +196482,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196401,6 +196504,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196423,6 +196527,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196444,6 +196549,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196466,6 +196572,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.storage_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196488,6 +196595,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.ui_base_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196509,6 +196617,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196531,6 +196640,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.viz_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196552,6 +196662,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196572,6 +196683,9 @@
         "test_id_prefix": "ninja://fuchsia/engine:web_engine_browsertests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -196591,6 +196705,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196611,6 +196726,9 @@
         "test_id_prefix": "ninja://fuchsia/engine:web_engine_unittests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -196630,6 +196748,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -196654,6 +196773,7 @@
       {
         "args": [
           "--num-retries=3",
+          "--device=aemu",
           "--platform=fuchsia"
         ],
         "isolate_name": "blink_web_tests",
@@ -196684,7 +196804,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -196713,7 +196834,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -196742,7 +196864,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -196771,7 +196894,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -196804,7 +196928,8 @@
           "--expected-vendor-id",
           "0",
           "--expected-device-id",
-          "0"
+          "0",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -196833,7 +196958,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -196862,7 +196988,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -196891,7 +197018,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 9ba6bca..dcf9e4e 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -13165,6 +13165,7 @@
     "gtest_tests": [
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13186,6 +13187,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13208,6 +13210,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13229,6 +13232,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13250,6 +13254,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13271,6 +13276,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13292,6 +13298,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13313,6 +13320,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13335,6 +13343,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.blink_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13356,6 +13365,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13377,6 +13387,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13398,6 +13409,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13419,6 +13431,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13440,6 +13453,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13461,6 +13475,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13482,6 +13497,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13505,6 +13521,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13527,6 +13544,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.content_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13548,6 +13566,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13569,6 +13588,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13590,6 +13610,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13611,6 +13632,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13632,6 +13654,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13653,6 +13676,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13674,6 +13698,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13695,6 +13720,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13716,6 +13742,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13736,6 +13763,9 @@
         "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -13756,6 +13786,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.headless_browsertests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13777,6 +13808,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13798,6 +13830,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13819,6 +13852,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13840,6 +13874,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13861,6 +13896,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13882,6 +13918,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13904,6 +13941,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13925,6 +13963,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13946,6 +13985,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13967,6 +14007,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -13989,6 +14030,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.services_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14010,6 +14052,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14031,6 +14074,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14053,6 +14097,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14074,6 +14119,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14096,6 +14142,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.storage_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14118,6 +14165,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.ui_base_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14139,6 +14187,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14161,6 +14210,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.viz_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14182,6 +14232,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14202,6 +14253,9 @@
         "test_id_prefix": "ninja://fuchsia/engine:web_engine_browsertests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -14221,6 +14275,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14241,6 +14296,9 @@
         "test_id_prefix": "ninja://fuchsia/engine:web_engine_unittests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -14260,6 +14318,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14288,6 +14347,7 @@
     "gtest_tests": [
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14309,6 +14369,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14331,6 +14392,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14352,6 +14414,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14373,6 +14436,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14394,6 +14458,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14415,6 +14480,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14436,6 +14502,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14458,6 +14525,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.blink_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14479,6 +14547,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14500,6 +14569,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14521,6 +14591,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14542,6 +14613,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14563,6 +14635,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14584,6 +14657,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14605,6 +14679,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14628,6 +14703,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14650,6 +14726,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.content_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14671,6 +14748,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14692,6 +14770,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14713,6 +14792,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14734,6 +14814,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14755,6 +14836,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14776,6 +14858,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14797,6 +14880,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14818,6 +14902,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14839,6 +14924,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14859,6 +14945,9 @@
         "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -14879,6 +14968,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.headless_browsertests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14900,6 +14990,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14921,6 +15012,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14942,6 +15034,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14963,6 +15056,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -14984,6 +15078,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15005,6 +15100,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15027,6 +15123,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15048,6 +15145,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15070,6 +15168,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15092,6 +15191,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15113,6 +15213,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15135,6 +15236,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.services_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15156,6 +15258,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15177,6 +15280,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15199,6 +15303,7 @@
       {
         "args": [
           "--child-arg=--ozone-platform=headless",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15220,6 +15325,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15242,6 +15348,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.storage_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15264,6 +15371,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.ui_base_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15285,6 +15393,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15307,6 +15416,7 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.viz_unittests.filter",
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15328,6 +15438,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15348,6 +15459,9 @@
         "test_id_prefix": "ninja://fuchsia/engine:web_engine_browsertests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -15367,6 +15481,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15387,6 +15502,9 @@
         "test_id_prefix": "ninja://fuchsia/engine:web_engine_unittests/"
       },
       {
+        "args": [
+          "--device=aemu"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -15406,6 +15524,7 @@
       },
       {
         "args": [
+          "--device=aemu",
           "--runner-logs-dir=${ISOLATED_OUTDIR}/runner_logs"
         ],
         "merge": {
@@ -15430,6 +15549,7 @@
       {
         "args": [
           "--num-retries=3",
+          "--device=aemu",
           "--platform=fuchsia"
         ],
         "isolate_name": "blink_web_tests",
@@ -15460,7 +15580,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -15489,7 +15610,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -15518,7 +15640,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -15547,7 +15670,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -15580,7 +15704,8 @@
           "--expected-vendor-id",
           "0",
           "--expected-device-id",
-          "0"
+          "0",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -15609,7 +15734,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -15638,7 +15764,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
@@ -15667,7 +15794,8 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--device=aemu"
         ],
         "isolate_name": "fuchsia_telemetry_gpu_integration_test",
         "merge": {
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 087aa23..8792b97 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -1748,9 +1748,9 @@
           "--expected-vendor-id",
           "8086",
           "--expected-device-id",
-          "5912",
+          "3e92",
           "--expected-device-id",
-          "3e92"
+          "5912"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -7458,10 +7458,10 @@
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
           "--expected-vendor-id",
           "1af4",
-          "--expected-device-id",
-          "1050",
           "--remote=127.0.0.1",
-          "--remote-ssh-port=9222"
+          "--remote-ssh-port=9222",
+          "--expected-device-id",
+          "1050"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -22394,9 +22394,9 @@
           "--expected-vendor-id",
           "8086",
           "--expected-device-id",
-          "5912",
+          "3e92",
           "--expected-device-id",
-          "3e92"
+          "5912"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -24494,9 +24494,9 @@
           "--expected-vendor-id",
           "8086",
           "--expected-device-id",
-          "5912",
+          "3e92",
           "--expected-device-id",
-          "3e92"
+          "5912"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
@@ -26196,9 +26196,9 @@
           "--expected-vendor-id",
           "8086",
           "--expected-device-id",
-          "5912",
+          "3e92",
           "--expected-device-id",
-          "3e92"
+          "5912"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index cd1b4c5..889cff71 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -7331,7 +7331,7 @@
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
+          "shards": 4
         },
         "test": "content_browsertests",
         "test_id_prefix": "ninja://content/test:content_browsertests/"
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index ef07935..679958c 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1294,31 +1294,11 @@
       'Win V8 FYI Release (NVIDIA)',
     ],
     'modifications': {
-      # TODO(https://crbug.com/1099762): Remove the --expected-device-id args
-      # once JSON generation properly handles multiple GPUs.
-      'ANGLE GPU Win10 x64 Release (Intel HD 630)': {
+      'ChromeOS FYI Release (amd64-generic)': {
         'args': [
-          '--expected-device-id', '3e92',
-        ],
-      },
-      'Optional Win10 x64 Release (Intel HD 630)': {
-        'args': [
-          '--expected-device-id', '3e92',
-        ],
-      },
-      'Win10 FYI x64 Exp Release (Intel HD 630)': {
-        'args': [
-          '--expected-device-id', '3e92',
-        ],
-      },
-      'Win10 FYI x64 Release (Intel HD 630)': {
-        'args': [
-          '--expected-device-id', '3e92',
-        ],
-      },
-      'Win10 FYI x64 dEQP Release (Intel HD 630)': {
-        'args': [
-          '--expected-device-id', '3e92',
+          # Swarming does not report a GPU since tests are run in a VM, but
+          # the VM does report that a GPU is present.
+          '--expected-device-id', '1050',
         ],
       },
       'Win10 x64 Debug (NVIDIA)': {
@@ -1335,7 +1315,9 @@
           # Swarming does not report a GPU since tests are run in a VM, but
           # the VM does report that a GPU is present.
           '--expected-vendor-id': '1af4',
-          '--expected-device-id': '1050',
+          # Magic substitution happens after regular replacement, so remove it
+          # now since we are manually applying the expected device ID above.
+          '$$MAGIC_SUBSTITUTION_GPUExpectedDeviceId': None,
         },
       }
     },
@@ -2231,6 +2213,15 @@
       },
     },
   },
+  'storage_service_unsandboxed_content_browsertests': {
+    'modifications': {
+      'Linux Tests (dbg)(1)': {
+        'swarming': {
+          'shards': 4,
+        },
+      },
+    },
+  },
   'storage_service_unsandboxed_interactive_ui_tests': {
     'modifications': {
       'linux-chromeos-chrome': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 57a6dff..4ea94642 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1331,8 +1331,7 @@
         'args': [
           '--expected-vendor-id',
           '${gpu_vendor_id}',
-          '--expected-device-id',
-          '${gpu_device_id}',
+          '$$MAGIC_SUBSTITUTION_GPUExpectedDeviceId',
         ],
         'isolate_name': 'fuchsia_telemetry_gpu_integration_test',
       },
@@ -2134,8 +2133,7 @@
         'args': [
           '--expected-vendor-id',
           '${gpu_vendor_id}',
-          '--expected-device-id',
-          '${gpu_device_id}',
+          '$$MAGIC_SUBSTITUTION_GPUExpectedDeviceId',
           # On dual-GPU devices we want the high-performance GPU to be active
           '--extra-browser-args=--force_high_performance_gpu',
         ],
@@ -2474,8 +2472,7 @@
         'args': [
           '--expected-vendor-id',
           '${gpu_vendor_id}',
-          '--expected-device-id',
-          '${gpu_device_id}',
+          '$$MAGIC_SUBSTITUTION_GPUExpectedDeviceId',
           # On dual-GPU devices we want the high-performance GPU to be active
           '--extra-browser-args=--force_high_performance_gpu',
         ],
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 0ea2c41..a6eecc3 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1974,6 +1974,7 @@
           'all',
         ],
         'mixins': [
+          'fuchsia_aemu',
           'linux-xenial',
         ],
         'swarming': {
@@ -1994,6 +1995,7 @@
         'browser_config': 'web-engine-shell',
         'os_type': 'linux',
         'mixins': [
+          'fuchsia_aemu',
           'linux-xenial',
         ],
         'swarming': {
diff --git a/third_party/blink/renderer/bindings/core/v8/binding_security.cc b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
index 3dffc0d..80a5030c 100644
--- a/third_party/blink/renderer/bindings/core/v8/binding_security.cc
+++ b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
@@ -130,11 +130,11 @@
     return false;
 
   const SecurityOrigin* accessing_origin =
-      accessing_window->document()->GetSecurityOrigin();
+      accessing_window->GetSecurityOrigin();
 
   SecurityOrigin::AccessResultDomainDetail detail;
   bool can_access = accessing_origin->CanAccess(
-      local_target_window->document()->GetSecurityOrigin(), detail);
+      local_target_window->GetSecurityOrigin(), detail);
   if (detail ==
           SecurityOrigin::AccessResultDomainDetail::kDomainSetByOnlyOneOrigin ||
       detail ==
diff --git a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
index 5ac9a7e..2c0252c 100644
--- a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
+++ b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
@@ -175,7 +175,7 @@
        IsolatedWorldCSP::Get().HasContentSecurityPolicy(world_->GetWorldId()));
   if (evaluate_csp_for_eval) {
     ContentSecurityPolicy* csp =
-        GetFrame()->DomWindow()->GetContentSecurityPolicyForWorld();
+        GetFrame()->DomWindow()->GetContentSecurityPolicyForCurrentWorld();
     context->AllowCodeGenerationFromStrings(!csp->ShouldCheckEval());
     context->SetErrorMessageForCodeGenerationFromStrings(
         V8String(GetIsolate(), csp->EvalDisabledErrorMessage()));
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
index 04f635f..77724df4 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -376,7 +376,7 @@
     // See crbug.com/982388.)
 
     if (ContentSecurityPolicy* policy =
-            execution_context->GetContentSecurityPolicyForWorld()) {
+            execution_context->GetContentSecurityPolicyForCurrentWorld()) {
       v8::String::Value source_str(context->GetIsolate(), source);
       UChar snippet[ContentSecurityPolicy::kMaxSampleLength + 1];
       size_t len = std::min((sizeof(snippet) / sizeof(UChar)) - 1,
diff --git a/third_party/blink/renderer/core/css/style_element.cc b/third_party/blink/renderer/core/css/style_element.cc
index 39b26e7d..7e6e23ff 100644
--- a/third_party/blink/renderer/core/css/style_element.cc
+++ b/third_party/blink/renderer/core/css/style_element.cc
@@ -133,7 +133,8 @@
 
   const ContentSecurityPolicy* csp =
       element.GetExecutionContext()
-          ? element.GetExecutionContext()->GetContentSecurityPolicyForWorld()
+          ? element.GetExecutionContext()
+                ->GetContentSecurityPolicyForCurrentWorld()
           : nullptr;
 
   // CSP is bypassed for style elements in user agent shadow DOM.
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 010f5b5..0b8b2f9 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7184,7 +7184,7 @@
   // Step 5.1. If the Should element's inline behavior be blocked by Content
   // Security Policy? algorithm returns "Blocked" when executed upon element,
   // "script attribute", and value, then return. [CSP] [spec text]
-  if (!window->GetContentSecurityPolicyForWorld()->AllowInline(
+  if (!window->GetContentSecurityPolicyForCurrentWorld()->AllowInline(
           ContentSecurityPolicy::InlineType::kScriptAttribute, element,
           listener->ScriptBody(), String() /* nonce */, context_url,
           context_line))
diff --git a/third_party/blink/renderer/core/dom/document_init.cc b/third_party/blink/renderer/core/dom/document_init.cc
index 59ab0af9..7482530 100644
--- a/third_party/blink/renderer/core/dom/document_init.cc
+++ b/third_party/blink/renderer/core/dom/document_init.cc
@@ -526,7 +526,7 @@
   // document.
   if (!GetFrame()->Loader().StateMachine()->IsDisplayingInitialEmptyDocument())
     return false;
-  return GetFrame()->GetDocument()->GetSecurityOrigin()->CanAccess(
+  return GetFrame()->DomWindow()->GetSecurityOrigin()->CanAccess(
       GetDocumentOrigin().get());
 }
 
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index aa6be04..92e57b6 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -6158,7 +6158,7 @@
               ContainingShadowRoot()->IsUserAgent()) ||
              (GetExecutionContext() &&
               GetExecutionContext()
-                  ->GetContentSecurityPolicyForWorld()
+                  ->GetContentSecurityPolicyForCurrentWorld()
                   ->AllowInline(
                       ContentSecurityPolicy::InlineType::kStyleAttribute, this,
                       new_style_string, String() /* nonce */,
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.cc b/third_party/blink/renderer/core/execution_context/execution_context.cc
index 15e7869..d9ab972 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.cc
+++ b/third_party/blink/renderer/core/execution_context/execution_context.cc
@@ -231,7 +231,8 @@
   return *csp_delegate_;
 }
 
-ContentSecurityPolicy* ExecutionContext::GetContentSecurityPolicyForWorld() {
+ContentSecurityPolicy*
+ExecutionContext::GetContentSecurityPolicyForCurrentWorld() {
   v8::Isolate* isolate = GetIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> v8_context = isolate->GetCurrentContext();
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index e0e4548..0aebaf7 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -165,10 +165,10 @@
   // JavaScript world we are in.
   // Note: As part of crbug.com/896041, existing usages of
   // ContentSecurityPolicy::ShouldBypassMainWorld should eventually be replaced
-  // by GetContentSecurityPolicyForWorld. However this is under active
+  // by GetContentSecurityPolicyForCurrentWorld. However this is under active
   // development, hence new callers should still use
   // ContentSecurityPolicy::ShouldBypassMainWorld for now.
-  ContentSecurityPolicy* GetContentSecurityPolicyForWorld();
+  ContentSecurityPolicy* GetContentSecurityPolicyForCurrentWorld();
 
   // Returns the content security policy to be used for the given |world|.
   virtual ContentSecurityPolicy* GetContentSecurityPolicyForWorld(
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_parser.cc b/third_party/blink/renderer/core/feature_policy/feature_policy_parser.cc
index 9c2566c..0e928919 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy_parser.cc
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy_parser.cc
@@ -8,6 +8,7 @@
 
 #include <bitset>
 #include "base/metrics/histogram_macros.h"
+#include "net/http/structured_headers.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
@@ -50,12 +51,11 @@
 
   ~ParsingContext() = default;
 
-  ParsedFeaturePolicy Parse(const String& policy);
+  ParsedFeaturePolicy ParseIR(const internal::FeaturePolicyNode& root);
+  internal::FeaturePolicyNode ParseFeaturePolicyToIR(const String& policy);
+  internal::FeaturePolicyNode ParsePermissionsPolicyToIR(const String& policy);
 
  private:
-  ParsedFeaturePolicy ParseIR(const internal::FeaturePolicyNode& root);
-  internal::FeaturePolicyNode ParseToIR(const String& policy);
-
   // normally 1 char = 1 byte
   // max length to parse = 2^16 = 64 kB
   static constexpr wtf_size_t MAX_LENGTH_PARSE = 1 << 16;
@@ -328,10 +328,6 @@
   return parsed_feature;
 }
 
-ParsedFeaturePolicy ParsingContext::Parse(const String& policy) {
-  return ParseIR(ParseToIR(policy));
-}
-
 ParsedFeaturePolicy ParsingContext::ParseIR(
     const internal::FeaturePolicyNode& root) {
   ParsedFeaturePolicy parsed_policy;
@@ -347,7 +343,8 @@
   return parsed_policy;
 }
 
-internal::FeaturePolicyNode ParsingContext::ParseToIR(const String& policy) {
+internal::FeaturePolicyNode ParsingContext::ParseFeaturePolicyToIR(
+    const String& policy) {
   internal::FeaturePolicyNode root;
 
   if (policy.length() > MAX_LENGTH_PARSE) {
@@ -403,15 +400,87 @@
   return root;
 }
 
+internal::FeaturePolicyNode ParsingContext::ParsePermissionsPolicyToIR(
+    const String& policy) {
+  auto root = net::structured_headers::ParseDictionary(policy.Utf8());
+  if (!root) {
+    logger_.Error(
+        "Parse of permission policy failed because of errors reported by "
+        "strctured header parser.");
+    return {};
+  }
+
+  internal::FeaturePolicyNode ir_root;
+  for (const auto& feature_entry : root.value()) {
+    const auto& key = feature_entry.first;
+    const char* feature_name = key.c_str();
+    const auto& value = feature_entry.second;
+
+    if (!value.params.empty()) {
+      logger_.Warn(
+          String::Format("Feature %s's parameters are ignored.", feature_name));
+    }
+
+    Vector<String> allowlist;
+    for (const auto& parameterized_item : value.member) {
+      if (!parameterized_item.params.empty()) {
+        logger_.Warn(String::Format("Feature %s's parameters are ignored.",
+                                    feature_name));
+      }
+
+      String allowlist_item;
+      if (parameterized_item.item.is_token()) {
+        // All special keyword appears as token, i.e. self, src and *.
+        const std::string& token_value = parameterized_item.item.GetString();
+        if (token_value != "*" && token_value != "self") {
+          logger_.Warn(String::Format(
+              "Invalid allowlist item(%s) for feature %s. Allowlist item "
+              "must be *, self or quoted url.",
+              token_value.c_str(), feature_name));
+          continue;
+        }
+
+        if (token_value == "*") {
+          allowlist_item = "*";
+        } else {
+          allowlist_item = String::Format("'%s'", token_value.c_str());
+        }
+      } else if (parameterized_item.item.is_string()) {
+        allowlist_item = parameterized_item.item.GetString().c_str();
+      } else {
+        logger_.Warn(
+            String::Format("Invalid allowlist item for feature %s. Allowlist "
+                           "item must be *, self, or quoted url.",
+                           feature_name));
+        continue;
+      }
+      allowlist.push_back(allowlist_item);
+    }
+
+    if (allowlist.IsEmpty())
+      allowlist.push_back("'none'");
+
+    ir_root.push_back(
+        internal::FeaturePolicyDeclarationNode{feature_name, allowlist});
+  }
+
+  return ir_root;
+}
+
 }  // namespace
 
 ParsedFeaturePolicy FeaturePolicyParser::ParseHeader(
-    const String& policy,
+    const String& feature_policy_header,
     scoped_refptr<const SecurityOrigin> origin,
     PolicyParserMessageBuffer& logger,
-    FeaturePolicyParserDelegate* delegate) {
-  return Parse(policy, origin, nullptr, logger, GetDefaultFeatureNameMap(),
-               delegate);
+    FeaturePolicyParserDelegate* delegate,
+    const String& permissions_policy_header) {
+  ParsingContext context(logger, origin, nullptr, GetDefaultFeatureNameMap(),
+                         delegate);
+  auto policy_ir =
+      context.ParsePermissionsPolicyToIR(permissions_policy_header);
+  policy_ir.AppendVector(context.ParseFeaturePolicyToIR(feature_policy_header));
+  return context.ParseIR(policy_ir);
 }
 
 ParsedFeaturePolicy FeaturePolicyParser::ParseAttribute(
@@ -420,21 +489,33 @@
     scoped_refptr<const SecurityOrigin> src_origin,
     PolicyParserMessageBuffer& logger,
     FeaturePolicyParserDelegate* delegate) {
-  return Parse(policy, self_origin, src_origin, logger,
-               GetDefaultFeatureNameMap(), delegate);
+  ParsingContext context(logger, self_origin, src_origin,
+                         GetDefaultFeatureNameMap(), delegate);
+  return context.ParseIR(context.ParseFeaturePolicyToIR(policy));
 }
 
-// static
-ParsedFeaturePolicy FeaturePolicyParser::Parse(
+ParsedFeaturePolicy FeaturePolicyParser::ParseFeaturePolicyForTest(
     const String& policy,
     scoped_refptr<const SecurityOrigin> self_origin,
     scoped_refptr<const SecurityOrigin> src_origin,
     PolicyParserMessageBuffer& logger,
     const FeatureNameMap& feature_names,
     FeaturePolicyParserDelegate* delegate) {
-  return ParsingContext(logger, self_origin, src_origin, feature_names,
-                        delegate)
-      .Parse(policy);
+  ParsingContext context(logger, self_origin, src_origin, feature_names,
+                         delegate);
+  return context.ParseIR(context.ParseFeaturePolicyToIR(policy));
+}
+
+ParsedFeaturePolicy FeaturePolicyParser::ParsePermissionsPolicyForTest(
+    const String& policy,
+    scoped_refptr<const SecurityOrigin> self_origin,
+    scoped_refptr<const SecurityOrigin> src_origin,
+    PolicyParserMessageBuffer& logger,
+    const FeatureNameMap& feature_names,
+    FeaturePolicyParserDelegate* delegate) {
+  ParsingContext context(logger, self_origin, src_origin, feature_names,
+                         delegate);
+  return context.ParseIR(context.ParsePermissionsPolicyToIR(policy));
 }
 
 bool IsFeatureDeclared(mojom::blink::FeaturePolicyFeature feature,
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_parser.h b/third_party/blink/renderer/core/feature_policy/feature_policy_parser.h
index 5c7bcf7..0d5016f3 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy_parser.h
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy_parser.h
@@ -58,10 +58,11 @@
   // parsing. Example of a feature policy string:
   //     "vibrate a.com b.com; fullscreen 'none'; payment 'self', payment *".
   static ParsedFeaturePolicy ParseHeader(
-      const String& policy,
+      const String& feature_policy_header,
       scoped_refptr<const SecurityOrigin>,
       PolicyParserMessageBuffer& logger,
-      FeaturePolicyParserDelegate* delegate = nullptr);
+      FeaturePolicyParserDelegate* delegate = nullptr,
+      const String& permission_policy_header = g_empty_string);
 
   // Converts a container policy string into a vector of allowlists, given self
   // and src origins provided, one for each feature specified. Unrecognized
@@ -75,13 +76,15 @@
       PolicyParserMessageBuffer& logger,
       FeaturePolicyParserDelegate* delegate = nullptr);
 
-  // Converts a feature policy string into a vector of allowlists (see comments
-  // above), with an explicit FeatureNameMap. This algorithm is called by both
-  // header policy parsing and container policy parsing. |self_origin|,
-  // |src_origin|, and |execution_context| are nullable. The optional
-  // ExecutionContext is used to determine if any origin trials affect the
-  // parsing.
-  static ParsedFeaturePolicy Parse(
+  static ParsedFeaturePolicy ParseFeaturePolicyForTest(
+      const String& policy,
+      scoped_refptr<const SecurityOrigin> self_origin,
+      scoped_refptr<const SecurityOrigin> src_origin,
+      PolicyParserMessageBuffer& logger,
+      const FeatureNameMap& feature_names,
+      FeaturePolicyParserDelegate* delegate = nullptr);
+
+  static ParsedFeaturePolicy ParsePermissionsPolicyForTest(
       const String& policy,
       scoped_refptr<const SecurityOrigin> self_origin,
       scoped_refptr<const SecurityOrigin> src_origin,
@@ -89,6 +92,7 @@
       const FeatureNameMap& feature_names,
       FeaturePolicyParserDelegate* delegate = nullptr);
 };
+
 // Returns true iff any declaration in the policy is for the given feature.
 CORE_EXPORT bool IsFeatureDeclared(mojom::blink::FeaturePolicyFeature,
                                    const ParsedFeaturePolicy&);
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc b/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
index bac1082..8ae43f2 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy_test.cc
@@ -24,6 +24,10 @@
 #define ORIGIN_C "https://example.org/"
 #define OPAQUE_ORIGIN ""
 
+// identifier used for feature/permissions policy parsing test.
+// when there is a testcase for one syntax but not the other.
+#define NOT_APPLICABLE nullptr
+
 class GURL;
 
 namespace blink {
@@ -110,7 +114,8 @@
   const char* test_name;
 
   // Test inputs.
-  const char* policy_string;
+  const char* feature_policy_string;
+  const char* permissions_policy_string;
   const char* self_origin;
   const char* src_origin;
 
@@ -121,26 +126,63 @@
 class FeaturePolicyParserParsingTest
     : public FeaturePolicyParserTest,
       public ::testing::WithParamInterface<FeaturePolicyParserTestCase> {
- protected:
-  ParsedFeaturePolicy Parse(const char* policy_string,
-                            const char* self_origin_string,
-                            const char* src_origin_string,
-                            PolicyParserMessageBuffer& logger,
-                            const FeatureNameMap& feature_names) {
-    scoped_refptr<const SecurityOrigin> self_origin =
-        SecurityOrigin::CreateFromString(self_origin_string);
-
+ private:
+  scoped_refptr<const SecurityOrigin> GetSrcOrigin(const char* origin_str) {
     scoped_refptr<const SecurityOrigin> src_origin;
-    if (String(src_origin_string) == OPAQUE_ORIGIN) {
+    if (String(origin_str) == OPAQUE_ORIGIN) {
       src_origin = SecurityOrigin::CreateUniqueOpaque();
     } else {
-      src_origin = src_origin_string
-                       ? SecurityOrigin::CreateFromString(src_origin_string)
-                       : nullptr;
+      src_origin =
+          origin_str ? SecurityOrigin::CreateFromString(origin_str) : nullptr;
     }
+    return src_origin;
+  }
 
-    return FeaturePolicyParser::Parse(policy_string, self_origin, src_origin,
-                                      logger, feature_names);
+ protected:
+  ParsedFeaturePolicy ParseFeaturePolicy(
+      const char* policy_string,
+      const char* self_origin_string,
+      const char* src_origin_string,
+      PolicyParserMessageBuffer& logger,
+      const FeatureNameMap& feature_names,
+      FeaturePolicyParserDelegate* delegate = nullptr) {
+    return FeaturePolicyParser::ParseFeaturePolicyForTest(
+        policy_string, SecurityOrigin::CreateFromString(self_origin_string),
+        GetSrcOrigin(src_origin_string), logger, feature_names, delegate);
+  }
+
+  ParsedFeaturePolicy ParsePermissionsPolicy(
+      const char* policy_string,
+      const char* self_origin_string,
+      const char* src_origin_string,
+      PolicyParserMessageBuffer& logger,
+      const FeatureNameMap& feature_names,
+      FeaturePolicyParserDelegate* delegate = nullptr) {
+    return FeaturePolicyParser::ParsePermissionsPolicyForTest(
+        policy_string, SecurityOrigin::CreateFromString(self_origin_string),
+        GetSrcOrigin(src_origin_string), logger, feature_names, delegate);
+  }
+
+  void CheckParsedPolicy(const ParsedFeaturePolicy& actual,
+                         const ParsedPolicyForTest& expected) {
+    ASSERT_EQ(actual.size(), expected.size());
+    for (size_t i = 0; i < actual.size(); ++i) {
+      const auto& actual_declaration = actual[i];
+      const auto& expected_declaration = expected[i];
+
+      EXPECT_EQ(actual_declaration.feature, expected_declaration.feature);
+      EXPECT_EQ(actual_declaration.matches_all_origins,
+                expected_declaration.matches_all_origins);
+      EXPECT_EQ(actual_declaration.matches_opaque_src,
+                expected_declaration.matches_opaque_src);
+
+      ASSERT_EQ(actual_declaration.allowed_origins.size(),
+                expected_declaration.origins.size());
+      for (size_t j = 0; j < actual_declaration.allowed_origins.size(); ++j) {
+        EXPECT_TRUE(actual_declaration.allowed_origins[j].IsSameOriginWith(
+            url::Origin::Create(GURL(expected_declaration.origins[j]))));
+      }
+    }
   }
 
  public:
@@ -150,14 +192,32 @@
 const FeaturePolicyParserTestCase FeaturePolicyParserParsingTest::kCases[] = {
     {
         /* test_name */ "EmptyPolicy",
-        /* policy_string */ "",
+        /* feature_policy_string */ "",
+        /* permissions_policy_string */ "",
         /* self_origin */ ORIGIN_A,
         /* src_origin */ ORIGIN_B,
         /* expected_parse_result */ {},
     },
     {
         /* test_name */ "SimplePolicyWithSelf",
-        /* policy_string */ "geolocation 'self'",
+        /* feature_policy_string */ "geolocation 'self'",
+        /* permissions_policy_string */ "geolocation=self",
+        /* self_origin */ ORIGIN_A,
+        /* src_origin */ ORIGIN_B,
+        /* expected_parse_result */
+        {
+            {
+                mojom::blink::FeaturePolicyFeature::kGeolocation,
+                /* matches_all_origins */ false,
+                /* matches_opaque_src */ false,
+                {ORIGIN_A},
+            },
+        },
+    },
+    {
+        /* test_name */ "SimplePolicyWithSelfExplicitListSyntax",
+        /* feature_policy_string */ NOT_APPLICABLE,
+        /* permissions_policy_string */ "geolocation=(self)",
         /* self_origin */ ORIGIN_A,
         /* src_origin */ ORIGIN_B,
         /* expected_parse_result */
@@ -172,7 +232,8 @@
     },
     {
         /* test_name */ "SimplePolicyWithStar",
-        /* policy_string */ "geolocation *",
+        /* feature_policy_string */ "geolocation *",
+        /* permissions_policy_string */ "geolocation=*",
         /* self_origin */ ORIGIN_A,
         /* src_origin */ ORIGIN_B,
         /* expected_parse_result */
@@ -187,10 +248,14 @@
     },
     {
         /* test_name */ "ComplicatedPolicy",
-        /* policy_string */
+        /* feature_policy_string */
         "geolocation *; "
         "fullscreen " ORIGIN_B " " ORIGIN_C "; "
         "payment 'self'",
+        /* permissions_policy_string */
+        "geolocation=*, "
+        "fullscreen=(\"" ORIGIN_B "\" \"" ORIGIN_C "\"),"
+        "payment=self",
         /* self_origin */ ORIGIN_A,
         /* src_origin */ ORIGIN_B,
         /* expected_parse_result */
@@ -216,11 +281,15 @@
         },
     },
     {
-        /* test_name */ "MultiplePolicies",
-        /* policy_string */
+        /* test_name */ "MultiplePoliciesIncludingBadFeatureName",
+        /* feature_policy_string */
         "geolocation * " ORIGIN_B "; "
-        "fullscreen " ORIGIN_B " none " ORIGIN_C ","
+        "fullscreen " ORIGIN_B " bad_feature_name " ORIGIN_C ","
         "payment 'self' badorigin",
+        /* permissions_policy_string */
+        "geolocation=(* \"" ORIGIN_B "\"),"
+        "fullscreen=(\"" ORIGIN_B "\" bad_feature_name \"" ORIGIN_C "\"),"
+        "payment=(self \"badorigin\")",
         /* self_origin */ ORIGIN_A,
         /* src_origin */ ORIGIN_B,
         /* expected_parse_result */
@@ -247,7 +316,12 @@
     },
     {
         /* test_name */ "HeaderPoliciesWithNoOptionalOriginLists",
-        /* policy_string */ "geolocation;fullscreen;payment",
+        /* feature_policy_string */ "geolocation;fullscreen;payment",
+        // Note: In structured header, if no value is associated with a key
+        // in dictionary, default value would be boolean true, which is
+        // not allowed as allowlist value in permission policy syntax.
+        /* permissions_policy_string */
+        "geolocation=self,fullscreen=self,payment=self",
         /* self_origin */ ORIGIN_A,
         /* src_origin */ nullptr,
         /* expected_parse_result */
@@ -274,14 +348,16 @@
     },
     {
         /* test_name */ "EmptyPolicyOpaqueSrcOrigin",
-        /* policy_string */ "",
+        /* feature_policy_string */ "",
+        /* permissions_policy_string */ "",
         /* self_origin */ ORIGIN_A,
         /* src_origin */ OPAQUE_ORIGIN,
         /* expected_parse_result */ {},
     },
     {
         /* test_name */ "SimplePolicyOpaqueSrcOrigin",
-        /* policy_string */ "geolocation",
+        /* feature_policy_string */ "geolocation",
+        /* permissions_policy_string */ NOT_APPLICABLE,
         /* self_origin */ ORIGIN_A,
         /* src_origin */ OPAQUE_ORIGIN,
         /* expected_parse_result */
@@ -296,7 +372,8 @@
     },
     {
         /* test_name */ "SimplePolicyWithSrcOpaqueSrcOrigin",
-        /* policy_string */ "geolocation 'src'",
+        /* feature_policy_string */ "geolocation 'src'",
+        /* permissions_policy_string */ NOT_APPLICABLE,
         /* self_origin */ ORIGIN_A,
         /* src_origin */ OPAQUE_ORIGIN,
         /* expected_parse_result */
@@ -311,7 +388,8 @@
     },
     {
         /* test_name */ "SimplePolicyWithStarOpaqueSrcOrigin",
-        /* policy_string */ "geolocation *",
+        /* feature_policy_string */ "geolocation *",
+        /* permissions_policy_string */ "geolocation=*",
         /* self_origin */ ORIGIN_A,
         /* src_origin */ OPAQUE_ORIGIN,
         /* expected_parse_result */
@@ -326,7 +404,9 @@
     },
     {
         /* test_name */ "PolicyWithExplicitOriginsOpaqueSrcOrigin",
-        /* policy_string */ "geolocation " ORIGIN_B " " ORIGIN_C,
+        /* feature_policy_string */ "geolocation " ORIGIN_B " " ORIGIN_C,
+        /* permissions_policy_string */
+        "geolocation=(\"" ORIGIN_B "\" \"" ORIGIN_C "\")",
         /* self_origin */ ORIGIN_A,
         /* src_origin */ OPAQUE_ORIGIN,
         /* expected_parse_result */
@@ -342,7 +422,8 @@
     {
         /* test_name */ "PolicyWithMultipleOriginsIncludingSrc"
                         "OpaqueSrcOrigin",
-        /* policy_string */ "geolocation " ORIGIN_B " 'src'",
+        /* feature_policy_string */ "geolocation " ORIGIN_B " 'src'",
+        /* permissions_policy_string */ NOT_APPLICABLE,
         /* self_origin */ ORIGIN_A,
         /* src_origin */ OPAQUE_ORIGIN,
         /* expected_parse_result */
@@ -355,6 +436,57 @@
             },
         },
     },
+    {
+        /* test_name */ "PolicyWithInvalidDataTypeInt",
+        /* feature_policy_string */ NOT_APPLICABLE,
+        // int value should be rejected as allowlist items.
+        /* permissions_policy_string */ "geolocation=9",
+        /* self_origin */ ORIGIN_A,
+        /* src_origin */ nullptr,
+        /* expected_parse_result */
+        {
+            {
+                mojom::blink::FeaturePolicyFeature::kGeolocation,
+                /* matches_all_origins */ false,
+                /* matches_opaque_src */ false,
+                {},
+            },
+        },
+    },
+    {
+        /* test_name */ "PolicyWithInvalidDataTypeFloat",
+        /* feature_policy_string */ NOT_APPLICABLE,
+        // decimal value should be rejected as allowlist items.
+        /* permissions_policy_string */ "geolocation=1.1",
+        /* self_origin */ ORIGIN_A,
+        /* src_origin */ nullptr,
+        /* expected_parse_result */
+        {
+            {
+                mojom::blink::FeaturePolicyFeature::kGeolocation,
+                /* matches_all_origins */ false,
+                /* matches_opaque_src */ false,
+                {},
+            },
+        },
+    },
+    {
+        /* test_name */ "PolicyWithInvalidDataTypeBoolean",
+        /* feature_policy_string */ NOT_APPLICABLE,
+        // boolean value should be rejected as allowlist items.
+        /* permissions_policy_string */ "geolocation=?0",
+        /* self_origin */ ORIGIN_A,
+        /* src_origin */ nullptr,
+        /* expected_parse_result */
+        {
+            {
+                mojom::blink::FeaturePolicyFeature::kGeolocation,
+                /* matches_all_origins */ false,
+                /* matches_opaque_src */ false,
+                {},
+            },
+        },
+    },
 };
 
 INSTANTIATE_TEST_SUITE_P(
@@ -365,39 +497,71 @@
       return param_info.param.test_name;
     });
 
-TEST_P(FeaturePolicyParserParsingTest, PolicyParsedCorrectly) {
+TEST_P(FeaturePolicyParserParsingTest, FeaturePolicyParsedCorrectly) {
   PolicyParserMessageBuffer logger;
   const FeaturePolicyParserTestCase& test_case = GetParam();
+  if (test_case.feature_policy_string == NOT_APPLICABLE)
+    return;
+
   ASSERT_NE(test_case.self_origin, nullptr);
-  const ParsedFeaturePolicy actual =
-      Parse(test_case.policy_string, test_case.self_origin,
-            test_case.src_origin, logger, test_feature_name_map);
-  const ParsedPolicyForTest& expected = test_case.expected_parse_result;
-  ASSERT_EQ(actual.size(), expected.size());
-  for (size_t i = 0; i < actual.size(); ++i) {
-    const auto& actual_declaration = actual[i];
-    const auto& expected_declaration = expected[i];
 
-    EXPECT_EQ(actual_declaration.feature, expected_declaration.feature);
-    EXPECT_EQ(actual_declaration.matches_all_origins,
-              expected_declaration.matches_all_origins);
-    EXPECT_EQ(actual_declaration.matches_opaque_src,
-              expected_declaration.matches_opaque_src);
+  CheckParsedPolicy(
+      ParseFeaturePolicy(test_case.feature_policy_string, test_case.self_origin,
+                         test_case.src_origin, logger, test_feature_name_map),
+      test_case.expected_parse_result);
+}
 
-    ASSERT_EQ(actual_declaration.allowed_origins.size(),
-              expected_declaration.origins.size());
-    for (size_t j = 0; j < actual_declaration.allowed_origins.size(); ++j) {
-      EXPECT_TRUE(actual_declaration.allowed_origins[j].IsSameOriginWith(
-          url::Origin::Create(GURL(expected_declaration.origins[j]))));
-    }
-  }
+TEST_P(FeaturePolicyParserParsingTest, PermissionsPolicyParsedCorrectly) {
+  PolicyParserMessageBuffer logger;
+  const FeaturePolicyParserTestCase& test_case = GetParam();
+  if (test_case.permissions_policy_string == NOT_APPLICABLE)
+    return;
+
+  ASSERT_NE(test_case.self_origin, nullptr);
+  CheckParsedPolicy(
+      ParsePermissionsPolicy(test_case.permissions_policy_string,
+                             test_case.self_origin, test_case.src_origin,
+                             logger, test_feature_name_map),
+      test_case.expected_parse_result);
+}
+
+TEST_F(FeaturePolicyParserParsingTest,
+       FeaturePolicyHeaderPermissionsPolicyHeaderCoExist) {
+  PolicyParserMessageBuffer logger;
+  // When there is conflict take the value from permission policy.
+  // Non-conflicting entries will be merged.
+  CheckParsedPolicy(
+      FeaturePolicyParser::ParseHeader(
+          "geolocation 'none', fullscreen 'self'", origin_a_.get(), logger,
+          nullptr /* delegate */, "geolocation=self, payment=*"),
+      {
+          {
+              mojom::blink::FeaturePolicyFeature::kGeolocation,
+              /* matches_all_origins */ false,
+              /* matches_opaque_src */ false,
+              {ORIGIN_A},
+          },
+          {
+              mojom::blink::FeaturePolicyFeature::kPayment,
+              /* matches_all_origins */ true,
+              /* matches_opaque_src */ true,
+              {},
+          },
+          {
+              mojom::blink::FeaturePolicyFeature::kFullscreen,
+              /* matches_all_origins */ false,
+              /* matches_opaque_src */ false,
+              {ORIGIN_A},
+          },
+      });
 }
 
 TEST_F(FeaturePolicyParserTest, ParseValidPolicy) {
   for (const char* policy_string : kValidPolicies) {
     PolicyParserMessageBuffer logger;
-    FeaturePolicyParser::Parse(policy_string, origin_a_.get(), origin_b_.get(),
-                               logger, test_feature_name_map);
+    FeaturePolicyParser::ParseFeaturePolicyForTest(
+        policy_string, origin_a_.get(), origin_b_.get(), logger,
+        test_feature_name_map);
     EXPECT_EQ(0UL, logger.GetMessages().size())
         << "Should parse " << policy_string;
   }
@@ -406,8 +570,9 @@
 TEST_F(FeaturePolicyParserTest, ParseInvalidPolicy) {
   for (const char* policy_string : kInvalidPolicies) {
     PolicyParserMessageBuffer logger;
-    FeaturePolicyParser::Parse(policy_string, origin_a_.get(), origin_b_.get(),
-                               logger, test_feature_name_map);
+    FeaturePolicyParser::ParseFeaturePolicyForTest(
+        policy_string, origin_a_.get(), origin_b_.get(), logger,
+        test_feature_name_map);
     EXPECT_LT(0UL, logger.GetMessages().size())
         << "Should fail to parse " << policy_string;
   }
@@ -416,8 +581,9 @@
 TEST_F(FeaturePolicyParserTest, ParseTooLongPolicy) {
   PolicyParserMessageBuffer logger;
   auto policy_string = "geolocation http://" + std::string(1 << 17, 'a');
-  FeaturePolicyParser::Parse(policy_string.c_str(), origin_a_.get(),
-                             origin_b_.get(), logger, test_feature_name_map);
+  FeaturePolicyParser::ParseFeaturePolicyForTest(
+      policy_string.c_str(), origin_a_.get(), origin_b_.get(), logger,
+      test_feature_name_map);
   EXPECT_EQ(1UL, logger.GetMessages().size())
       << "Should fail to parse string with size " << policy_string.size();
 }
@@ -428,8 +594,9 @@
   HistogramTester tester;
   PolicyParserMessageBuffer logger;
 
-  FeaturePolicyParser::Parse("payment; fullscreen", origin_a_.get(), nullptr,
-                             logger, test_feature_name_map);
+  FeaturePolicyParser::ParseFeaturePolicyForTest("payment; fullscreen",
+                                                 origin_a_.get(), nullptr,
+                                                 logger, test_feature_name_map);
   tester.ExpectTotalCount(histogram_name, 2);
   tester.ExpectBucketCount(
       histogram_name,
@@ -448,11 +615,12 @@
 
   // If the same feature is listed multiple times, it should only be counted
   // once.
-  FeaturePolicyParser::Parse("geolocation 'self'; payment; geolocation *",
-                             origin_a_.get(), nullptr, logger,
-                             test_feature_name_map);
-  FeaturePolicyParser::Parse("fullscreen 'self', fullscreen *", origin_a_.get(),
-                             nullptr, logger, test_feature_name_map);
+  FeaturePolicyParser::ParseFeaturePolicyForTest(
+      "geolocation 'self'; payment; geolocation *", origin_a_.get(), nullptr,
+      logger, test_feature_name_map);
+  FeaturePolicyParser::ParseFeaturePolicyForTest(
+      "fullscreen 'self', fullscreen *", origin_a_.get(), nullptr, logger,
+      test_feature_name_map);
   tester.ExpectTotalCount(histogram_name, 3);
   tester.ExpectBucketCount(
       histogram_name,
@@ -472,12 +640,13 @@
   PolicyParserMessageBuffer logger;
   auto dummy = std::make_unique<DummyPageHolder>();
 
-  FeaturePolicyParser::Parse("payment; fullscreen", origin_a_.get(),
-                             origin_b_.get(), logger, test_feature_name_map,
-                             dummy->GetFrame().DomWindow());
-  FeaturePolicyParser::Parse("fullscreen; geolocation", origin_a_.get(),
-                             origin_b_.get(), logger, test_feature_name_map,
-                             dummy->GetFrame().DomWindow());
+  FeaturePolicyParser::ParseFeaturePolicyForTest(
+      "payment; fullscreen", origin_a_.get(), origin_b_.get(), logger,
+      test_feature_name_map, dummy->GetFrame().DomWindow());
+  FeaturePolicyParser::ParseFeaturePolicyForTest(
+      "fullscreen; geolocation", origin_a_.get(), origin_b_.get(), logger,
+      test_feature_name_map, dummy->GetFrame().DomWindow());
+
   tester.ExpectTotalCount(histogram_name, 3);
   tester.ExpectBucketCount(
       histogram_name,
@@ -501,12 +670,13 @@
   auto dummy = std::make_unique<DummyPageHolder>();
   auto dummy2 = std::make_unique<DummyPageHolder>();
 
-  FeaturePolicyParser::Parse("payment; fullscreen", origin_a_.get(),
-                             origin_b_.get(), logger, test_feature_name_map,
-                             dummy->GetFrame().DomWindow());
-  FeaturePolicyParser::Parse("fullscreen; geolocation", origin_a_.get(),
-                             origin_b_.get(), logger, test_feature_name_map,
-                             dummy2->GetFrame().DomWindow());
+  FeaturePolicyParser::ParseFeaturePolicyForTest(
+      "payment; fullscreen", origin_a_.get(), origin_b_.get(), logger,
+      test_feature_name_map, dummy->GetFrame().DomWindow());
+  FeaturePolicyParser::ParseFeaturePolicyForTest(
+      "fullscreen; geolocation", origin_a_.get(), origin_b_.get(), logger,
+      test_feature_name_map, dummy2->GetFrame().DomWindow());
+
   tester.ExpectTotalCount(histogram_name, 4);
   tester.ExpectBucketCount(
       histogram_name,
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index e9b0475f..6888fe3 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -548,7 +548,7 @@
 
   // "- should fetching |request| be blocked as content security returns
   //    blocked"
-  if (!execution_context_->GetContentSecurityPolicyForWorld()
+  if (!execution_context_->GetContentSecurityPolicyForCurrentWorld()
            ->AllowConnectToSource(fetch_request_data_->Url(),
                                   fetch_request_data_->Url(),
                                   RedirectStatus::kNoRedirect)) {
diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc
index 7a6d243..83650656 100644
--- a/third_party/blink/renderer/core/frame/dom_window.cc
+++ b/third_party/blink/renderer/core/frame/dom_window.cc
@@ -182,15 +182,14 @@
 String DOMWindow::SanitizedCrossDomainAccessErrorMessage(
     const LocalDOMWindow* accessing_window,
     CrossDocumentAccessPolicy cross_document_access) const {
-  if (!accessing_window || !accessing_window->document() || !GetFrame())
+  if (!accessing_window || !GetFrame())
     return String();
 
-  const KURL& accessing_window_url = accessing_window->document()->Url();
+  const KURL& accessing_window_url = accessing_window->Url();
   if (accessing_window_url.IsNull())
     return String();
 
-  const SecurityOrigin* active_origin =
-      accessing_window->document()->GetSecurityOrigin();
+  const SecurityOrigin* active_origin = accessing_window->GetSecurityOrigin();
   String message;
   if (cross_document_access == CrossDocumentAccessPolicy::kDisallowed) {
     message = "Blocked a restricted frame with origin \"" +
@@ -209,17 +208,16 @@
 String DOMWindow::CrossDomainAccessErrorMessage(
     const LocalDOMWindow* accessing_window,
     CrossDocumentAccessPolicy cross_document_access) const {
-  if (!accessing_window || !accessing_window->document() || !GetFrame())
+  if (!accessing_window || !GetFrame())
     return String();
 
-  const KURL& accessing_window_url = accessing_window->document()->Url();
+  const KURL& accessing_window_url = accessing_window->Url();
   if (accessing_window_url.IsNull())
     return String();
 
   // FIXME: This message, and other console messages, have extra newlines.
   // Should remove them.
-  const SecurityOrigin* active_origin =
-      accessing_window->document()->GetSecurityOrigin();
+  const SecurityOrigin* active_origin = accessing_window->GetSecurityOrigin();
   const SecurityOrigin* target_origin =
       GetFrame()->GetSecurityContext()->GetSecurityOrigin();
   auto* local_dom_window = DynamicTo<LocalDOMWindow>(this);
@@ -238,24 +236,24 @@
 
   // Sandbox errors: Use the origin of the frames' location, rather than their
   // actual origin (since we know that at least one will be "null").
-  KURL active_url = accessing_window->document()->Url();
+  KURL active_url = accessing_window->Url();
   // TODO(alexmos): RemoteFrames do not have a document, and their URLs
   // aren't replicated.  For now, construct the URL using the replicated
   // origin for RemoteFrames. If the target frame is remote and sandboxed,
   // there isn't anything else to show other than "null" for its origin.
   KURL target_url = local_dom_window
-                        ? local_dom_window->document()->Url()
+                        ? local_dom_window->Url()
                         : KURL(NullURL(), target_origin->ToString());
   using SandboxFlags = network::mojom::blink::WebSandboxFlags;
   if (GetFrame()->GetSecurityContext()->IsSandboxed(SandboxFlags::kOrigin) ||
-      accessing_window->document()->IsSandboxed(SandboxFlags::kOrigin)) {
+      accessing_window->IsSandboxed(SandboxFlags::kOrigin)) {
     message = "Blocked a frame at \"" +
               SecurityOrigin::Create(active_url)->ToString() +
               "\" from accessing a frame at \"" +
               SecurityOrigin::Create(target_url)->ToString() + "\". ";
 
     if (GetFrame()->GetSecurityContext()->IsSandboxed(SandboxFlags::kOrigin) &&
-        accessing_window->document()->IsSandboxed(SandboxFlags::kOrigin)) {
+        accessing_window->IsSandboxed(SandboxFlags::kOrigin)) {
       return "Sandbox access violation: " + message +
              " Both frames are sandboxed and lack the \"allow-same-origin\" "
              "flag.";
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.h b/third_party/blink/renderer/core/frame/local_dom_window.h
index 743f288..370e0333 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.h
+++ b/third_party/blink/renderer/core/frame/local_dom_window.h
@@ -112,12 +112,6 @@
   void Trace(Visitor*) const override;
 
   // ExecutionContext overrides:
-
-  // TODO(karandeepb): Rename
-  // ExecutionContext::GetContentSecurityPolicyForWorld() to
-  // GetContentSecurityPolicyForCurrentWorld() to avoid confusion.
-  using ExecutionContext::GetContentSecurityPolicyForWorld;
-
   bool IsWindow() const final { return true; }
   bool IsContextThread() const final;
   bool ShouldInstallV8Extensions() const final;
diff --git a/third_party/blink/renderer/core/frame/local_dom_window_test.cc b/third_party/blink/renderer/core/frame/local_dom_window_test.cc
index b31dd86..278de3f 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window_test.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window_test.cc
@@ -141,7 +141,7 @@
   DISALLOW_COPY_AND_ASSIGN(IsolatedWorldCSPTest);
 };
 
-// Tests ExecutionContext::GetContentSecurityPolicyForWorld().
+// Tests ExecutionContext::GetContentSecurityPolicyForCurrentWorld().
 TEST_P(IsolatedWorldCSPTest, CSPForWorld) {
   using ::testing::ElementsAre;
 
@@ -175,7 +175,8 @@
 
   // Returns the csp headers being used for the current world.
   auto get_csp_headers = [this]() {
-    auto* csp = GetFrame().DomWindow()->GetContentSecurityPolicyForWorld();
+    auto* csp =
+        GetFrame().DomWindow()->GetContentSecurityPolicyForCurrentWorld();
     return csp->Headers();
   };
 
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 3e9a5cb..6c0f657 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -585,7 +585,7 @@
 }
 
 const SecurityContext* LocalFrame::GetSecurityContext() const {
-  return GetDocument() ? &GetDocument()->GetSecurityContext() : nullptr;
+  return DomWindow() ? &DomWindow()->GetSecurityContext() : nullptr;
 }
 
 void LocalFrame::PrintNavigationErrorMessage(const Frame& target_frame,
diff --git a/third_party/blink/renderer/core/frame/location.cc b/third_party/blink/renderer/core/frame/location.cc
index 32a09ab..31d897f2 100644
--- a/third_party/blink/renderer/core/frame/location.cc
+++ b/third_party/blink/renderer/core/frame/location.cc
@@ -285,10 +285,11 @@
   if (completed_url.ProtocolIsJavaScript()) {
     String script_source = DecodeURLEscapeSequences(
         completed_url.GetString(), DecodeURLMode::kUTF8OrIsomorphic);
-    if (!incumbent_window->GetContentSecurityPolicyForWorld()->AllowInline(
-            ContentSecurityPolicy::InlineType::kNavigation,
-            nullptr /* element */, script_source, String() /* nonce */,
-            incumbent_window->Url(), OrdinalNumber())) {
+    if (!incumbent_window->GetContentSecurityPolicyForCurrentWorld()
+             ->AllowInline(ContentSecurityPolicy::InlineType::kNavigation,
+                           nullptr /* element */, script_source,
+                           String() /* nonce */, incumbent_window->Url(),
+                           OrdinalNumber())) {
       return;
     }
   }
diff --git a/third_party/blink/renderer/core/html/html_script_element.cc b/third_party/blink/renderer/core/html/html_script_element.cc
index 756865b..228bac0 100644
--- a/third_party/blink/renderer/core/html/html_script_element.cc
+++ b/third_party/blink/renderer/core/html/html_script_element.cc
@@ -270,9 +270,10 @@
     const AtomicString& nonce,
     const WTF::OrdinalNumber& context_line,
     const String& script_content) {
-  return GetExecutionContext()->GetContentSecurityPolicyForWorld()->AllowInline(
-      ContentSecurityPolicy::InlineType::kScript, this, script_content, nonce,
-      GetDocument().Url(), context_line);
+  return GetExecutionContext()
+      ->GetContentSecurityPolicyForCurrentWorld()
+      ->AllowInline(ContentSecurityPolicy::InlineType::kScript, this,
+                    script_content, nonce, GetDocument().Url(), context_line);
 }
 
 Document& HTMLScriptElement::GetDocument() const {
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
index 1d54f04..e69fcf0 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
@@ -586,8 +586,292 @@
   }
 }
 
+void DistributeExcessBlockSizeToRows(
+    const wtf_size_t start_row_index,
+    const wtf_size_t row_count,
+    LayoutUnit desired_block_size,
+    bool desired_block_size_is_rowspan,
+    LayoutUnit border_block_spacing,
+    LayoutUnit percentage_resolution_block_size,
+    NGTableTypes::Rows* rows) {
+  DCHECK_LE(start_row_index + row_count, rows->size());
+  DCHECK_GE(desired_block_size, LayoutUnit());
+  // This algorithm has not been defined by the standard in 2019.
+  // Discussion at https://github.com/w3c/csswg-drafts/issues/4418
+  if (row_count == 0)
+    return;
+  NGTableTypes::Row* start_row = std::next(rows->begin(), start_row_index);
+  NGTableTypes::Row* end_row =
+      std::next(rows->begin(), start_row_index + row_count);
+
+  auto RowBlockSizeDeficit = [&percentage_resolution_block_size](
+                                 NGTableTypes::Row* row) {
+    if (percentage_resolution_block_size == kIndefiniteSize)
+      return LayoutUnit();
+    DCHECK(row->percent);
+    return (LayoutUnit(*row->percent * percentage_resolution_block_size / 100) -
+            row->block_size)
+        .ClampNegativeToZero();
+  };
+
+  auto IsUnconstrainedNonEmptyRow =
+      [&percentage_resolution_block_size](NGTableTypes::Row* row) {
+        if (row->block_size == LayoutUnit())
+          return false;
+        if (row->percent && percentage_resolution_block_size == kIndefiniteSize)
+          return true;
+        return !row->is_constrained;
+      };
+
+  auto IsRowWithOriginatingRowspan =
+      [&start_row, &desired_block_size_is_rowspan](NGTableTypes::Row* row) {
+        // Rowspans are treated specially only during rowspan distribution.
+        return desired_block_size_is_rowspan && row != start_row &&
+               row->has_rowspan_start;
+      };
+
+  unsigned percent_rows_with_deficit_count = 0;
+  unsigned rows_with_originating_rowspan = 0;
+  unsigned unconstrained_non_empty_row_count = 0;
+  unsigned constrained_non_empty_row_count = 0;
+  unsigned empty_row_count = 0;
+
+  LayoutUnit total_block_size;
+  LayoutUnit percentage_block_size_deficit;
+  LayoutUnit unconstrained_non_empty_row_block_size;
+
+  for (NGTableTypes::Row* row = start_row; row != end_row; ++row) {
+    total_block_size += row->block_size;
+    if (row->percent) {
+      LayoutUnit deficit = RowBlockSizeDeficit(row);
+      if (deficit != LayoutUnit()) {
+        percent_rows_with_deficit_count++;
+        percentage_block_size_deficit += deficit;
+      }
+    }
+    if (IsRowWithOriginatingRowspan(row)) {
+      rows_with_originating_rowspan++;
+    }
+    if (IsUnconstrainedNonEmptyRow(row)) {
+      unconstrained_non_empty_row_count++;
+      unconstrained_non_empty_row_block_size += row->block_size;
+    } else if (row->is_constrained && row->block_size != LayoutUnit()) {
+      constrained_non_empty_row_count++;
+    }
+    if (row->block_size == LayoutUnit())
+      empty_row_count++;
+  }
+
+  LayoutUnit distributable_block_size =
+      (desired_block_size - border_block_spacing * (row_count - 1)) -
+      total_block_size;
+  if (distributable_block_size <= LayoutUnit())
+    return;
+
+  // Step 1: percentage rows grow to their percentage size.
+  if (percent_rows_with_deficit_count > 0) {
+    float ratio = std::min(
+        distributable_block_size.ToFloat() / percentage_block_size_deficit,
+        1.0f);
+    LayoutUnit remaining_deficit =
+        LayoutUnit(ratio * percentage_block_size_deficit);
+    NGTableTypes::Row* last_row;
+    LayoutUnit distributed_block_size;
+    for (NGTableTypes::Row* row = start_row; row != end_row; ++row) {
+      if (!row->percent)
+        continue;
+      last_row = row;
+      LayoutUnit delta = LayoutUnit(RowBlockSizeDeficit(row) * ratio);
+      row->block_size += delta;
+      total_block_size += delta;
+      distributed_block_size += delta;
+      remaining_deficit -= delta;
+    }
+    last_row->block_size += remaining_deficit;
+    distributed_block_size += remaining_deficit;
+    distributable_block_size -= distributed_block_size;
+  }
+  DCHECK_GE(distributable_block_size, LayoutUnit());
+  if (distributable_block_size <= LayoutUnit())
+    return;
+
+  // Step 2: Distribute to rows that have an originating rowspan.
+  if (rows_with_originating_rowspan > 0) {
+    LayoutUnit remaining_deficit = distributable_block_size;
+    NGTableTypes::Row* last_row;
+    for (NGTableTypes::Row* row = start_row; row != end_row; ++row) {
+      if (!IsRowWithOriginatingRowspan(row))
+        continue;
+      last_row = row;
+      LayoutUnit delta =
+          LayoutUnit(distributable_block_size / rows_with_originating_rowspan);
+      row->block_size += delta;
+      remaining_deficit -= delta;
+    }
+    last_row->block_size += remaining_deficit;
+    return;
+  }
+  // Step 3: "unconstrained non-empty rows" grow in proportion to current
+  // block size.
+  if (unconstrained_non_empty_row_count > 0) {
+    LayoutUnit remaining_deficit = distributable_block_size;
+    NGTableTypes::Row* last_row;
+    for (NGTableTypes::Row* row = start_row; row != end_row; ++row) {
+      if (!IsUnconstrainedNonEmptyRow(row))
+        continue;
+      last_row = row;
+      LayoutUnit delta =
+          LayoutUnit(row->block_size * distributable_block_size.ToFloat() /
+                     unconstrained_non_empty_row_block_size);
+      row->block_size += delta;
+      remaining_deficit -= delta;
+    }
+    last_row->block_size += remaining_deficit;
+    return;
+  }
+
+  // Step 4: Empty row distribution
+  // Table distributes evenly between all rows.
+  // If there are any empty rows except start row, last row takes all the
+  // excess block size.
+  if (empty_row_count > 0) {
+    if (desired_block_size_is_rowspan) {
+      NGTableTypes::Row* last_row = nullptr;
+      NGTableTypes::Row* row = start_row;
+      if (empty_row_count != row_count)  // skip initial row.
+        ++row;
+      for (; row != end_row; ++row) {
+        if (row->block_size != LayoutUnit())
+          continue;
+        last_row = row;
+      }
+      if (last_row) {
+        last_row->block_size = distributable_block_size;
+        return;
+      }
+    } else if (empty_row_count == row_count ||
+               (empty_row_count + constrained_non_empty_row_count ==
+                row_count)) {
+      // Grow empty rows if one of these is true:
+      // - all rows are empty.
+      // - non-empty rows are all constrained.
+      // Different browsers disagree on when to grow empty rows.
+      NGTableTypes::Row* last_row;
+      LayoutUnit remaining_deficit = distributable_block_size;
+      for (NGTableTypes::Row* row = start_row; row != end_row; ++row) {
+        if (row->block_size != LayoutUnit())
+          continue;
+        last_row = row;
+        // Table block size distributes equally, while rowspan distributes to
+        // last row.
+        LayoutUnit delta =
+            LayoutUnit(distributable_block_size.ToFloat() / empty_row_count);
+        row->block_size = delta;
+        remaining_deficit -= delta;
+      }
+      last_row->block_size += remaining_deficit;
+      return;
+    }
+  }
+
+  // Step 5: Grow non-empty rows in proportion to current block size.
+  // It grows constrained, and unconstrained rows.
+  NGTableTypes::Row* last_row = nullptr;
+  LayoutUnit remaining_deficit = distributable_block_size;
+  for (NGTableTypes::Row* row = start_row; row != end_row; ++row) {
+    if (row->block_size == LayoutUnit())
+      continue;
+    last_row = row;
+    LayoutUnit delta = LayoutUnit(distributable_block_size *
+                                  row->block_size.ToFloat() / total_block_size);
+    row->block_size += delta;
+    remaining_deficit -= delta;
+  }
+  if (last_row)
+    last_row->block_size += remaining_deficit;
+}
+
 }  // namespace
 
+void NGTableAlgorithmHelpers::ComputeGridInlineMinmax(
+    NGTableTypes::Columns& column_constraints,
+    bool is_fixed_layout,
+    bool containing_block_expects_minmax_without_percentages,
+    LayoutUnit undistributable_space,
+    MinMaxSizes* minmax) {
+  DCHECK_EQ(minmax->min_size, LayoutUnit());
+  DCHECK_EQ(minmax->max_size, LayoutUnit());
+  // https://www.w3.org/TR/css-tables-3/#computing-the-table-width
+  // Compute standard GRID_MIN/GRID_MAX. They are sum of column_constraints.
+  //
+  // Standard does not specify how to handle percentages.
+  // "a percentage represents a constraint on the column's inline size, which a
+  // UA should try to satisfy"
+  // Percentages cannot be resolved into pixels because size of containing
+  // block is unknown. Instead, percentages are used to enforce following
+  // constraints:
+  // 1) Column min inline size and percentage imply that total inline sum must
+  // be large enough to fit the column. Mathematically, column with
+  // min_inline_size of X, and percentage Y% implies that the
+  // total inline sum MINSUM must satisfy: MINSUM * Y% >= X.
+  // 2) Let T% be sum of all percentages. Let M be sum of min_inline_sizes of
+  // all non-percentage columns. Total min size sum MINSUM must satisfy:
+  // T% * MINSUM + M = MINSUM.
+
+  // Minimum total size estimate based on column's min_inline_size and percent.
+  LayoutUnit percent_maxsize_estimate;
+  // Sum of max_inline_sizes of non-percentage columns.
+  LayoutUnit non_percent_maxsize_sum;
+  float percent_sum = 0;
+  for (const NGTableTypes::Column& column : column_constraints) {
+    if (column.min_inline_size) {
+      // In fixed layout, constrained cells minimum inline size is their
+      // maximum.
+      if (is_fixed_layout && column.IsFixed()) {
+        minmax->min_size += *column.max_inline_size;
+      } else {
+        minmax->min_size += *column.min_inline_size;
+      }
+      if (column.percent) {
+        if (*column.max_inline_size > LayoutUnit() && *column.percent > 0) {
+          LayoutUnit estimate =
+              LayoutUnit(100 / *column.percent * *column.max_inline_size);
+          percent_maxsize_estimate =
+              std::max(percent_maxsize_estimate, estimate);
+        }
+      } else {
+        non_percent_maxsize_sum += *column.max_inline_size;
+      }
+    }
+    if (column.max_inline_size)
+      minmax->max_size += *column.max_inline_size;
+    if (column.percent)
+      percent_sum += *column.percent;
+  }
+  DCHECK_LE(percent_sum, 100.0f);
+
+  // Table max inline size constraint can be computed from:
+  // total column percentage combined with max_inline_size of nonpercent
+  // columns.
+  if (percent_sum > 0 && !containing_block_expects_minmax_without_percentages) {
+    LayoutUnit size_from_percent_and_fixed;
+    DCHECK_GE(percent_sum, 0.0f);
+    if (non_percent_maxsize_sum != LayoutUnit()) {
+      if (percent_sum == 100.0f) {
+        size_from_percent_and_fixed = NGTableTypes::kTableMaxInlineSize;
+      } else {
+        size_from_percent_and_fixed =
+            LayoutUnit((100 / (100 - percent_sum)) * non_percent_maxsize_sum);
+      }
+    }
+    minmax->max_size = std::max(minmax->max_size, size_from_percent_and_fixed);
+    minmax->max_size = std::max(minmax->max_size, percent_maxsize_estimate);
+  }
+
+  minmax->max_size = std::max(minmax->min_size, minmax->max_size);
+  *minmax += undistributable_space;
+}
+
 void NGTableAlgorithmHelpers::DistributeColspanCellToColumns(
     const NGTableTypes::ColspanCell& colspan_cell,
     LayoutUnit inline_border_spacing,
@@ -628,4 +912,202 @@
   }
 }
 
+void NGTableAlgorithmHelpers::DistributeRowspanCellToRows(
+    const NGTableTypes::RowspanCell& rowspan_cell,
+    LayoutUnit border_block_spacing,
+    NGTableTypes::Rows* rows) {
+  DCHECK_GE(rowspan_cell.span, 0u);
+  DistributeExcessBlockSizeToRows(
+      rowspan_cell.start_row, rowspan_cell.span,
+      rowspan_cell.cell_block_constraint.min_block_size,
+      /* desired_block_size_is_rowspan */ true, border_block_spacing,
+      kIndefiniteSize, rows);
+}
+
+// Legacy code ignores section block size.
+void NGTableAlgorithmHelpers::DistributeSectionFixedBlockSizeToRows(
+    const wtf_size_t start_row,
+    const wtf_size_t rowspan,
+    LayoutUnit section_fixed_block_size,
+    LayoutUnit border_block_spacing,
+    LayoutUnit percentage_resolution_block_size,
+    NGTableTypes::Rows* rows) {
+  DistributeExcessBlockSizeToRows(start_row, rowspan, section_fixed_block_size,
+                                  /* desired_block_size_is_rowspan */ false,
+                                  border_block_spacing,
+                                  percentage_resolution_block_size, rows);
+}
+
+void NGTableAlgorithmHelpers::DistributeTableBlockSizeToSections(
+    LayoutUnit border_block_spacing,
+    LayoutUnit table_block_size,
+    NGTableTypes::Sections* sections,
+    NGTableTypes::Rows* rows) {
+  if (sections->size() == 0)
+    return;
+  // Redistribute table block size over sections algorithm:
+  // 1. Compute section groups:
+  //   Group 0: sections with 0-block size
+  //   Group 1: sections with %-age block size not in Group 0
+  //   Group 2: unconstrained tbody sections not in Group 0
+  //   Group 3: all tbody sections not in Group 0
+  //   Group 4: all sections not in Group 0
+  //
+  // 2. Percentage redistribution:
+  //   Grow sections in group 1 up to their %ge block size
+  //
+  // 3. Final redistribution
+  //   Pick first non-empty group between groups 4, 3, 2, and 0.
+  //   Grow sections in picked group.
+  //   Groups 4, 3, 2 grow proportiononaly to their block size.
+  //   Group 0 grows evenly.
+  unsigned block_space_count = sections->size() + 1;
+  LayoutUnit undistributable_space = block_space_count * border_block_spacing;
+
+  LayoutUnit distributable_table_block_size =
+      std::max(LayoutUnit(), table_block_size - undistributable_space);
+  bool has_growable_percent_sections = false;
+  LayoutUnit desired_percentage_block_size_deficit;
+  LayoutUnit total_group_block_sizes[5];
+  unsigned number_of_empty_groups = 0;
+
+  auto is_group_0 = [](auto& section) {
+    return section.block_size == LayoutUnit();
+  };
+  auto is_group_1 = [](auto& section) {
+    return section.percent.has_value() && section.percent != 0.0 &&
+           section.block_size != LayoutUnit();
+  };
+  auto is_group_2 = [](auto& section) {
+    return section.is_tbody && !section.is_constrained &&
+           section.block_size != LayoutUnit();
+  };
+  auto is_group_3 = [](auto& section) {
+    return section.is_tbody && section.block_size != LayoutUnit();
+  };
+  auto is_group_4 = [](auto& section) {
+    return section.block_size != LayoutUnit();
+  };
+
+  auto update_block_sizes = [&total_group_block_sizes, &number_of_empty_groups,
+                             &is_group_0, &is_group_2, &is_group_3,
+                             &is_group_4](auto& section) {
+    if (is_group_2(section))
+      total_group_block_sizes[2] += section.block_size;
+    if (is_group_3(section))
+      total_group_block_sizes[3] += section.block_size;
+    if (is_group_4(section))
+      total_group_block_sizes[4] += section.block_size;
+    if (is_group_0(section))
+      number_of_empty_groups++;
+  };
+
+  for (NGTableTypes::Section& section : *sections) {
+    section.needs_redistribution = false;
+    update_block_sizes(section);
+    if (is_group_1(section)) {
+      has_growable_percent_sections = true;
+      desired_percentage_block_size_deficit +=
+          (LayoutUnit(*section.percent * distributable_table_block_size / 100) -
+           section.block_size)
+              .ClampNegativeToZero();
+    }
+  }
+  LayoutUnit excess_block_size =
+      distributable_table_block_size - total_group_block_sizes[4];
+  if (excess_block_size <= LayoutUnit())
+    return;
+
+  // Step 1: Percentage redistribution: grow percentages to their maximum.
+  if (has_growable_percent_sections) {
+    // Because percentages will grow, need to recompute all the totals.
+    total_group_block_sizes[2] = LayoutUnit();
+    total_group_block_sizes[3] = LayoutUnit();
+    total_group_block_sizes[4] = LayoutUnit();
+    number_of_empty_groups = 0;
+    float ratio = std::min(
+        excess_block_size / desired_percentage_block_size_deficit.ToFloat(),
+        1.0f);
+    LayoutUnit remaining_deficit =
+        LayoutUnit(ratio * desired_percentage_block_size_deficit);
+    NGTableTypes::Section* last_section = nullptr;
+    for (NGTableTypes::Section& section : *sections) {
+      if (!is_group_1(section)) {
+        update_block_sizes(section);
+        continue;
+      }
+      LayoutUnit desired_block_size =
+          LayoutUnit(*section.percent * distributable_table_block_size / 100);
+      LayoutUnit block_size_deficit =
+          (desired_block_size - section.block_size).ClampNegativeToZero();
+      LayoutUnit grow_by = LayoutUnit(block_size_deficit * ratio);
+      if (grow_by != LayoutUnit()) {
+        section.block_size += grow_by;
+        section.needs_redistribution = true;
+        last_section = &section;
+        remaining_deficit -= grow_by;
+      }
+      update_block_sizes(section);
+    }
+    if (last_section && remaining_deficit != LayoutUnit()) {
+      last_section->block_size += remaining_deficit;
+      if (is_group_4(*last_section))
+        total_group_block_sizes[4] += remaining_deficit;
+    }
+  }
+  excess_block_size =
+      distributable_table_block_size - total_group_block_sizes[4];
+
+  if (excess_block_size > LayoutUnit()) {
+    // Step 2: distribute remaining block sizes to group 0, 2, 3, or 4.
+    unsigned group_index;
+    if (total_group_block_sizes[2] > LayoutUnit())
+      group_index = 2;
+    else if (total_group_block_sizes[3] > LayoutUnit())
+      group_index = 3;
+    else if (total_group_block_sizes[4] > LayoutUnit())
+      group_index = 4;
+    else
+      group_index = 0;
+
+    LayoutUnit remaining_deficit = excess_block_size;
+    NGTableTypes::Section* last_section;
+    for (NGTableTypes::Section& section : *sections) {
+      if (group_index == 2 && !is_group_2(section))
+        continue;
+      if (group_index == 3 && !is_group_3(section))
+        continue;
+      if (group_index == 4 && !is_group_4(section))
+        continue;
+      LayoutUnit grow_by;
+      if (group_index == 0) {
+        grow_by =
+            LayoutUnit(excess_block_size.ToFloat() / number_of_empty_groups);
+      } else {
+        grow_by = LayoutUnit(section.block_size.ToFloat() * excess_block_size /
+                             total_group_block_sizes[group_index]);
+      }
+      if (grow_by > LayoutUnit()) {
+        section.block_size += grow_by;
+        section.needs_redistribution = true;
+        remaining_deficit -= grow_by;
+        last_section = &section;
+      }
+    }
+    if (last_section && remaining_deficit != LayoutUnit()) {
+      last_section->block_size += remaining_deficit;
+    }
+  }
+
+  // Step 3: Propagate section expansion to rows.
+  for (NGTableTypes::Section& section : *sections) {
+    if (!section.needs_redistribution)
+      continue;
+    DistributeExcessBlockSizeToRows(
+        section.start_row, section.rowspan, section.block_size,
+        /* desired_block_size_is_rowspan */ false, border_block_spacing,
+        section.block_size, rows);
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h
index dd1d9f0..ad9b320 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h
@@ -11,10 +11,10 @@
 namespace blink {
 
 // Table size distribution algorithms.
-class NGTableAlgorithmHelpers {
+class CORE_EXPORT NGTableAlgorithmHelpers {
  public:
-  // Compute maximum number of table columns that can deduced from
-  // single cell and its colspan.
+  // Compute maximum number of table columns that can deduced from single cell
+  // and its colspan.
   static wtf_size_t ComputeMaxColumn(wtf_size_t current_column,
                                      wtf_size_t colspan,
                                      bool is_fixed_table_layout) {
@@ -24,6 +24,19 @@
     return current_column + 1;
   }
 
+  // Flex/grid containing blocks need Table minmax size to be computed without
+  // using percentages.
+  // |containing_block_expects_minmax_without_percentages| is used to do
+  // this.
+  // |undistributable_space| is size of space not occupied by cells
+  // (borders, border spacing).
+  static void ComputeGridInlineMinmax(
+      NGTableTypes::Columns& column_constraints,
+      bool is_fixed_layout,
+      bool containing_block_expects_minmax_without_percentages,
+      LayoutUnit undistributable_space,
+      MinMaxSizes* minmax_sum);
+
   static void DistributeColspanCellToColumns(
       const NGTableTypes::ColspanCell& colspan_cell,
       LayoutUnit inline_border_spacing,
@@ -35,6 +48,25 @@
       LayoutUnit inline_border_spacing,
       bool is_fixed_layout,
       NGTableTypes::Columns* column_constraints);
+
+  static void DistributeRowspanCellToRows(
+      const NGTableTypes::RowspanCell& rowspan_cell,
+      LayoutUnit border_block_spacing,
+      NGTableTypes::Rows* rows);
+
+  static void DistributeSectionFixedBlockSizeToRows(
+      const wtf_size_t start_row,
+      const wtf_size_t end_row,
+      LayoutUnit section_fixed_block_size,
+      LayoutUnit border_block_spacing,
+      LayoutUnit percentage_resolution_block_size,
+      NGTableTypes::Rows* rows);
+
+  static void DistributeTableBlockSizeToSections(
+      LayoutUnit border_block_spacing,
+      LayoutUnit table_block_size,
+      NGTableTypes::Sections* sections,
+      NGTableTypes::Rows* rows);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
new file mode 100644
index 0000000..a252b2e
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
@@ -0,0 +1,298 @@
+// 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 "third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class NGTableAlgorithmHelpersTest : public testing::Test {
+  void SetUp() override {}
+
+ public:
+  NGTableTypes::Column MakeColumn(int min_width,
+                                  int max_width,
+                                  base::Optional<float> percent = base::nullopt,
+                                  bool is_constrained = false) {
+    return NGTableTypes::Column{LayoutUnit(min_width), LayoutUnit(max_width),
+                                percent, is_constrained};
+  }
+
+  NGTableTypes::Row MakeRow(int block_size,
+                            bool is_constrained = false,
+                            bool has_rowspan_start = false,
+                            base::Optional<float> percent = base::nullopt) {
+    return NGTableTypes::Row{
+        LayoutUnit(block_size), LayoutUnit(), percent,           0,    0,
+        is_constrained,         false,        has_rowspan_start, false};
+  }
+
+  NGTableTypes::Section MakeSection(
+      NGTableTypes::Rows* rows,
+      int block_size,
+      wtf_size_t rowspan = 1,
+      base::Optional<float> percent = base::nullopt) {
+    wtf_size_t start_row = rows->size();
+    for (wtf_size_t i = 0; i < rowspan; i++)
+      rows->push_back(MakeRow(10));
+    bool is_constrained = percent || block_size != 0;
+    bool is_tbody = true;
+    return NGTableTypes::Section{
+        start_row, rowspan, LayoutUnit(block_size), percent, is_constrained,
+        is_tbody,  false};
+  }
+};
+
+TEST_F(NGTableAlgorithmHelpersTest, DistributeColspanAutoPercent) {
+  NGTableTypes::ColspanCell colspan_cell(NGTableTypes::CellInlineConstraint(),
+                                         0, 3);
+  colspan_cell.start_column = 0;
+  colspan_cell.span = 3;
+
+  NGTableTypes::Columns column_constraints;
+
+  colspan_cell.cell_inline_constraint.percent = 60.0f;
+
+  // Distribute over non-percent columns proportial to max size.
+  // Columns: 10px, 20px, 30%
+  // Distribute 60%: 10% 20% 30%
+  column_constraints.Shrink(0);
+  column_constraints.push_back(MakeColumn(0, 10));
+  column_constraints.push_back(MakeColumn(0, 20));
+  column_constraints.push_back(MakeColumn(0, 10, 30));
+  NGTableAlgorithmHelpers::DistributeColspanCellToColumns(
+      colspan_cell, LayoutUnit(), false, &column_constraints);
+  EXPECT_EQ(*column_constraints[0].percent, 10);
+  EXPECT_EQ(*column_constraints[1].percent, 20);
+
+  // Distribute evenly over empty columns.
+  // Columns: 0px 0px 10%
+  // Distribute 60%: 25% 25% 10%
+  column_constraints.Shrink(0);
+  column_constraints.push_back(MakeColumn(0, 0));
+  column_constraints.push_back(MakeColumn(0, 0));
+  column_constraints.push_back(MakeColumn(0, 10, 10));
+  NGTableAlgorithmHelpers::DistributeColspanCellToColumns(
+      colspan_cell, LayoutUnit(), false, &column_constraints);
+  EXPECT_EQ(*column_constraints[0].percent, 25);
+  EXPECT_EQ(*column_constraints[1].percent, 25);
+}
+
+TEST_F(NGTableAlgorithmHelpersTest, DistributeColspanAutoSizeUnconstrained) {
+  NGTableTypes::ColspanCell colspan_cell(NGTableTypes::CellInlineConstraint(),
+                                         0, 3);
+  colspan_cell.start_column = 0;
+  colspan_cell.span = 3;
+
+  NGTableTypes::Columns column_constraints;
+
+  // Columns distributing over auto columns.
+  colspan_cell.cell_inline_constraint.min_inline_size = LayoutUnit(100);
+  colspan_cell.cell_inline_constraint.max_inline_size = LayoutUnit(100);
+  // Distribute over non-percent columns proportial to max size.
+  // Columns min/max: 0/10, 0/10, 0/20
+  // Distribute 25, 25, 50
+  column_constraints.Shrink(0);
+  column_constraints.push_back(MakeColumn(0, 10));
+  column_constraints.push_back(MakeColumn(0, 10));
+  column_constraints.push_back(MakeColumn(0, 20));
+  NGTableAlgorithmHelpers::DistributeColspanCellToColumns(
+      colspan_cell, LayoutUnit(), false, &column_constraints);
+  EXPECT_EQ(*column_constraints[0].min_inline_size, 25);
+  EXPECT_EQ(*column_constraints[1].min_inline_size, 25);
+  EXPECT_EQ(*column_constraints[2].min_inline_size, 50);
+}
+
+TEST_F(NGTableAlgorithmHelpersTest, DistributeColspanAutoSizeConstrained) {
+  NGTableTypes::ColspanCell colspan_cell(NGTableTypes::CellInlineConstraint(),
+                                         0, 3);
+  colspan_cell.start_column = 0;
+  colspan_cell.span = 3;
+
+  NGTableTypes::Columns column_constraints;
+
+  // Columns distributing over auto columns.
+  colspan_cell.cell_inline_constraint.min_inline_size = LayoutUnit(100);
+  colspan_cell.cell_inline_constraint.max_inline_size = LayoutUnit(100);
+  // Distribute over fixed columns proportial to:
+  // Columns min/max: 0/10, 0/10, 0/20
+  // Distribute 25, 25, 50
+  column_constraints.Shrink(0);
+  column_constraints.push_back(MakeColumn(0, 10, base::nullopt, true));
+  column_constraints.push_back(MakeColumn(10, 10, base::nullopt, true));
+  column_constraints.push_back(MakeColumn(0, 20, base::nullopt, true));
+  NGTableAlgorithmHelpers::DistributeColspanCellToColumns(
+      colspan_cell, LayoutUnit(), false, &column_constraints);
+  EXPECT_EQ(*column_constraints[0].min_inline_size, 25);
+  EXPECT_EQ(*column_constraints[1].min_inline_size, 25);
+  EXPECT_EQ(*column_constraints[2].min_inline_size, 50);
+}
+
+TEST_F(NGTableAlgorithmHelpersTest, ComputeGridInlineMinmax) {
+  NGTableTypes::Columns column_constraints;
+
+  LayoutUnit undistributable_space;
+  bool is_fixed_layout = false;
+  bool containing_block_expects_minmax_without_percentages = false;
+
+  // No percentages, just sums up min/max.
+  column_constraints.push_back(MakeColumn(10, 100));
+  column_constraints.push_back(MakeColumn(20, 200));
+  column_constraints.push_back(MakeColumn(30, 300));
+
+  MinMaxSizes minmax;
+  NGTableAlgorithmHelpers::ComputeGridInlineMinmax(
+      column_constraints, is_fixed_layout,
+      containing_block_expects_minmax_without_percentages,
+      undistributable_space, &minmax);
+  EXPECT_EQ(minmax.min_size, LayoutUnit(60));
+  EXPECT_EQ(minmax.max_size, LayoutUnit(600));
+
+  // Percentage: 99px max size/10% cell =>
+  // table max size of 100%/10% * 99px
+  minmax = MinMaxSizes();
+  column_constraints.Shrink(0);
+  column_constraints.push_back(MakeColumn(10, 99, 10));
+  column_constraints.push_back(MakeColumn(10, 10));
+  column_constraints.push_back(MakeColumn(10, 10));
+  NGTableAlgorithmHelpers::ComputeGridInlineMinmax(
+      column_constraints, is_fixed_layout,
+      containing_block_expects_minmax_without_percentages,
+      undistributable_space, &minmax);
+  EXPECT_EQ(minmax.min_size, LayoutUnit(30));
+  EXPECT_EQ(minmax.max_size, LayoutUnit(990));
+
+  // Without percent, minmax ignores percent
+  minmax = MinMaxSizes();
+  containing_block_expects_minmax_without_percentages = true;
+  NGTableAlgorithmHelpers::ComputeGridInlineMinmax(
+      column_constraints, is_fixed_layout,
+      containing_block_expects_minmax_without_percentages,
+      undistributable_space, &minmax);
+  EXPECT_EQ(minmax.min_size, LayoutUnit(30));
+  EXPECT_EQ(minmax.max_size, LayoutUnit(119));
+
+  // Percentage: total percentage of 20%, and non-percent width of 800 =>
+  // table max size of 800 + (20% * 800/80%) = 1000
+  minmax = MinMaxSizes();
+  containing_block_expects_minmax_without_percentages = false;
+  column_constraints.Shrink(0);
+  column_constraints.push_back(MakeColumn(10, 100, 10));
+  column_constraints.push_back(MakeColumn(10, 10, 10));
+  column_constraints.push_back(MakeColumn(10, 800));
+  NGTableAlgorithmHelpers::ComputeGridInlineMinmax(
+      column_constraints, is_fixed_layout,
+      containing_block_expects_minmax_without_percentages,
+      undistributable_space, &minmax);
+  EXPECT_EQ(minmax.min_size, LayoutUnit(30));
+  EXPECT_EQ(minmax.max_size, LayoutUnit(1000));
+}
+
+TEST_F(NGTableAlgorithmHelpersTest, DistributeRowspanCellToRows) {
+  NGTableTypes::CellBlockConstraint cell_block_constraint{
+      LayoutUnit(300),      LayoutUnit(), NGBoxStrut(), 0, 0, 3,
+      EVerticalAlign::kTop, true};
+  NGTableTypes::RowspanCell rowspan_cell = NGTableTypes::CreateRowspanCell(
+      0, 3, &cell_block_constraint, base::nullopt);
+  NGTableTypes::Rows rows;
+
+  // Distribute to regular rows, rows grow in proportion to size.
+  rows.push_back(MakeRow(10));
+  rows.push_back(MakeRow(20));
+  rows.push_back(MakeRow(30));
+  NGTableAlgorithmHelpers::DistributeRowspanCellToRows(rowspan_cell,
+                                                       LayoutUnit(), &rows);
+  EXPECT_EQ(rows[0].block_size, LayoutUnit(50));
+  EXPECT_EQ(rows[1].block_size, LayoutUnit(100));
+  EXPECT_EQ(rows[2].block_size, LayoutUnit(150));
+
+  // If some rows are empty, non-empty row gets everything
+  rows.Shrink(0);
+  rows.push_back(MakeRow(0));
+  rows.push_back(MakeRow(10));
+  rows.push_back(MakeRow(0));
+  NGTableAlgorithmHelpers::DistributeRowspanCellToRows(rowspan_cell,
+                                                       LayoutUnit(), &rows);
+  EXPECT_EQ(rows[0].block_size, LayoutUnit(0));
+  EXPECT_EQ(rows[1].block_size, LayoutUnit(300));
+  EXPECT_EQ(rows[2].block_size, LayoutUnit(0));
+
+  // If all rows are empty,last row gets everything.
+  rows.Shrink(0);
+  rows.push_back(MakeRow(0));
+  rows.push_back(MakeRow(0));
+  rows.push_back(MakeRow(0));
+  NGTableAlgorithmHelpers::DistributeRowspanCellToRows(rowspan_cell,
+                                                       LayoutUnit(), &rows);
+  EXPECT_EQ(rows[0].block_size, LayoutUnit(0));
+  EXPECT_EQ(rows[1].block_size, LayoutUnit(0));
+  EXPECT_EQ(rows[2].block_size, LayoutUnit(300));
+}
+
+TEST_F(NGTableAlgorithmHelpersTest, DistributeSectionFixedBlockSizeToRows) {
+  NGTableTypes::Rows rows;
+
+  // Percentage rows get percentage, rest is distributed evenly.
+  rows.push_back(MakeRow(100));
+  rows.push_back(MakeRow(100, true, false, 50));
+  rows.push_back(MakeRow(100));
+  NGTableAlgorithmHelpers::DistributeSectionFixedBlockSizeToRows(
+      0, 3, LayoutUnit(1000), LayoutUnit(), LayoutUnit(1000), &rows);
+  EXPECT_EQ(rows[0].block_size, LayoutUnit(250));
+  EXPECT_EQ(rows[1].block_size, LayoutUnit(500));
+  EXPECT_EQ(rows[2].block_size, LayoutUnit(250));
+}
+
+TEST_F(NGTableAlgorithmHelpersTest, DistributeTableBlockSizeToSections) {
+  NGTableTypes::Sections sections;
+  NGTableTypes::Rows rows;
+
+  // Empty sections only grow if there are no other growable sections.
+  sections.push_back(MakeSection(&rows, 0));
+  sections.push_back(MakeSection(&rows, 100));
+  NGTableAlgorithmHelpers::DistributeTableBlockSizeToSections(
+      LayoutUnit(), LayoutUnit(500), &sections, &rows);
+  EXPECT_EQ(sections[0].block_size, LayoutUnit(0));
+
+  // Sections with % block size grow to percentage.
+  sections.Shrink(0);
+  rows.Shrink(0);
+  sections.push_back(MakeSection(&rows, 100, 1, 90));
+  sections.push_back(MakeSection(&rows, 100));
+  NGTableAlgorithmHelpers::DistributeTableBlockSizeToSections(
+      LayoutUnit(), LayoutUnit(1000), &sections, &rows);
+  EXPECT_EQ(sections[0].block_size, LayoutUnit(900));
+  EXPECT_EQ(sections[1].block_size, LayoutUnit(100));
+
+  // When table height is greater than sum of intrinsic heights,
+  // intrinsic heights are computed, and then they grow in
+  // proportion to intrinsic height.
+  sections.Shrink(0);
+  rows.Shrink(0);
+  sections.push_back(MakeSection(&rows, 100, 1, 30));
+  sections.push_back(MakeSection(&rows, 100));
+  // 30% section grows to 300px.
+  // Extra 600 px is distributed between 300 and 100 px proportionally.
+  // TODO(atotic) Is this what we want? FF/Edge/Legacy all disagree.
+  NGTableAlgorithmHelpers::DistributeTableBlockSizeToSections(
+      LayoutUnit(), LayoutUnit(1000), &sections, &rows);
+  EXPECT_EQ(sections[0].block_size, LayoutUnit(750));
+  EXPECT_EQ(sections[1].block_size, LayoutUnit(250));
+
+  // If there is a constrained section, and an unconstrained section,
+  // unconstrained section grows.
+  sections.Shrink(0);
+  rows.Shrink(0);
+  NGTableTypes::Section section(MakeSection(&rows, 100));
+  section.is_constrained = false;
+  sections.push_back(section);
+  sections.push_back(MakeSection(&rows, 100));
+  NGTableAlgorithmHelpers::DistributeTableBlockSizeToSections(
+      LayoutUnit(), LayoutUnit(1000), &sections, &rows);
+  EXPECT_EQ(sections[0].block_size, LayoutUnit(900));
+  EXPECT_EQ(sections[1].block_size, LayoutUnit(100));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
index 43df69d..090c199 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
@@ -65,7 +65,6 @@
 // "outer min-content and outer max-content widths for colgroups"
 NGTableTypes::Column NGTableTypes::CreateColumn(
     const ComputedStyle& style,
-    bool is_fixed_layout,
     base::Optional<LayoutUnit> default_inline_size) {
   base::Optional<LayoutUnit> inline_size;
   base::Optional<LayoutUnit> min_inline_size;
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
index 715a7337..ec8c7a5 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
@@ -19,7 +19,7 @@
 class NGLayoutInputNode;
 
 // Define constraint classes for NGTableLayoutAlgorithm.
-class NGTableTypes {
+class CORE_EXPORT NGTableTypes {
  public:
   static constexpr LayoutUnit kTableMaxInlineSize = LayoutUnit::Max();
 
@@ -182,7 +182,6 @@
   };
 
   static Column CreateColumn(const ComputedStyle&,
-                             bool is_fixed_layout,
                              base::Optional<LayoutUnit> default_inline_size);
 
   static CellInlineConstraint CreateCellInlineConstraint(
diff --git a/third_party/blink/renderer/core/loader/ping_loader.cc b/third_party/blink/renderer/core/loader/ping_loader.cc
index 1babd229..0238077 100644
--- a/third_party/blink/renderer/core/loader/ping_loader.cc
+++ b/third_party/blink/renderer/core/loader/ping_loader.cc
@@ -187,7 +187,7 @@
                       const KURL& url,
                       const Beacon& beacon) {
   if (!frame->DomWindow()
-           ->GetContentSecurityPolicyForWorld()
+           ->GetContentSecurityPolicyForCurrentWorld()
            ->AllowConnectToSource(url, url, RedirectStatus::kNoRedirect)) {
     // We're simulating a network failure here, so we return 'true'.
     return true;
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context_test.cc b/third_party/blink/renderer/core/origin_trials/origin_trial_context_test.cc
index 5536f33..66c6b68 100644
--- a/third_party/blink/renderer/core/origin_trials/origin_trial_context_test.cc
+++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context_test.cc
@@ -428,8 +428,8 @@
 
   PolicyParserMessageBuffer logger;
   ParsedFeaturePolicy result;
-  result = FeaturePolicyParser::Parse("frobulate", security_origin, nullptr,
-                                      logger, feature_map, window);
+  result = FeaturePolicyParser::ParseFeaturePolicyForTest(
+      "frobulate", security_origin, nullptr, logger, feature_map, window);
   EXPECT_TRUE(logger.GetMessages().IsEmpty());
   ASSERT_EQ(1u, result.size());
   EXPECT_EQ(mojom::blink::FeaturePolicyFeature::kFrobulate, result[0].feature);
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc
index bfd2c61..d8235b0 100644
--- a/third_party/blink/renderer/core/page/create_window.cc
+++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -243,12 +243,12 @@
 
   const KURL& url = request.GetResourceRequest().Url();
   if (url.ProtocolIsJavaScript() &&
-      opener_frame.DomWindow()->GetContentSecurityPolicyForWorld()) {
+      opener_frame.DomWindow()->GetContentSecurityPolicyForCurrentWorld()) {
     String script_source = DecodeURLEscapeSequences(
         url.GetString(), DecodeURLMode::kUTF8OrIsomorphic);
 
     if (!opener_frame.DomWindow()
-             ->GetContentSecurityPolicyForWorld()
+             ->GetContentSecurityPolicyForCurrentWorld()
              ->AllowInline(
                  ContentSecurityPolicy::InlineType::kNavigation,
                  nullptr /* element */, script_source, String() /* nonce */,
diff --git a/third_party/blink/renderer/core/svg/svg_script_element.cc b/third_party/blink/renderer/core/svg/svg_script_element.cc
index ecebe50b..f8a9c1e22 100644
--- a/third_party/blink/renderer/core/svg/svg_script_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_script_element.cc
@@ -137,9 +137,10 @@
     const AtomicString& nonce,
     const WTF::OrdinalNumber& context_line,
     const String& script_content) {
-  return GetExecutionContext()->GetContentSecurityPolicyForWorld()->AllowInline(
-      ContentSecurityPolicy::InlineType::kScript, this, script_content, nonce,
-      GetDocument().Url(), context_line);
+  return GetExecutionContext()
+      ->GetContentSecurityPolicyForCurrentWorld()
+      ->AllowInline(ContentSecurityPolicy::InlineType::kScript, this,
+                    script_content, nonce, GetDocument().Url(), context_line);
 }
 
 Document& SVGScriptElement::GetDocument() const {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_selection.cc b/third_party/blink/renderer/modules/accessibility/ax_selection.cc
index 78b8325..877ddab 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_selection.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_selection.cc
@@ -144,16 +144,26 @@
   const AXObject* ax_text_control =
       ax_object_cache_impl->GetOrCreate(&text_control);
   DCHECK(ax_text_control);
+
+  // We can't directly use "text_control.Selection()" because the selection it
+  // returns is inside the shadow DOM and it's not anchored to the text field
+  // itself.
   const TextAffinity extent_affinity = text_control.Selection().Affinity();
   const TextAffinity base_affinity =
       text_control.selectionStart() == text_control.selectionEnd()
           ? extent_affinity
           : TextAffinity::kDownstream;
+
+  const bool is_backward = (text_control.selectionDirection() == "backward");
   const auto ax_base = AXPosition::CreatePositionInTextObject(
-      *ax_text_control, static_cast<int>(text_control.selectionStart()),
+      *ax_text_control,
+      (is_backward ? int{text_control.selectionEnd()}
+                   : int{text_control.selectionStart()}),
       base_affinity);
   const auto ax_extent = AXPosition::CreatePositionInTextObject(
-      *ax_text_control, static_cast<int>(text_control.selectionEnd()),
+      *ax_text_control,
+      (is_backward ? int{text_control.selectionStart()}
+                   : int{text_control.selectionEnd()}),
       extent_affinity);
 
   if (!ax_base.IsValid() || !ax_extent.IsValid())
diff --git a/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc b/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc
index 0970f700..a155a8b 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc
@@ -1059,6 +1059,12 @@
   EXPECT_EQ(0u, ToTextControl(*input).selectionStart());
   EXPECT_EQ(18u, ToTextControl(*input).selectionEnd());
   EXPECT_EQ("forward", ToTextControl(*input).selectionDirection());
+
+  // Ensure that the selection that was just set could be successfully
+  // retrieved.
+  const auto ax_current_selection =
+      AXSelection::FromCurrentSelection(ToTextControl(*input));
+  EXPECT_EQ(ax_selection, ax_current_selection);
 }
 
 TEST_F(AccessibilitySelectionTest, BackwardSelectionInTextField) {
@@ -1088,6 +1094,12 @@
   EXPECT_EQ(3u, ToTextControl(*input).selectionStart());
   EXPECT_EQ(10u, ToTextControl(*input).selectionEnd());
   EXPECT_EQ("backward", ToTextControl(*input).selectionDirection());
+
+  // Ensure that the selection that was just set could be successfully
+  // retrieved.
+  const auto ax_current_selection =
+      AXSelection::FromCurrentSelection(ToTextControl(*input));
+  EXPECT_EQ(ax_selection, ax_current_selection);
 }
 
 TEST_F(AccessibilitySelectionTest, SelectingTheWholeOfTheTextField) {
@@ -1358,6 +1370,12 @@
   EXPECT_EQ(0u, ToTextControl(*textarea).selectionStart());
   EXPECT_EQ(53u, ToTextControl(*textarea).selectionEnd());
   EXPECT_EQ("forward", ToTextControl(*textarea).selectionDirection());
+
+  // Ensure that the selection that was just set could be successfully
+  // retrieved.
+  const auto ax_current_selection =
+      AXSelection::FromCurrentSelection(ToTextControl(*textarea));
+  EXPECT_EQ(ax_selection, ax_current_selection);
 }
 
 TEST_F(AccessibilitySelectionTest, BackwardSelectionInTextarea) {
@@ -1391,6 +1409,12 @@
   EXPECT_EQ(3u, ToTextControl(*textarea).selectionStart());
   EXPECT_EQ(10u, ToTextControl(*textarea).selectionEnd());
   EXPECT_EQ("backward", ToTextControl(*textarea).selectionDirection());
+
+  // Ensure that the selection that was just set could be successfully
+  // retrieved.
+  const auto ax_current_selection =
+      AXSelection::FromCurrentSelection(ToTextControl(*textarea));
+  EXPECT_EQ(ax_selection, ax_current_selection);
 }
 
 TEST_F(AccessibilitySelectionTest, SelectTheWholeOfTheTextarea) {
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc
index 4166fe0a..df695668 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc
@@ -67,7 +67,7 @@
 // called synchronously from the background fetch call.
 bool ShouldBlockDueToCSP(ExecutionContext* execution_context,
                          const KURL& request_url) {
-  return !execution_context->GetContentSecurityPolicyForWorld()
+  return !execution_context->GetContentSecurityPolicyForCurrentWorld()
               ->AllowConnectToSource(request_url, request_url,
                                      RedirectStatus::kNoRedirect);
 }
diff --git a/third_party/blink/renderer/modules/websockets/websocket_common.cc b/third_party/blink/renderer/modules/websockets/websocket_common.cc
index 4cd6d80..3a255de 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_common.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_common.cc
@@ -87,7 +87,7 @@
     return ConnectResult::kException;
   }
 
-  if (!execution_context->GetContentSecurityPolicyForWorld()
+  if (!execution_context->GetContentSecurityPolicyForCurrentWorld()
            ->AllowConnectToSource(url_, url_, RedirectStatus::kNoRedirect)) {
     state_ = kClosed;
 
diff --git a/third_party/blink/renderer/modules/webtransport/quic_transport.cc b/third_party/blink/renderer/modules/webtransport/quic_transport.cc
index 4a8645d..eec17682 100644
--- a/third_party/blink/renderer/modules/webtransport/quic_transport.cc
+++ b/third_party/blink/renderer/modules/webtransport/quic_transport.cc
@@ -636,7 +636,7 @@
 
   auto* execution_context = GetExecutionContext();
 
-  if (!execution_context->GetContentSecurityPolicyForWorld()
+  if (!execution_context->GetContentSecurityPolicyForCurrentWorld()
            ->AllowConnectToSource(url_, url_, RedirectStatus::kNoRedirect)) {
     // TODO(ricea): This error should probably be asynchronous like it is for
     // WebSockets and fetch.
diff --git a/third_party/blink/renderer/modules/webtransport/quic_transport_test.cc b/third_party/blink/renderer/modules/webtransport/quic_transport_test.cc
index 54dd6515..1342ef6d 100644
--- a/third_party/blink/renderer/modules/webtransport/quic_transport_test.cc
+++ b/third_party/blink/renderer/modules/webtransport/quic_transport_test.cc
@@ -372,7 +372,7 @@
   V8TestingScope scope;
   auto& exception_state = scope.GetExceptionState();
   scope.GetExecutionContext()
-      ->GetContentSecurityPolicyForWorld()
+      ->GetContentSecurityPolicyForCurrentWorld()
       ->DidReceiveHeader("connect-src 'none'",
                          network::mojom::ContentSecurityPolicyType::kEnforce,
                          network::mojom::ContentSecurityPolicySource::kHTTP);
@@ -393,7 +393,7 @@
   // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src.
   auto& exception_state = scope.GetExceptionState();
   scope.GetExecutionContext()
-      ->GetContentSecurityPolicyForWorld()
+      ->GetContentSecurityPolicyForCurrentWorld()
       ->DidReceiveHeader("connect-src quic-transport://example.com",
                          network::mojom::ContentSecurityPolicyType::kEnforce,
                          network::mojom::ContentSecurityPolicySource::kHTTP);
diff --git a/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc b/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc
index baf18a18..b83012ba 100644
--- a/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc
+++ b/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc
@@ -190,11 +190,13 @@
 
     // We first obtain the list of glyphs mapped from PUA codepoint range:
     // https://unicode.org/charts/PDF/UE000.pdf
-    const SkUnichar pua_start = 0xE000;
-    const SkUnichar pua_end = 0xF900;
-    Vector<SkUnichar> pua_codepoints(pua_end - pua_start);
-    for (wtf_size_t i = 0; i < pua_codepoints.size(); ++i)
-      pua_codepoints[i] = pua_start + i;
+    // Note: The two supplementary PUA here are too long but not used much by
+    // icon fonts, so we don't include them in this heuristic.
+    wtf_size_t pua_length =
+        kPrivateUseLastCharacter - kPrivateUseFirstCharacter + 1;
+    Vector<SkUnichar> pua_codepoints(pua_length);
+    for (wtf_size_t i = 0; i < pua_length; ++i)
+      pua_codepoints[i] = kPrivateUseFirstCharacter + i;
 
     Vector<SkGlyphID> glyphs(pua_codepoints.size());
     base_typeface_->unicharsToGlyphs(pua_codepoints.data(),
@@ -206,8 +208,8 @@
     if (!glyphs[0])
       glyphs.EraseAt(0);
 
-    // We use the heuristic that if most of the define glyphs are in PUA, then
-    // the font may be an icon font.
+    // We use the heuristic that if more than half of the define glyphs are in
+    // PUA, then the font may be an icon font.
     wtf_size_t pua_glyph_count = glyphs.size();
     wtf_size_t total_glyphs = base_typeface_->countGlyphs();
     may_be_icon_font_ = pua_glyph_count * 2 > total_glyphs;
diff --git a/third_party/blink/renderer/platform/fonts/font_selection_types.h b/third_party/blink/renderer/platform/fonts/font_selection_types.h
index 3424097..06d293f0 100644
--- a/third_party/blink/renderer/platform/fonts/font_selection_types.h
+++ b/third_party/blink/renderer/platform/fonts/font_selection_types.h
@@ -48,13 +48,16 @@
   FontSelectionValue() = default;
 
   // Explicit because it is lossy.
-  explicit FontSelectionValue(int x) : backing_(x * fractionalEntropy) {}
+  explicit FontSelectionValue(int x)
+      : backing_(clampTo<int16_t>(x * fractionalEntropy)) {}
 
   // Explicit because it is lossy.
-  explicit FontSelectionValue(float x) : backing_(x * fractionalEntropy) {}
+  explicit FontSelectionValue(float x)
+      : backing_(clampTo<int16_t>(x * fractionalEntropy)) {}
 
   // Explicit because it is lossy.
-  explicit FontSelectionValue(double x) : backing_(x * fractionalEntropy) {}
+  explicit FontSelectionValue(double x)
+      : backing_(clampTo<int16_t>(x * fractionalEntropy)) {}
 
   operator float() const {
     // floats have 23 fractional bits, but only 14 fractional bits are
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
index d9f9842..addda81 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
@@ -962,9 +962,13 @@
       fps * (cc::VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit + 1);
   WTF::HashMap<uint32_t, viz::FrameTimingDetails> timing_details;
 
-  MakeSubmitter(
-      base::BindLambdaForTesting([&](int frames, base::TimeDelta duration,
-                                     double roughness) { reports++; }));
+  MakeSubmitter(base::BindLambdaForTesting(
+      [&](int frames, base::TimeDelta duration, double roughness, int hz,
+          gfx::Size frame_size) {
+        ASSERT_EQ(frame_size.width(), 8);
+        ASSERT_EQ(frame_size.height(), 8);
+        reports++;
+      }));
   EXPECT_CALL(*sink_, SetNeedsBeginFrame(true));
   submitter_->StartRendering();
   task_environment_.RunUntilIdle();
diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
index 16bba930..72316a8b 100644
--- a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
@@ -450,23 +450,13 @@
   if (decode_to_half_float_)
     buffer.SetPixelFormat(ImageFrame::PixelFormat::kRGBA_F16);
 
+  // For AVIFs, the frame always fills the entire image.
   buffer.SetOriginalFrameRect(IntRect(IntPoint(), Size()));
 
   avifImageTiming timing;
   auto ret = avifDecoderNthImageTiming(decoder_.get(), index, &timing);
   DCHECK_EQ(ret, AVIF_RESULT_OK);
   buffer.SetDuration(base::TimeDelta::FromSecondsD(timing.duration));
-
-  // The AVIF file format does not contain information equivalent to the
-  // disposal method or alpha blend source. Since the AVIF decoder handles frame
-  // dependence internally, set options that best correspond to "each frame is
-  // independent".
-  buffer.SetDisposalMethod(ImageFrame::kDisposeNotSpecified);
-  buffer.SetAlphaBlendSource(ImageFrame::kBlendAtopBgcolor);
-
-  // Leave all frames as being independent (the default) because we require all
-  // frames be the same size.
-  DCHECK_EQ(buffer.RequiredPreviousFrameIndex(), kNotFound);
 }
 
 void AVIFImageDecoder::Decode(size_t index) {
diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder.h b/third_party/blink/renderer/platform/image-decoders/image_decoder.h
index 130462c5..be709479 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_decoder.h
+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder.h
@@ -444,8 +444,8 @@
   // call.
   bool InitFrameBuffer(size_t);
 
-  // Performs any additional setup of the requested frame after it has been
-  // initially created, e.g. setting a duration or disposal method.
+  // Performs any decoder-specific setup of the requested frame after it has
+  // been newly created, e.g. setting the frame's duration or disposal method.
   virtual void InitializeNewFrame(size_t) {}
 
   // Decodes the requested frame.
diff --git a/third_party/blink/renderer/platform/widget/widget_base_client.h b/third_party/blink/renderer/platform/widget/widget_base_client.h
index 03d353c..530360b 100644
--- a/third_party/blink/renderer/platform/widget/widget_base_client.h
+++ b/third_party/blink/renderer/platform/widget/widget_base_client.h
@@ -148,7 +148,7 @@
   virtual void MouseCaptureLost() {}
 
   // The FrameWidget interface if this is a FrameWidget.
-  virtual FrameWidget* FrameWidget() { return nullptr; }
+  virtual blink::FrameWidget* FrameWidget() { return nullptr; }
 
   // Called to inform the Widget that it has gained or lost keyboard focus.
   virtual void FocusChanged(bool) = 0;
diff --git a/third_party/blink/renderer/platform/wtf/text/character_names.h b/third_party/blink/renderer/platform/wtf/text/character_names.h
index c940700d..d1a7a2f7 100644
--- a/third_party/blink/renderer/platform/wtf/text/character_names.h
+++ b/third_party/blink/renderer/platform/wtf/text/character_names.h
@@ -172,6 +172,8 @@
 const UChar kZeroWidthNonJoinerCharacter = 0x200C;
 const UChar kZeroWidthSpaceCharacter = 0x200B;
 const UChar kZeroWidthNoBreakSpaceCharacter = 0xFEFF;
+const UChar kPrivateUseFirstCharacter = 0xE000;
+const UChar kPrivateUseLastCharacter = 0xF8FF;
 const UChar32 kMaxCodepoint = 0x10ffff;
 
 }  // namespace unicode
@@ -270,6 +272,8 @@
 using WTF::unicode::kPartialDifferential;
 using WTF::unicode::kPopDirectionalFormattingCharacter;
 using WTF::unicode::kPopDirectionalIsolateCharacter;
+using WTF::unicode::kPrivateUseFirstCharacter;
+using WTF::unicode::kPrivateUseLastCharacter;
 using WTF::unicode::kRainbowCharacter;
 using WTF::unicode::kReplacementCharacter;
 using WTF::unicode::kReverseSolidusCharacter;
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index f4fc77f5..b6497666 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -1234,8 +1234,8 @@
             # the subsequent repetitions of the test do not overwrite older
             # trace files.
             current_time = time.strftime("%Y-%m-%d-%H-%M-%S")
-            file_name = 'trace_layout_test_' + test_name.replace(
-                '/', '_').replace('.', '_') + '_' + current_time + '.json'
+            file_name = 'trace_layout_test_{}_{}.json'.format(
+                self._filesystem.sanitize_filename(test_name), current_time)
             args.append('--trace-startup-file=' + file_name)
         return args
 
@@ -1894,25 +1894,67 @@
                                                    suite_prefixes))
         return tests
 
-    def _all_virtual_tests_for_suite(self, suite):
+    def _get_bases_for_suite_with_paths(self, suite, paths):
+        """Returns a set of bases of the virutual suite that are referenced by
+        paths. E.g. given a virtual test suite `foo` with the following bases:
+          bar/baz
+          bar/quu
+          qux
+        and given paths of [virtual/foo/bar], this method would return
+          [bar/baz, bar/quu]
+
+        Given paths of [virtual/foo/bar/baz/test.html], the return would be
+        [bar/baz]
+        """
+
+        real_paths = [p.replace(suite.full_prefix, '', 1) for p in paths \
+            if p.startswith(suite.full_prefix)]
+        # Test for paths that are under the suite's bases, so that we don't run
+        # a non-existent test.
+        bases = set()
+        for real_path in real_paths:
+            for base in suite.bases:
+                if real_path.startswith(base) or base.startswith(real_path):
+                    bases.add(base)
+
+        return list(bases)
+
+    def _virtual_tests_for_suite_with_paths(self, suite, paths):
         if not suite.bases:
             return []
+
+        bases = self._get_bases_for_suite_with_paths(suite, paths)
+
+        if not bases:
+            return []
+
         tests = []
         tests.extend(
-            map(lambda x: suite.full_prefix + x, self.real_tests(suite.bases)))
-        tests.extend(
-            self._wpt_test_urls_matching_paths(
-                suite.bases, [suite.full_prefix] * len(suite.bases)))
+            map(lambda x: suite.full_prefix + x, self.real_tests(bases)))
+
+        wpt_bases = []
+        for base in bases:
+            if any(base.startswith(wpt_dir) for wpt_dir in self.WPT_DIRS):
+                wpt_bases.append(base)
+
+        if wpt_bases:
+            tests.extend(
+                self._wpt_test_urls_matching_paths(
+                    wpt_bases, [suite.full_prefix] * len(wpt_bases)))
+
         return tests
 
     def _virtual_tests_matching_paths(self, paths):
         tests = []
         normalized_paths = [self.normalize_test_name(p) for p in paths]
         for suite in self.virtual_test_suites():
-            if not any(
-                    p.startswith(suite.full_prefix) for p in normalized_paths):
+            virtual_paths = [
+                p for p in normalized_paths if p.startswith(suite.full_prefix)
+            ]
+            if not virtual_paths:
                 continue
-            for test in self._all_virtual_tests_for_suite(suite):
+            for test in self._virtual_tests_for_suite_with_paths(
+                    suite, virtual_paths):
                 if any(test.startswith(p) for p in normalized_paths):
                     tests.append(test)
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py b/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
index bf8a1e1..c78d6f2 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
@@ -1024,6 +1024,40 @@
             sorted(port.tests(['virtual/virtual_wpt_dom/'])),
             dom_wpt + ['virtual/virtual_wpt_dom/wpt_internal/dom/bar.html'])
 
+    def test_virtual_test_paths(self):
+        port = self.make_port(with_tests=True)
+        PortTest._add_manifest_to_mock_file_system(port)
+        ssl_tests = [
+            'virtual/mixed_wpt/http/tests/ssl/text.html',
+        ]
+        http_passes_tests = [
+            'virtual/mixed_wpt/http/tests/passes/image.html',
+            'virtual/mixed_wpt/http/tests/passes/text.html',
+        ]
+        dom_tests = [
+            'virtual/mixed_wpt/external/wpt/dom/ranges/Range-attributes-slow.html',
+            'virtual/mixed_wpt/external/wpt/dom/ranges/Range-attributes.html',
+        ]
+
+        #  The full set of tests must be returned when running the entire suite.
+        self.assertEqual(sorted(port.tests(['virtual/mixed_wpt/'])),
+                         dom_tests + http_passes_tests + ssl_tests)
+
+        self.assertEqual(sorted(port.tests(['virtual/mixed_wpt/external'])),
+                         dom_tests)
+
+        self.assertEqual(sorted(port.tests(['virtual/mixed_wpt/http'])),
+                         http_passes_tests + ssl_tests)
+        self.assertEqual(
+            sorted(
+                port.tests([
+                    'virtual/mixed_wpt/http/tests/ssl',
+                    'virtual/mixed_wpt/external/wpt/dom'
+                ])), dom_tests + ssl_tests)
+
+        # Make sure we don't run a non-existent test.
+        self.assertEqual(sorted(port.tests(['virtual/mixed_wpt/passes'])), [])
+
     def test_is_non_wpt_test_file(self):
         port = self.make_port(with_tests=True)
         self.assertTrue(port.is_non_wpt_test_file('', 'foo.html'))
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/test.py b/third_party/blink/tools/blinkpy/web_tests/port/test.py
index d0845c3..5fe4710 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/test.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/test.py
@@ -135,7 +135,7 @@
 #
 # These numbers may need to be updated whenever we add or delete tests. This includes virtual tests.
 #
-TOTAL_TESTS = 171
+TOTAL_TESTS = 174
 TOTAL_WONTFIX = 3
 TOTAL_SKIPS = 26 + TOTAL_WONTFIX
 TOTAL_CRASHES = 78
@@ -737,6 +737,9 @@
             VirtualTestSuite(prefix='virtual_empty_bases',
                              bases=[],
                              args=['--virtual-arg-empty-bases']),
+            VirtualTestSuite(prefix='mixed_wpt',
+                             bases=['http', 'external/wpt/dom'],
+                             args=['--virtual-arg']),
         ]
 
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/run_web_tests_unittest.py b/third_party/blink/tools/blinkpy/web_tests/run_web_tests_unittest.py
index 794b5bc88..a12a544 100644
--- a/third_party/blink/tools/blinkpy/web_tests/run_web_tests_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/run_web_tests_unittest.py
@@ -43,6 +43,7 @@
 from blinkpy.common.system.path import abspath_to_uri
 from blinkpy.common.system.system_host import SystemHost
 
+from blinkpy.w3c.wpt_manifest import MANIFEST_NAME
 from blinkpy.web_tests import run_web_tests
 from blinkpy.web_tests.models import test_expectations
 from blinkpy.web_tests.models import test_failures
@@ -2296,6 +2297,14 @@
             baseline_full_path = '%s/%s' % (test.WEB_TEST_DIR, baseline)
             self.assertIsNone(written_files.get(baseline_full_path))
 
+    def assert_wpt_manifests_not_written(self, host, written_files):
+        external_manifest = host.filesystem.join(test.WEB_TEST_DIR,
+                                                 'external/wpt', MANIFEST_NAME)
+        internal_manifest = host.filesystem.join(test.WEB_TEST_DIR,
+                                                 'wpt_internal', MANIFEST_NAME)
+        self.assertNotIn(external_manifest, written_files)
+        self.assertNotIn(internal_manifest, written_files)
+
     def test_reset_results_basic(self):
         # Test that we update baselines in place when the test fails
         # (text and image mismatch).
@@ -2310,6 +2319,7 @@
         # baselines, it's OK for actual results to not match baselines.
         self.assertEqual(details.exit_code, 0)
         self.assertEqual(len(written_files.keys()), 7)
+        self.assert_wpt_manifests_not_written(host, written_files)
         self.assert_baselines(
             written_files,
             log_stream,
@@ -2703,7 +2713,7 @@
                                              host=host)
         written_files = host.filesystem.written_files
         self.assertEqual(details.exit_code, 0)
-        self.assertEqual(len(written_files.keys()), 9)
+        self.assertEqual(len(written_files.keys()), 7)
         # We should create new image baseline only.
         self.assert_baselines(
             written_files,
@@ -2792,7 +2802,8 @@
         self.assertEqual(details.exit_code, 0)
         self.assertFalse(host.filesystem.exists(virtual_baseline_txt))
         written_files = host.filesystem.written_files
-        self.assertEqual(len(written_files.keys()), 10)
+        self.assertEqual(len(written_files.keys()), 8)
+        self.assert_wpt_manifests_not_written(host, written_files)
         # We should create new image baseline only.
         self.assert_baselines(
             written_files,
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 78106cda..7a504f12 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5344,6 +5344,13 @@
 ###crbug.com/816914 [ Mac ] fast/canvas/canvas-drawImage-live-video.html [ Failure Pass ]
 crbug.com/817167 http/tests/devtools/oopif/oopif-cookies-refresh.js [ Failure Timeout Pass ]
 
+# Disable temporarily on Win7, will remove them when they are not flaky. 
+crbug.com/1047176 [ Win7 ] fast/forms/suggestion-picker/date-suggestion-picker-mouse-operations.html [ Pass Failure ]
+crbug.com/1047176 [ Win7 ] fast/forms/suggestion-picker/datetimelocal-suggestion-picker-mouse-operations.html [ Pass Failure ]
+crbug.com/1047176 [ Win7 ] fast/forms/suggestion-picker/month-suggestion-picker-mouse-operations.html [ Pass Failure ]
+crbug.com/1047176 [ Win7 ] fast/forms/suggestion-picker/time-suggestion-picker-mouse-operations.html [ Pass Failure ]
+crbug.com/1047176 [ Win7 ] fast/forms/suggestion-picker/week-suggestion-picker-mouse-operations.html [ Pass Failure ]
+
 # Sheriff 2018-03-02
 crbug.com/818076 http/tests/devtools/oopif/oopif-elements-navigate-in.js [ Failure Pass ]
 
diff --git a/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt b/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt
index 2e758649..4cc7b24 100644
--- a/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt
+++ b/third_party/blink/web_tests/accessibility/textarea-selection-expected.txt
@@ -11,33 +11,33 @@
 PASS textareaAccessible is textareaAccessible.selectionFocusObject
 PASS textareaAccessible.selectionFocusOffset is 7
 PASS textareaAccessible is textareaAccessible.selectionAnchorObject
-PASS textareaAccessible.selectionAnchorOffset is 0
+PASS textareaAccessible.selectionAnchorOffset is 7
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 0
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 7
+PASS textareaAccessible is textareaAccessible.selectionFocusObject
+PASS textareaAccessible.selectionFocusOffset is 14
+PASS textareaAccessible is textareaAccessible.selectionAnchorObject
+PASS textareaAccessible.selectionAnchorOffset is 14
 PASS textareaAccessible is textareaAccessible.selectionFocusObject
 PASS textareaAccessible.selectionFocusOffset is 7
 PASS textareaAccessible is textareaAccessible.selectionAnchorObject
-PASS textareaAccessible.selectionAnchorOffset is 7
-PASS textareaAccessible is textareaAccessible.selectionFocusObject
-PASS textareaAccessible.selectionFocusOffset is 14
-PASS textareaAccessible is textareaAccessible.selectionAnchorObject
-PASS textareaAccessible.selectionAnchorOffset is 7
-PASS textareaAccessible is textareaAccessible.selectionFocusObject
-PASS textareaAccessible.selectionFocusOffset is 14
-PASS textareaAccessible is textareaAccessible.selectionAnchorObject
 PASS textareaAccessible.selectionAnchorOffset is 14
 PASS textareaAccessible is textareaAccessible.selectionFocusObject
 PASS textareaAccessible.selectionFocusOffset is 21
 PASS textareaAccessible is textareaAccessible.selectionAnchorObject
-PASS textareaAccessible.selectionAnchorOffset is 14
+PASS textareaAccessible.selectionAnchorOffset is 21
 PASS textareaAccessible is textareaAccessible.selectionFocusObject
-PASS textareaAccessible.selectionFocusOffset is 21
+PASS textareaAccessible.selectionFocusOffset is 14
 PASS emptyTextareaAccessible is textareaAccessible.selectionAnchorObject
 PASS textareaAccessible.selectionAnchorOffset is 0
 PASS emptyTextareaAccessible is textareaAccessible.selectionFocusObject
 PASS textareaAccessible.selectionFocusOffset is 0
 PASS textareaAccessible is textareaAccessible.selectionAnchorObject
-PASS textareaAccessible.selectionAnchorOffset is 14
+PASS textareaAccessible.selectionAnchorOffset is 21
 PASS textareaAccessible is textareaAccessible.selectionFocusObject
-PASS textareaAccessible.selectionFocusOffset is 21
+PASS textareaAccessible.selectionFocusOffset is 14
 PASS emptyTextareaAccessible is emptyTextareaAccessible.selectionAnchorObject
 PASS emptyTextareaAccessible.selectionAnchorOffset is 0
 PASS emptyTextareaAccessible is emptyTextareaAccessible.selectionFocusObject
diff --git a/third_party/blink/web_tests/accessibility/textarea-selection.html b/third_party/blink/web_tests/accessibility/textarea-selection.html
index e58317d7..73c8ab3 100644
--- a/third_party/blink/web_tests/accessibility/textarea-selection.html
+++ b/third_party/blink/web_tests/accessibility/textarea-selection.html
@@ -18,7 +18,7 @@
 
         if (window.accessibilityController) {
 
-            let textarea = document.getElementById('textarea');
+            const textarea = document.getElementById('textarea');
             textarea.focus();
             window.textareaAccessible =
                 accessibilityController.accessibleElementById('textarea');
@@ -47,14 +47,14 @@
                                                direction);
                     shouldBe("textareaAccessible", "textareaAccessible.selectionAnchorObject");
                     shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset",
-                        selectionAnchorOffset);
+                        directionIndex ? selectionFocusOffset : selectionAnchorOffset);
                     shouldBe("textareaAccessible", "textareaAccessible.selectionFocusObject");
                     shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset",
-                        selectionFocusOffset);
+                        directionIndex ? selectionAnchorOffset : selectionFocusOffset);
                 }
             }
 
-            let emptyTextarea = document.getElementById('textarea-empty');
+            const emptyTextarea = document.getElementById('textarea-empty');
             emptyTextarea.focus();            
             // Each textarea remembers its own independent selection but
             // textareas that are not focused don't expose their selection
@@ -66,9 +66,9 @@
 
             textarea.focus();
             shouldBe("textareaAccessible", "textareaAccessible.selectionAnchorObject");
-            shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset", 14);
+            shouldBeEqualToNumber("textareaAccessible.selectionAnchorOffset", 21);
             shouldBe("textareaAccessible", "textareaAccessible.selectionFocusObject");
-            shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset", 21);
+            shouldBeEqualToNumber("textareaAccessible.selectionFocusOffset", 14);
 
             emptyTextarea.focus();
             shouldBe("emptyTextareaAccessible", "emptyTextareaAccessible.selectionAnchorObject");
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_update_index9.any.js b/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_update_index9.any.js
new file mode 100644
index 0000000..d666b59
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_update_index9.any.js
@@ -0,0 +1,43 @@
+// META: script=support-promises.js
+
+promise_test(async t => {
+  const db = await createDatabase(t, db => {
+    const store = db.createObjectStore('store');
+    store.createIndex('index', 'value');
+    store.put({value: 1}, 1);
+    store.put({value: 2}, 2);
+    store.put({value: 3}, 3);
+  });
+
+  {
+    // Iterate over all index entries until an upper bound is reached.
+    // On each record found, increment the value used as the index
+    // key, which will make it show again up later in the iteration.
+    const tx = db.transaction('store', 'readwrite');
+    const range = IDBKeyRange.upperBound(9);
+    const index = tx.objectStore('store').index('index');
+    const request = index.openCursor(range);
+    request.onsuccess = t.step_func(e => {
+      const cursor = e.target.result;
+      if (!cursor)
+        return;
+
+      const record = cursor.value;
+      record.value += 1;
+      cursor.update(record);
+
+      cursor.continue();
+    });
+
+    await promiseForTransaction(t, tx);
+  }
+
+  {
+    const tx = db.transaction('store', 'readonly');
+    const results = await promiseForRequest(t, tx.objectStore('store').getAll());
+    assert_array_equals(
+      results.map(record => record.value),
+      [10, 10, 10],
+      'Values should all be incremented until bound reached');
+  }
+}, 'Index cursor - indexed values updated during iteration');
diff --git a/third_party/blink/web_tests/fast/forms/number/number-wheel-event-expected.txt b/third_party/blink/web_tests/fast/forms/number/number-wheel-event-expected.txt
index f4d5677..1e5660c 100644
--- a/third_party/blink/web_tests/fast/forms/number/number-wheel-event-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/number/number-wheel-event-expected.txt
@@ -4,11 +4,11 @@
 
 Initial value is 0. We'll wheel up by 1:
 PASS input.value is "1"
-Wheel up by 100:
+Wheel up by 3:
 PASS input.value is "2"
 Wheel down by 1:
 PASS input.value is "1"
-Wheel down by 256:
+Wheel down by 3:
 PASS input.value is "0"
 Disabled input element:
 PASS input.value is "0"
diff --git a/third_party/blink/web_tests/fast/forms/number/number-wheel-event.html b/third_party/blink/web_tests/fast/forms/number/number-wheel-event.html
index 25be77e9..a99d035 100644
--- a/third_party/blink/web_tests/fast/forms/number/number-wheel-event.html
+++ b/third_party/blink/web_tests/fast/forms/number/number-wheel-event.html
@@ -2,15 +2,21 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/gesture-util.js"></script>
 <script src="../resources/common-wheel-event.js"></script>
 </head>
-<body>
+<body onload="runTest();">
 <script>
-testWheelEvent({
-    'inputType' : 'number',
-    'initialValue' : '0',
-    'stepUpValue1' : '1',
-    'stepUpValue2' : '2' });
+
+async function runTest() {
+    await testWheelEvent({
+        'inputType' : 'number',
+        'initialValue' : '0',
+        'stepUpValue1' : '1',
+        'stepUpValue2' : '2' });
+    finishJSTest();
+}
+
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/fast/forms/resources/common-wheel-event.js b/third_party/blink/web_tests/fast/forms/resources/common-wheel-event.js
index 8e952e6..8bb7ed8 100644
--- a/third_party/blink/web_tests/fast/forms/resources/common-wheel-event.js
+++ b/third_party/blink/web_tests/fast/forms/resources/common-wheel-event.js
@@ -1,12 +1,15 @@
-function dispatchWheelEvent(element, deltaX, deltaY)
+jsTestIsAsync = true;
+
+// Positive deltaX or deltaY means scroll right or down.
+async function dispatchWheelEvent(element, deltaX, deltaY)
 {
-    var rect = element.getClientRects()[0]
-    eventSender.mouseMoveTo(rect.left, rect.top);
-    eventSender.mouseScrollBy(deltaX, deltaY);
+    element_center = elementCenter(element);
+    await wheelTick(deltaX, deltaY, element_center);
+    await waitForCompositorCommit();
 }
 
 var input;
-function testWheelEvent(parameters)
+async function testWheelEvent(parameters)
 {
     var inputType = parameters['inputType'];
     var initialValue = parameters['initialValue'];
@@ -20,37 +23,40 @@
     input.focus();
 
     debug('Initial value is ' + initialValue + '. We\'ll wheel up by 1:');
-    dispatchWheelEvent(input, 0, 1);
+    await dispatchWheelEvent(input, 0, -1);
     shouldBeEqualToString('input.value', stepUpValue1);
 
-    debug('Wheel up by 100:');
-    dispatchWheelEvent(input, 0, 100);
+    // We change the selected value in ScrollBegin and the three wheel ticks
+    // are treated as a single stream with one ScrollBegin, so we increase or
+    // decrease by one value even though there is more than one wheel tick.
+    debug('Wheel up by 3:');
+    await dispatchWheelEvent(input, 0, -3);
     shouldBeEqualToString('input.value', stepUpValue2);
 
     debug('Wheel down by 1:');
-    dispatchWheelEvent(input, 0, -1);
+    await dispatchWheelEvent(input, 0, 1);
     shouldBeEqualToString('input.value', stepUpValue1);
 
-    debug('Wheel down by 256:');
-    dispatchWheelEvent(input, 0, -256);
+    debug('Wheel down by 3:');
+    await dispatchWheelEvent(input, 0, 3);
     shouldBeEqualToString('input.value', initialValue);
 
     debug('Disabled input element:');
     input.disabled = true;
-    dispatchWheelEvent(input, 0, 1);
+    await dispatchWheelEvent(input, 0, -1);
     shouldBeEqualToString('input.value', initialValue);
     input.removeAttribute('disabled');
 
 
     debug('Read-only input element:');
     input.readOnly = true;
-    dispatchWheelEvent(input, 0, 1);
+    await dispatchWheelEvent(input, 0, -1);
     shouldBeEqualToString('input.value', initialValue);
     input.readOnly = false;
 
     debug('No focus:');
     document.getElementById('another').focus();
-    dispatchWheelEvent(input, 0, 1);
+    await dispatchWheelEvent(input, 0, -1);
     shouldBeEqualToString('input.value', initialValue);
 
     parent.parentNode.removeChild(parent);
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/date-suggestion-picker-mouse-operations.html b/third_party/blink/web_tests/fast/forms/suggestion-picker/date-suggestion-picker-mouse-operations.html
index 9872577..3933e47 100644
--- a/third_party/blink/web_tests/fast/forms/suggestion-picker/date-suggestion-picker-mouse-operations.html
+++ b/third_party/blink/web_tests/fast/forms/suggestion-picker/date-suggestion-picker-mouse-operations.html
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/gesture-util.js"></script>
 <script src="../../forms/resources/common.js"></script>
 <script src="../../forms/resources/common-wheel-event.js"></script>
 <script src="../../forms/resources/picker-common.js"></script>
@@ -88,7 +89,11 @@
 debug('Check that page popup doesn\'t exist at first.');
 shouldBeNull('$("mock-page-popup")');
 
-openPicker($('date'), test1);
+window.onload = function() {
+    if (window.internals)
+        internals.settings.setScrollAnimatorEnabled(false);
+    openPicker($('date'), test1);
+};
 
 function test1() {
     debug('Check that page popup exists.');
@@ -129,15 +134,19 @@
         waitUntilClosing(test2AfterClosing);
     }
 
-    function scrollUp() {
+    async function scrollUp() {
         scrollTopBeforeWheelEvent = suggestionList.scrollTop;
-        dispatchWheelEvent(suggestionList, 0, 100);
+        await dispatchWheelEvent(suggestionList, 0, -3);
+        await waitFor(() => {
+            return scrollTopBeforeWheelEvent > suggestionList.scrollTop});
         shouldBecomeEqual('scrollTopBeforeWheelEvent > suggestionList.scrollTop', 'true', finishTest);
     }
 
-    function scrollDown() {
+    async function scrollDown() {
         scrollTopBeforeWheelEvent = suggestionList.scrollTop;
-        dispatchWheelEvent(suggestionList, 0, -100);
+        await dispatchWheelEvent(suggestionList, 0, 3);
+        await waitFor(() => {
+            return scrollTopBeforeWheelEvent < suggestionList.scrollTop});
         shouldBecomeEqual('scrollTopBeforeWheelEvent < suggestionList.scrollTop', 'true', scrollUp);
     }
 
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-mouse-operations.html b/third_party/blink/web_tests/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-mouse-operations.html
index 62f2569..ece07599 100644
--- a/third_party/blink/web_tests/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-mouse-operations.html
+++ b/third_party/blink/web_tests/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-mouse-operations.html
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/gesture-util.js"></script>
 <script src="../../forms/resources/common.js"></script>
 <script src="../../forms/resources/common-wheel-event.js"></script>
 <script src="../../forms/resources/picker-common.js"></script>
@@ -39,6 +40,8 @@
 shouldBeNull('$("mock-page-popup")');
 
 window.onload = function() {
+    if (window.internals)
+        internals.settings.setScrollAnimatorEnabled(false);
     openPicker($('datetime-local'), test1);
 };
 
@@ -82,15 +85,19 @@
         waitUntilClosing(test2AfterClosing);
     }
 
-    function scrollUp() {
+    async function scrollUp() {
         scrollTopBeforeWheelEvent = suggestionList.scrollTop;
-        dispatchWheelEvent(suggestionList, 0, 100);
-        shouldBecomeEqual('scrollTopBeforeWheelEvent > suggestionList.scrollTop', 'true', finishTest)
+        await dispatchWheelEvent(suggestionList, 0, -3);
+        await waitFor(() => {
+            return scrollTopBeforeWheelEvent > suggestionList.scrollTop});
+        shouldBecomeEqual('scrollTopBeforeWheelEvent > suggestionList.scrollTop', 'true', finishTest);
     }
 
-    function scrollDown() {
+    async function scrollDown() {
         scrollTopBeforeWheelEvent = suggestionList.scrollTop;
-        dispatchWheelEvent(suggestionList, 0, -100);
+        await dispatchWheelEvent(suggestionList, 0, 3);
+        await waitFor(() => {
+            return scrollTopBeforeWheelEvent < suggestionList.scrollTop});
         shouldBecomeEqual('scrollTopBeforeWheelEvent < suggestionList.scrollTop', 'true', scrollUp);
     }
 
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/month-suggestion-picker-mouse-operations.html b/third_party/blink/web_tests/fast/forms/suggestion-picker/month-suggestion-picker-mouse-operations.html
index e7683c07..5ac7c6a 100644
--- a/third_party/blink/web_tests/fast/forms/suggestion-picker/month-suggestion-picker-mouse-operations.html
+++ b/third_party/blink/web_tests/fast/forms/suggestion-picker/month-suggestion-picker-mouse-operations.html
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/gesture-util.js"></script>
 <script src="../../forms/resources/common.js"></script>
 <script src="../../forms/resources/common-wheel-event.js"></script>
 <script src="../../forms/resources/picker-common.js"></script>
@@ -68,7 +69,11 @@
 debug('Check that page popup doesn\'t exist at first.');
 shouldBeNull('$("mock-page-popup")');
 
-openPicker($('month'), test1);
+window.onload = function() {
+    if (window.internals)
+        internals.settings.setScrollAnimatorEnabled(false);
+    openPicker($('month'), test1);
+};
 
 function test1() {
     debug('Check that page popup exists.');
@@ -109,15 +114,19 @@
         waitUntilClosing(test2AfterClosing);
     }
 
-    function scrollUp() {
+    async function scrollUp() {
         scrollTopBeforeWheelEvent = suggestionList.scrollTop;
-        dispatchWheelEvent(suggestionList, 0, 100);
+        await dispatchWheelEvent(suggestionList, 0, -3);
+        await waitFor(() => {
+            return scrollTopBeforeWheelEvent > suggestionList.scrollTop});
         shouldBecomeEqual('scrollTopBeforeWheelEvent > suggestionList.scrollTop', 'true', finishTest)
     }
 
-    function scrollDown() {
+    async function scrollDown() {
         scrollTopBeforeWheelEvent = suggestionList.scrollTop;
-        dispatchWheelEvent(suggestionList, 0, -100);
+        await dispatchWheelEvent(suggestionList, 0, 3);
+        await waitFor(() => {
+            return scrollTopBeforeWheelEvent < suggestionList.scrollTop});
         shouldBecomeEqual('scrollTopBeforeWheelEvent < suggestionList.scrollTop', 'true', scrollUp);
     }
 
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-mouse-operations-expected.txt b/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-mouse-operations-expected.txt
index 2dcfae1..b04d5c9 100644
--- a/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-mouse-operations-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-mouse-operations-expected.txt
@@ -2,8 +2,6 @@
 PASS $("mock-page-popup") is null
 Check that page popup exists.
 PASS popupWindow.pagePopupController.toString() is "[object PagePopupController]"
-Check that page popup exists.
-PASS popupWindow.pagePopupController.toString() is "[object PagePopupController]"
 Check that hovering over an entry highlights it.
 PASS highlightedEntry() is "01:02"
 Check that moving the mouse outside the popup de-highlights entries.
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-mouse-operations.html b/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-mouse-operations.html
index e250790..fafdbe1 100644
--- a/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-mouse-operations.html
+++ b/third_party/blink/web_tests/fast/forms/suggestion-picker/time-suggestion-picker-mouse-operations.html
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/gesture-util.js"></script>
 <script src="../../forms/resources/common.js"></script>
 <script src="../../forms/resources/common-wheel-event.js"></script>
 <script src="../../forms/resources/picker-common.js"></script>
@@ -39,6 +40,8 @@
 shouldBeNull('$("mock-page-popup")');
 
 window.onload = function() {
+    if (window.internals)
+        internals.settings.setScrollAnimatorEnabled(false);
     openPicker($('time'), test1);
 };
 
@@ -47,9 +50,6 @@
     debug('Check that page popup exists.');
     shouldBeEqualToString('popupWindow.pagePopupController.toString()', '[object PagePopupController]');
 
-    debug('Check that page popup exists.');
-    shouldBeEqualToString('popupWindow.pagePopupController.toString()', '[object PagePopupController]');
-
     debug('Check that hovering over an entry highlights it.');
     hoverOverElement(popupWindow.document.querySelector(".suggestion-list-entry:nth-child(2)"));
     shouldBeEqualToString('highlightedEntry()', '01:02');
@@ -85,15 +85,19 @@
         waitUntilClosing(test2AfterClosing);
     }
 
-    function scrollUp() {
+    async function scrollUp() {
         scrollTopBeforeWheelEvent = suggestionList.scrollTop;
-        dispatchWheelEvent(suggestionList, 0, 100);
+        await dispatchWheelEvent(suggestionList, 0, -3);
+        await waitFor(() => {
+            return scrollTopBeforeWheelEvent > suggestionList.scrollTop});
         shouldBecomeEqual('scrollTopBeforeWheelEvent > suggestionList.scrollTop', 'true', finishTest)
     }
 
-    function scrollDown() {
+    async function scrollDown() {
         scrollTopBeforeWheelEvent = suggestionList.scrollTop;
-        dispatchWheelEvent(suggestionList, 0, -100);
+        await dispatchWheelEvent(suggestionList, 0, 3);
+        await waitFor(() => {
+            return scrollTopBeforeWheelEvent < suggestionList.scrollTop});
         shouldBecomeEqual('scrollTopBeforeWheelEvent < suggestionList.scrollTop', 'true', scrollUp);
     }
 
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/week-suggestion-picker-mouse-operations-expected.txt b/third_party/blink/web_tests/fast/forms/suggestion-picker/week-suggestion-picker-mouse-operations-expected.txt
index 48696f0..705b944 100644
--- a/third_party/blink/web_tests/fast/forms/suggestion-picker/week-suggestion-picker-mouse-operations-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/suggestion-picker/week-suggestion-picker-mouse-operations-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE WARNING: line 13: The specified value "2012-12-24" does not conform to the required format.  The format is "yyyy-Www" where yyyy is year in four or more digits, and ww is 01-53.
 Check that page popup doesn't exist at first.
 PASS $("mock-page-popup") is null
 Check that page popup exists.
diff --git a/third_party/blink/web_tests/fast/forms/suggestion-picker/week-suggestion-picker-mouse-operations.html b/third_party/blink/web_tests/fast/forms/suggestion-picker/week-suggestion-picker-mouse-operations.html
index adbf3357..4b3edad 100644
--- a/third_party/blink/web_tests/fast/forms/suggestion-picker/week-suggestion-picker-mouse-operations.html
+++ b/third_party/blink/web_tests/fast/forms/suggestion-picker/week-suggestion-picker-mouse-operations.html
@@ -2,6 +2,7 @@
 <html>
 <head>
 <script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/gesture-util.js"></script>
 <script src="../../forms/resources/common.js"></script>
 <script src="../../forms/resources/common-wheel-event.js"></script>
 <script src="../../forms/resources/picker-common.js"></script>
@@ -10,7 +11,7 @@
 <body style="background-color: #bbbbbb;">
 <p id="description"></p>
 <div id="console"></div>
-<input type=week id=week value="2012-12-24" list=suggestions>
+<input type=week id=week value="2012-W52" list=suggestions>
 <datalist id=suggestions>
     <option label="This Week">2012-W01</option>
     <option>2012-W02</option>
@@ -87,7 +88,11 @@
 debug('Check that page popup doesn\'t exist at first.');
 shouldBeNull('$("mock-page-popup")');
 
-openPicker($('week'), test1);
+window.onload = function() {
+    if (window.internals)
+        internals.settings.setScrollAnimatorEnabled(false);
+    openPicker($('week'), test1);
+};
 
 function test1() {
     debug('Check that page popup exists.');
@@ -128,15 +133,19 @@
         waitUntilClosing(test2AfterClosing);
     }
 
-    function scrollUp() {
+    async function scrollUp() {
         scrollTopBeforeWheelEvent = suggestionList.scrollTop;
-        dispatchWheelEvent(suggestionList, 0, 100);
+        await dispatchWheelEvent(suggestionList, 0, -3);
+        await waitFor(() => {
+            return scrollTopBeforeWheelEvent > suggestionList.scrollTop});
         shouldBecomeEqual('scrollTopBeforeWheelEvent > suggestionList.scrollTop', 'true', finishTest)
     }
 
-    function scrollDown() {
+    async function scrollDown() {
         scrollTopBeforeWheelEvent = suggestionList.scrollTop;
-        dispatchWheelEvent(suggestionList, 0, -100);
+        await dispatchWheelEvent(suggestionList, 0, 3);
+        await waitFor(() => {
+            return scrollTopBeforeWheelEvent < suggestionList.scrollTop});
         shouldBecomeEqual('scrollTopBeforeWheelEvent < suggestionList.scrollTop', 'true', scrollUp);
     }
 
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-wheel.html b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-wheel.html
index abfbd5a..cf61708 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-wheel.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/time-picker/time-picker-wheel.html
@@ -3,6 +3,7 @@
 if (window.internals)
     internals.settings.setLangAttributeAwareFormControlUIEnabled(true);
 </script>
+<script src="../../../resources/gesture-util.js"></script>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../fast/forms/resources/common.js"></script>
@@ -13,16 +14,16 @@
 <script>
 let t = async_test('Test scrolling in time picker.');
 
-function test1() {
+async function test1() {
   let hourColumn = popupWindow.global.picker.timeColumns.firstChild;
   const scrollHourTopBeforeWheelEvent = hourColumn.scrollTop;
   // scroll up by 2 ticks ~ 2 cells
-  dispatchWheelEvent(hourColumn, 0, 2);
+  await dispatchWheelEvent(hourColumn, 0, -2);
 
   let minuteColumn = hourColumn.nextSibling;
   const scrollMinuteTopBeforeWheelEvent = minuteColumn.scrollTop;
   // scroll up by 3 ticks ~ 3 cells
-  dispatchWheelEvent(minuteColumn, 0, 3);
+  await dispatchWheelEvent(minuteColumn, 0, -3);
 
   t.step_timeout(function() {
     // verify that both columns have been scrolled up.
diff --git a/third_party/closure_compiler/externs/developer_private.js b/third_party/closure_compiler/externs/developer_private.js
index 8c2903b..ceb5521 100644
--- a/third_party/closure_compiler/externs/developer_private.js
+++ b/third_party/closure_compiler/externs/developer_private.js
@@ -218,13 +218,6 @@
 /**
  * @enum {string}
  */
-chrome.developerPrivate.ControllerType = {
-  POLICY: 'POLICY',
-};
-
-/**
- * @enum {string}
- */
 chrome.developerPrivate.HostAccess = {
   ON_CLICK: 'ON_CLICK',
   ON_SPECIFIC_SITES: 'ON_SPECIFIC_SITES',
@@ -233,7 +226,6 @@
 
 /**
  * @typedef {{
- *   type: !chrome.developerPrivate.ControllerType,
  *   text: string
  * }}
  */
diff --git a/tools/bisect-builds.py b/tools/bisect-builds.py
index 8a29c982..9efc7bfa 100755
--- a/tools/bisect-builds.py
+++ b/tools/bisect-builds.py
@@ -1077,7 +1077,9 @@
            '    Chrome\'s about: build number and omahaproxy branch_revision\n'
            '    are incorrect, they are from branches.\n'
            '\n'
-           'Tip: add "-- --no-first-run" to bypass the first run prompts.')
+           'Use "-- <args-to-pass-to-chromium>" to pass arbitrary extra \n'
+           'arguments to the test binaries.\n'
+           'E.g., add "-- --no-first-run" to bypass the first run prompts.')
   parser = optparse.OptionParser(usage=usage)
   # Strangely, the default help output doesn't include the choice list.
   choices = ['mac', 'mac64', 'win', 'win64', 'linux', 'linux64', 'linux-arm',
@@ -1113,14 +1115,17 @@
                     default=1,
                     help='Number of times to run each build before asking '
                          'if it\'s good or bad. Temporary profiles are reused.')
-  parser.add_option('-c', '--command',
+  parser.add_option('-c',
+                    '--command',
                     type='str',
                     default='%p %a',
                     help='Command to execute. %p and %a refer to Chrome '
-                         'executable and specified extra arguments '
-                         'respectively. Use %s to specify all extra arguments '
-                         'as one string. Defaults to "%p %a". Note that any '
-                         'extra paths specified should be absolute.')
+                    'executable and specified extra arguments respectively. '
+                    'Use %s to specify all extra arguments as one string. '
+                    'Defaults to "%p %a". Note that any extra paths specified '
+                    'should be absolute. If you just need to append an '
+                    'argument to the Chrome command line use "-- '
+                    '<args-to-pass-to-chromium>" instead.')
   parser.add_option('-l', '--blink',
                     action='store_true',
                     help='Use Blink bisect instead of Chromium. ')
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index c309114..2056513 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -455,6 +455,34 @@
   # enough GCC to build Clang.
   MaybeDownloadHostGcc(args)
 
+  # mac/arm64 needs MacOSX11.0.sdk. System Xcode (+ SDK) on the chrome bots
+  # is something much older.
+  # Options:
+  # - temporarily set system Xcode to Xcode 12 beta while running this script,
+  #   (cf build/swarming_xcode_install.py, but it looks unused)
+  # - use Xcode 12 beta for everything on tot bots, only need to fuzz with
+  #   scripts/slave/recipes/chromium_upload_clang.py then (but now the
+  #   chrome/ios build will use the 11.0 SDK too and we'd be on the hook for
+  #   keeping it green -- if it's currently green, who knows)
+  # - pass flags to cmake to try to coax it into using Xcode 12 beta for the
+  #   LLVM build without it being system Xcode.
+  #
+  # The last option seems best, so let's go with that. We need to pass
+  # -isysroot to the 11.0 SDK and -B to the /usr/bin so that the new ld64 is
+  # used.
+  if sys.platform == 'darwin':
+    isysroot = subprocess.check_output(['xcrun', '--show-sdk-path']).rstrip()
+  if sys.platform == 'darwin' and args.bootstrap:
+    sys.path.insert(1, os.path.join(CHROMIUM_DIR, 'build'))
+    import mac_toolchain
+    LLVM_XCODE = os.path.join(THIRD_PARTY_DIR, 'llvm-xcode')
+    mac_toolchain.InstallXcodeBinaries('xcode_12_beta', LLVM_XCODE)
+    isysroot = os.path.join(LLVM_XCODE, 'Contents', 'Developer', 'Platforms',
+                            'MacOSX.platform', 'Developer', 'SDKs',
+                            'MacOSX11.0.sdk')
+    xcode_bin = os.path.join(LLVM_XCODE, 'Contents', 'Developer', 'Toolchains',
+                             'XcodeDefault.xctoolchain', 'usr', 'bin')
+
   global CLANG_REVISION, PACKAGE_VERSION
   if args.llvm_force_head_revision:
     CLANG_REVISION = GetLatestLLVMCommit()
@@ -720,9 +748,8 @@
                 '-target', 'x86_64-unknown-unknown', '-O2', '-g', '-std=c++14',
                  '-fno-exceptions', '-fno-rtti', '-w', '-c', training_source]
     if sys.platform == 'darwin':
-      train_cmd.extend(['-stdlib=libc++', '-isysroot',
-                        subprocess.check_output(['xcrun',
-                                                 '--show-sdk-path']).rstrip()])
+      # TODO(thakis): Try switching tihs back to `xcrun --show-sdk-path`.
+      train_cmd.extend(['-stdlib=libc++', '-isysroot', isysroot])
     RunCommand(train_cmd, msvc_arch='x64')
 
     # Merge profiles.
@@ -749,9 +776,20 @@
         # iPhones). armv7k is Apple Watch, which we don't need.
         '-DDARWIN_ios_ARCHS=armv7;armv7s;arm64',
         '-DDARWIN_iossim_ARCHS=i386;x86_64',
-        # We don't need 32-bit intel support for macOS, we only ship 64-bit.
-        '-DDARWIN_osx_ARCHS=x86_64',
         ])
+    if args.bootstrap:
+      # Include an arm64 slice for libclang_rt.osx.a. This requires using
+      # MacOSX11.0.sdk (via -isysroot, via DARWIN_macosx_CACHED_SYSROOT) and
+      # the new ld, via -B
+      compiler_rt_args.extend([
+          # We don't need 32-bit intel support for macOS, we only ship 64-bit.
+          # XXX probably only add arm64 for bootstrap builds
+          '-DDARWIN_osx_ARCHS=arm64;x86_64',
+          '-DDARWIN_macosx_CACHED_SYSROOT=' + isysroot,
+      ])
+      ldflags += ['-B', xcode_bin]
+    else:
+      compiler_rt_args.extend(['-DDARWIN_osx_ARCHS=x86_64'])
   else:
     compiler_rt_args.append('-DCOMPILER_RT_BUILD_BUILTINS=OFF')
 
@@ -764,8 +802,8 @@
   if sys.platform == 'darwin' and args.bootstrap:
     # When building on 10.9, /usr/include usually doesn't exist, and while
     # Xcode's clang automatically sets a sysroot, self-built clangs don't.
-    cflags = ['-isysroot', subprocess.check_output(
-        ['xcrun', '--show-sdk-path']).rstrip()]
+    # TODO(thakis): Try changing this back to `xcrun --show-sdk-path`.
+    cflags = ['-isysroot', isysroot]
     cxxflags = ['-stdlib=libc++'] + cflags
     ldflags += ['-stdlib=libc++']
     deployment_target = '10.7'
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index e88ed2c70..e418f34 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -39,7 +39,7 @@
 # Reverting problematic clang rolls is safe, though.
 CLANG_REVISION = 'fb1aa286c1400ad7'
 CLANG_SVN_REVISION = 'n358615'
-CLANG_SUB_REVISION = 1
+CLANG_SUB_REVISION = 2
 
 PACKAGE_VERSION = '%s-%s-%s' % (CLANG_SVN_REVISION, CLANG_REVISION[:8],
                                 CLANG_SUB_REVISION)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 90e6205..a4cbd2d 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -41947,6 +41947,7 @@
   <int value="773743944" label="show-android-files-in-files-app"/>
   <int value="773919225" label="disable-office-editing-component-extension"/>
   <int value="775075949" label="ImeInputLogicHmm:enabled"/>
+  <int value="775148009" label="OsSettingsDeepLinking:disabled"/>
   <int value="777667507" label="DesktopPWAsLinkCapturing:enabled"/>
   <int value="779086132" label="enable-data-reduction-proxy-alt"/>
   <int value="779703052" label="ChromeOSAmbientMode:enabled"/>
@@ -42740,6 +42741,7 @@
   <int value="1661925474" label="silent-debugger-extension-api"/>
   <int value="1664401033" label="ColorCorrectRendering:enabled"/>
   <int value="1665349789" label="spurious-power-button-window"/>
+  <int value="1665400247" label="OsSettingsDeepLinking:enabled"/>
   <int value="1667584730" label="WebXR:disabled"/>
   <int value="1668611601" label="enable-encrypted-media"/>
   <int value="1669767166" label="NewTabPageBackgrounds:disabled"/>
@@ -50500,6 +50502,9 @@
 </enum>
 
 <enum name="OfflinePagesLoadStatus">
+  <obsolete>
+    OfflinePages.LoadStatus was removed in M65.
+  </obsolete>
   <int value="0" label="Success"/>
   <int value="1" label="Store init failed"/>
   <int value="2" label="Store load failed"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 10d5be3..b93f01cb 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -8236,6 +8236,17 @@
   </summary>
 </histogram>
 
+<histogram name="Arc.AccessibilityWithTalkBack" enum="BooleanEnabled"
+    expires_after="2021-07-01">
+  <owner>hirokisato@chromium.org</owner>
+  <owner>sarakato@chromium.org</owner>
+  <summary>
+    Whether the ARC window is opened with TalkBack enabled or not. Counted when
+    a user opens a window or toggles the feature. Checked only when the spoken
+    feedback is enabled in Chrome OS.
+  </summary>
+</histogram>
+
 <histogram name="Arc.AdbSideloadingEnablingScreen"
     enum="AdbSideloadingPromptEvent" expires_after="M88">
   <owner>victorhsieh@chromium.org</owner>
@@ -11106,8 +11117,9 @@
 </histogram>
 
 <histogram name="Ash.Window.AnimationSmoothness.Hide" units="%"
-    expires_after="2020-07-13">
-  <owner>wutao@chromium.org</owner>
+    expires_after="2021-07-01">
+  <owner>sammiequon@chromium.org</owner>
+  <owner>tclaiborne@chromium.org</owner>
   <summary>
     Relative smoothness of hiding window animation. 100% represents ideally
     smooth 60 frames per second. 50% represents when only 30 frames per second
@@ -11118,7 +11130,7 @@
 </histogram>
 
 <histogram name="Ash.Window.AnimationSmoothness.Snap" units="%"
-    expires_after="M89">
+    expires_after="2021-07-01">
   <owner>oshima@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
   <summary>
@@ -27127,7 +27139,8 @@
 </histogram>
 
 <histogram name="Clipboard.X11StoreCopyPasteDuration" units="ms"
-    expires_after="M85">
+    expires_after="M90">
+  <owner>dcheng@chromium.org</owner>
   <owner>pkotwicz@chromium.org</owner>
   <summary>
     The length of time that it takes to transfer ownership of Chrome's CLIPBOARD
@@ -69750,6 +69763,7 @@
     Superseded by Installer.Recovery.Reason in 8/2017.
   </obsolete>
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of times the device has been recovered.
 
@@ -80878,6 +80892,14 @@
   </summary>
 </histogram>
 
+<histogram name="Media.Video.Roughness" units="ms" expires_after="2021-01-05">
+  <owner>eugene@chromium.org</owner>
+  <owner>videostack-eng@chromium.org</owner>
+  <summary>
+    Video playback roughness for a 100s interval. Suffixed by the framerate.
+  </summary>
+</histogram>
+
 <histogram name="Media.Video.TimeFromForegroundToFirstFrame" units="ms"
     expires_after="2017-01-19">
   <obsolete>
@@ -111735,7 +111757,7 @@
 </histogram>
 
 <histogram name="OfflineIndicator.CTR" enum="OfflineIndicatorCTREvent"
-    expires_after="2019-11-30">
+    expires_after="2021-01-25">
   <owner>dimich@chromium.org</owner>
   <owner>jianli@chromium.org</owner>
   <summary>
@@ -111847,7 +111869,10 @@
 </histogram>
 
 <histogram name="OfflinePages.Background.BackgroundLoadingFailedCode"
-    enum="NetErrorCodes" expires_after="M85">
+    enum="NetErrorCodes" expires_after="M61">
+  <obsolete>
+    Removed in M61.
+  </obsolete>
   <owner>chili@chromium.org</owner>
   <summary>The error codes that caused a page load failure.</summary>
 </histogram>
@@ -111932,15 +111957,16 @@
 </histogram>
 
 <histogram name="OfflinePages.Background.FinalSavePageResult"
-    enum="OfflinePagesBackgroundSavePageResult" expires_after="M85">
+    enum="OfflinePagesBackgroundSavePageResult" expires_after="2021-06-25">
   <owner>chili@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Status code of background offlining requests at the final step.
   </summary>
 </histogram>
 
 <histogram name="OfflinePages.Background.ImmediateStart.AvailableRequestCount"
-    units="units" expires_after="M77">
+    units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -111952,7 +111978,7 @@
 
 <histogram
     name="OfflinePages.Background.ImmediateStart.AvailableRequestCount.Svelte"
-    units="units" expires_after="M85">
+    units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -111964,7 +111990,7 @@
 
 <histogram
     name="OfflinePages.Background.ImmediateStart.UnavailableRequestCount"
-    units="units" expires_after="M78">
+    units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -111976,7 +112002,7 @@
 
 <histogram
     name="OfflinePages.Background.ImmediateStart.UnavailableRequestCount.Svelte"
-    units="units" expires_after="M85">
+    units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -111987,7 +112013,8 @@
 </histogram>
 
 <histogram name="OfflinePages.Background.ImmediateStartStatus"
-    enum="OfflinePagesBackgroundImmediateStartStatus" expires_after="M77">
+    enum="OfflinePagesBackgroundImmediateStartStatus"
+    expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -111998,8 +112025,9 @@
 </histogram>
 
 <histogram name="OfflinePages.Background.LoadingErrorStatusCode"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="M85">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2021-06-25">
   <owner>chili@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     The combined http and net error codes that caused a page load failure.
   </summary>
@@ -112016,8 +112044,9 @@
 </histogram>
 
 <histogram name="OfflinePages.Background.OffliningPreviewStatus"
-    enum="BooleanEnabled" expires_after="M85">
+    enum="BooleanEnabled" expires_after="2021-01-25">
   <owner>petewil@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Whether any previews were selected for a page we were asked to make
     available offline.
@@ -112025,7 +112054,7 @@
 </histogram>
 
 <histogram name="OfflinePages.Background.RequestFailure.StartedAttemptCount"
-    units="units" expires_after="M77">
+    units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -112034,7 +112063,7 @@
 </histogram>
 
 <histogram name="OfflinePages.Background.RequestSuccess.StartedAttemptCount"
-    units="units" expires_after="M77">
+    units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -112076,13 +112105,14 @@
 </histogram>
 
 <histogram name="OfflinePages.Background.SavePageFromCCT" units="units"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>chili@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>Whether the save page result came from chrome custom tabs.</summary>
 </histogram>
 
 <histogram name="OfflinePages.Background.ScheduledStart.AvailableRequestCount"
-    units="units" expires_after="M77">
+    units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -112094,7 +112124,7 @@
 
 <histogram
     name="OfflinePages.Background.ScheduledStart.AvailableRequestCount.Svelte"
-    units="units" expires_after="M78">
+    units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -112106,7 +112136,7 @@
 
 <histogram
     name="OfflinePages.Background.ScheduledStart.UnavailableRequestCount"
-    units="units" expires_after="M78">
+    units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -112118,7 +112148,7 @@
 
 <histogram
     name="OfflinePages.Background.ScheduledStart.UnavailableRequestCount.Svelte"
-    units="units" expires_after="M78">
+    units="units" expires_after="2021-06-25">
   <owner>dougarnett@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -112215,8 +112245,9 @@
 </histogram>
 
 <histogram name="OfflinePages.CleanupThumbnails.Count" units="thumbnails"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>harringtond@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>Number of thumbnails removed by the thumbnail cleanup task.</summary>
 </histogram>
 
@@ -112264,8 +112295,9 @@
 </histogram>
 
 <histogram name="OfflinePages.ClearStoragePreRunUsage2" units="KiB"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>carlosk@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     The total disk storage size used by all offline pages from a specific client
     namespace.
@@ -112374,8 +112406,9 @@
 </histogram>
 
 <histogram name="OfflinePages.ConsistencyCheck.Legacy.DeletedHeadlessFileCount"
-    units="files" expires_after="M77">
+    units="files" expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Number of files which are deleted during legacy dir clearing since they have
     no associated DB entry and live in private directory.
@@ -112383,8 +112416,9 @@
 </histogram>
 
 <histogram name="OfflinePages.ConsistencyCheck.Persistent.ExpiredEntryCount"
-    units="entries" expires_after="M78">
+    units="entries" expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Number of DB entries (in persistent namespaces) that have been missing their
     files for longer than 365 days, and deleted during maintenance task.
@@ -112392,8 +112426,9 @@
 </histogram>
 
 <histogram name="OfflinePages.ConsistencyCheck.Persistent.MissingFileCount"
-    units="files" expires_after="M77">
+    units="files" expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Number of files that are found missing during maintenance task, which is
     also the number of DB entries that are updated with valid file missing time.
@@ -112429,8 +112464,9 @@
 </histogram>
 
 <histogram name="OfflinePages.ConsistencyCheck.Persistent.ReappearedFileCount"
-    units="files" expires_after="M77">
+    units="files" expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Number of files that were marked as missing reappeared in the file system,
     which is also the number of DB entries that removes file missing time.
@@ -112438,15 +112474,17 @@
 </histogram>
 
 <histogram name="OfflinePages.ConsistencyCheck.Persistent.Result"
-    enum="OfflinePagesSyncOperationResult" expires_after="M77">
+    enum="OfflinePagesSyncOperationResult" expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>Result of persistent page consistency check.</summary>
 </histogram>
 
 <histogram
     name="OfflinePages.ConsistencyCheck.Temporary.PagesMissingArchiveFileCount"
-    units="pages" expires_after="M78">
+    units="pages" expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Number of temporary offline pages without archive file when checking
     consistency. It will only be reported if the number is larger than 0.
@@ -112455,8 +112493,9 @@
 
 <histogram
     name="OfflinePages.ConsistencyCheck.Temporary.PagesMissingDbEntryCount"
-    units="pages" expires_after="M85">
+    units="pages" expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Number of archives without database entry when checking temporary page
     consistency. It will only be reported if the number is larger than 0.
@@ -112464,8 +112503,9 @@
 </histogram>
 
 <histogram name="OfflinePages.ConsistencyCheck.Temporary.Result"
-    enum="OfflinePagesSyncOperationResult" expires_after="M85">
+    enum="OfflinePagesSyncOperationResult" expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>Result of temporary page consistency check.</summary>
 </histogram>
 
@@ -112564,8 +112604,9 @@
 </histogram>
 
 <histogram name="OfflinePages.DeletePageCount"
-    enum="OfflinePagesNamespaceEnumeration" expires_after="M77">
+    enum="OfflinePagesNamespaceEnumeration" expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Counts the number of times an offline page is deleted. Events are reported
     per offline pages namespace.
@@ -112573,8 +112614,9 @@
 </histogram>
 
 <histogram name="OfflinePages.DeletePageResult"
-    enum="OfflinePagesDeletePageResult" expires_after="M82">
+    enum="OfflinePagesDeletePageResult" expires_after="2021-06-25">
   <owner>jianli@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>Result of removing an offline copy for a page.</summary>
 </histogram>
 
@@ -112775,7 +112817,10 @@
 </histogram>
 
 <histogram name="OfflinePages.LoadStatus" enum="OfflinePagesLoadStatus"
-    expires_after="M85">
+    expires_after="M65">
+  <obsolete>
+    Removed in M65.
+  </obsolete>
   <owner>jianli@chromium.org</owner>
   <summary>
     Status code of loading from the offline pages metadata store.
@@ -112926,8 +112971,9 @@
 </histogram>
 
 <histogram base="true" name="OfflinePages.PageAccessInterval" units="minutes"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Length of time between accesses to an offline page. This is the same time
     period used for expiring temporary pages. This metric is recorded when an
@@ -112952,8 +112998,9 @@
 </histogram>
 
 <histogram name="OfflinePages.PageSizeOnAccess.Offline" units="KiB"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>carlosk@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Records the archive size of an offline page loaded during a navigation that
     took place while the device was considered to be offline or with poor
@@ -112962,8 +113009,9 @@
 </histogram>
 
 <histogram name="OfflinePages.PageSizeOnAccess.Online" units="KiB"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>carlosk@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Records the archive size of an offline page loaded during a navigation that
     took place while the device was considered to be online.
@@ -112971,8 +113019,9 @@
 </histogram>
 
 <histogram name="OfflinePages.PrefetchEnabled" enum="BooleanEnabled"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Counts user-days when candidate articles were suggested to the Offline
     Prefetch pipeline while it was enabled. This metric is not recorded to UMA
@@ -113204,8 +113253,9 @@
 </histogram>
 
 <histogram name="OfflinePages.PrefetchUsage" enum="OfflinePagesPrefetchUsage"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     Counts user-days categorized by how Offline Prefetch performed and had its
     content accessed by the user. Buckets are mutually exclusive so that only
@@ -113504,8 +113554,9 @@
 </histogram>
 
 <histogram base="true" name="OfflinePages.SavePageTime" units="ms"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>jianli@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     The amount of time taken to save an offline copy for a page.
   </summary>
@@ -113581,8 +113632,9 @@
 </histogram>
 
 <histogram name="OfflinePages.StorageInfo.ExternalArchiveSizeMiB" units="MiB"
-    expires_after="M77">
+    expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     The storage space used by the archive files of offline pages, including all
     files in public directory with mhtml/mht extensions. Collected after every
@@ -113591,8 +113643,9 @@
 </histogram>
 
 <histogram name="OfflinePages.StorageInfo.ExternalFreeSpaceMiB" units="MiB"
-    expires_after="M77">
+    expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     The amount of free space on the external drive (which contains the public
     download directory). Collected after every time an offline page is saved
@@ -113601,8 +113654,9 @@
 </histogram>
 
 <histogram name="OfflinePages.StorageInfo.ExternalUsagePercentage" units="%"
-    expires_after="M77">
+    expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     The percentage of storage space on the external volume (that contains public
     download directory) used by offline pages archives.
@@ -113610,8 +113664,9 @@
 </histogram>
 
 <histogram name="OfflinePages.StorageInfo.InternalArchiveSizeMiB" units="MiB"
-    expires_after="M77">
+    expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     The storage space used by the archive files of offline pages, including the
     files in temporary and private directory. Collected after every time an
@@ -113620,8 +113675,9 @@
 </histogram>
 
 <histogram name="OfflinePages.StorageInfo.InternalFreeSpaceMiB" units="MiB"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     The amount of free space on the internal drive (which contains the app
     directory). Collected after every time an offline page is saved
@@ -113630,8 +113686,9 @@
 </histogram>
 
 <histogram name="OfflinePages.StorageInfo.InternalUsagePercentage" units="%"
-    expires_after="M85">
+    expires_after="2021-06-25">
   <owner>dimich@chromium.org</owner>
+  <owner>offline-dev@chromium.org</owner>
   <summary>
     The percentage of storage space on internal volume (that contains the app
     directory) used by offline pages archives.
@@ -113732,7 +113789,10 @@
 </histogram>
 
 <histogram name="OfflinePages.Wakeup.NetworkAvailable"
-    enum="NetworkConnectionType" expires_after="M85">
+    enum="NetworkConnectionType" expires_after="M77">
+  <obsolete>
+    Removed in M77.
+  </obsolete>
   <owner>petewil@chromium.org</owner>
   <owner>jianli@chromium.org</owner>
   <summary>
@@ -117072,8 +117132,9 @@
 </histogram>
 
 <histogram name="P2P.Client.Canceled.WaitingTimeSeconds" units="seconds"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The wall-clock time spent until a lookup was canceled. This is reported
     every time p2p is used to find a candidate but the request was canceled.
@@ -117081,8 +117142,9 @@
 </histogram>
 
 <histogram name="P2P.Client.Found.CandidateCount" units="count"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of candidates on the LAN, i.e. the number of peers on the LAN
     offering at least N bytes of the requested file X. This is reported after
@@ -117091,8 +117153,9 @@
 </histogram>
 
 <histogram name="P2P.Client.Found.ConnectionCount" units="count"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of p2p downloads of the peer that the returned URL points to.
     This is reported after examining responses from all peers on the LAN and
@@ -117101,8 +117164,9 @@
 </histogram>
 
 <histogram name="P2P.Client.Found.WaitingTimeSeconds" units="seconds"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The wall-clock time spent waiting for the LAN-wide number of p2p downloads
     (i.e. the sum of p2p downloads from each peer on the LAN) to drop below the
@@ -117112,8 +117176,9 @@
 </histogram>
 
 <histogram name="P2P.Client.LookupResult" enum="P2PLookupResult"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The result of the lookup. Possible values include &quot;Found&quot; (if a
     candidate - i.e. a peer offering at least N bytes of file X - was chosen),
@@ -117126,8 +117191,9 @@
   </summary>
 </histogram>
 
-<histogram name="P2P.Client.NumPeers" units="count" expires_after="M93">
+<histogram name="P2P.Client.NumPeers" units="count" expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of peers implementing p2p file sharing on the network. This is
     reported every time p2p is used to look up a resource on a network where
@@ -117136,8 +117202,9 @@
 </histogram>
 
 <histogram name="P2P.Client.Vanished.WaitingTimeSeconds" units="seconds"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The wall-clock time spent waiting for one or more candidates (i.e. peers
     offering at least N bytes of file X) that all vanished before the LAN-wide
@@ -117148,6 +117215,7 @@
 
 <histogram name="P2P.Server.ClientCount" units="count" expires_after="M85">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of currently connected HTTP clients. This is reported every time
     a HTTP client connects.
@@ -117155,8 +117223,9 @@
 </histogram>
 
 <histogram name="P2P.Server.ContentServedInterruptedMB" units="MB"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     Number of megabytes (1,000,000 bytes) served from the device (via HTTP)
     where the client disconnects prematurely. This is reported every time a file
@@ -117165,32 +117234,38 @@
 </histogram>
 
 <histogram name="P2P.Server.ContentServedSuccessfullyMB" units="MB"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     Number of megabytes (1,000,000 bytes) served from the device (via HTTP).
     This is reported every time a file have been served successfully.
   </summary>
 </histogram>
 
-<histogram name="P2P.Server.DownloadSpeedKBps" units="kB/s" expires_after="M93">
+<histogram name="P2P.Server.DownloadSpeedKBps" units="kB/s"
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The average speed at which the download was served at, in kB/s. This is
     reported every time a file have been served successfully.
   </summary>
 </histogram>
 
-<histogram name="P2P.Server.FileCount" units="count" expires_after="M93">
+<histogram name="P2P.Server.FileCount" units="count" expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of files available via p2p. This is reported every time a file is
     added or removed to the /var/cache/p2p directory.
   </summary>
 </histogram>
 
-<histogram name="P2P.Server.RangeBeginPercentage" units="%" expires_after="M93">
+<histogram name="P2P.Server.RangeBeginPercentage" units="%"
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     When a client resumes a download, the HTTP request includes range specifier
     to skip the bytes it already has. This metric conveys this as a percentage
@@ -117200,8 +117275,9 @@
 </histogram>
 
 <histogram name="P2P.Server.RequestResult" enum="P2PServerResult"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The result of the HTTP request. Possible values include &quot;Response
     Sent&quot; (the resource was found and the response was successfully sent),
@@ -121956,7 +122032,7 @@
 </histogram>
 
 <histogram name="PageSerialization.ProblemDetection.LoadedImagePercentage"
-    units="%" expires_after="M85">
+    units="%" expires_after="2021-06-25">
   <owner>sclittle@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
@@ -126824,8 +126900,9 @@
 </histogram>
 
 <histogram base="true" name="PerformanceMonitor.EnergyImpact"
-    units="ScaledUnits" expires_after="2020-07-30">
+    units="ScaledUnits" expires_after="2021-07-30">
   <owner>lgrey@chromium.org</owner>
+  <owner>markchang@chromium.org</owner>
   <summary>
     (Mac only) A synthetic power use estimate, as displayed in macOS Activity
     Monitor and the battery menu. This incorporates CPU utilization, idle
@@ -132083,7 +132160,7 @@
 
 <histogram name="PluginVm.SetupFailureReason" enum="PluginVmSetupFailureReason"
     expires_after="2020-12-31">
-  <owner>kimjae@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <owner>timloh@chromium.org</owner>
   <summary>Recorded when the Plugin VM installer fails.</summary>
 </histogram>
@@ -145853,7 +145930,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Result"
-    enum="SafeBrowsingV4ApplyUpdateResult" expires_after="M85">
+    enum="SafeBrowsingV4ApplyUpdateResult" expires_after="2021-07-01">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -145954,7 +146031,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4ProcessPartialUpdate.ApplyUpdate.Result"
-    enum="SafeBrowsingV4ApplyUpdateResult" expires_after="M85">
+    enum="SafeBrowsingV4ApplyUpdateResult" expires_after="2021-07-01">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -170888,7 +170965,10 @@
 </histogram>
 
 <histogram name="Sync.DirectoryVsPrefsConsistency"
-    enum="SyncDirectoryVsPrefsConsistency" expires_after="2020-09-27">
+    enum="SyncDirectoryVsPrefsConsistency" expires_after="2020-06-25">
+  <obsolete>
+    Removed 06/2020.
+  </obsolete>
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -180946,8 +181026,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.ConnectionType"
-    enum="UpdateEngineConnectionType" expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEngineConnectionType" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The network connection type when the attempt begins. Possible values include
     &quot;Unknown&quot;, &quot;Ethernet&quot;, &quot;Wifi&quot;,
@@ -180961,8 +181042,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.DownloadErrorCode"
-    enum="UpdateEngineDownloadErrorCode" expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEngineDownloadErrorCode" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     A more detailed description of the last Payload transfer error when
     downloading the payload.
@@ -180975,8 +181057,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.DownloadSource"
-    enum="UpdateEngineDownloadSource" expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEngineDownloadSource" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The download source used, possible values include &quot;HTTPS Server&quot;,
     &quot;HTTP Server&quot; and &quot;HTTP Peer&quot;.
@@ -180988,8 +181071,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.DurationMinutes" units="minutes"
-    expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of minutes the update attempt took including the time the device
     spent sleeping.
@@ -181001,8 +181085,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.DurationUptimeMinutes" units="minutes"
-    expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of minutes the update attempt took excluding the time the device
     spent sleeping.
@@ -181014,8 +181099,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.InternalErrorCode"
-    enum="UpdateEngineErrorCode" expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEngineErrorCode" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     A more detailed description of the last internal error. The possible values
     correspond to the ErrorCode enumeration in the update_engine source code.
@@ -181026,8 +181112,10 @@
   </summary>
 </histogram>
 
-<histogram name="UpdateEngine.Attempt.Number" units="count" expires_after="M85">
-  <owner>senj@chromium.org</owner>
+<histogram name="UpdateEngine.Attempt.Number" units="count"
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The attempt number which starts at 0 for the initial attempt and keeps
     increasing for subsequent attempts.
@@ -181039,8 +181127,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.PayloadBytesDownloadedMiB" units="MiB"
-    expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of payload mebibytes (1048576 bytes) actually download.
 
@@ -181051,8 +181140,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.PayloadDownloadSpeedKBps" units="KBps"
-    expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The payload download speed, in kilobytes per second (1000 bytes/second).
     This is calculated as the number of bytes downloaded divided by the duration
@@ -181065,8 +181155,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.PayloadSizeMiB" units="MiB"
-    expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The payload size, in mebibytes (1048576 bytes).
 
@@ -181077,8 +181168,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.PayloadType"
-    enum="UpdateEnginePayloadFormat" expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEnginePayloadFormat" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The payload type, possible values include &quot;Delta&quot; (if Omaha
     specified to download a delta payload); and &quot;Full&quot; (if Omaha
@@ -181092,8 +181184,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.Result" enum="UpdateEngineAttemptResult"
-    expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The result of the update attempt.
 
@@ -181104,8 +181197,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.TimeSinceLastAttemptMinutes"
-    units="minutes" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    units="minutes" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of minutes since the last attempt including the time the device
     spent sleeping.
@@ -181118,8 +181212,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.TimeSinceLastAttemptUptimeMinutes"
-    units="minutes" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    units="minutes" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of minutes since the last attempt excluding the time the device
     spent sleeping.
@@ -181132,8 +181227,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.CertificateCheck.Download"
-    enum="UpdateEngineCertificateCheckStatus" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEngineCertificateCheckStatus" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The status of the certificate check done when downloading a payload over
     HTTPS. Note that most downloads are done over HTTP.
@@ -181146,8 +181242,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.CertificateCheck.UpdateCheck"
-    enum="UpdateEngineCertificateCheckStatus" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEngineCertificateCheckStatus" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The status of the certificate check done when querying Omaha for a new
     version.
@@ -181159,8 +181256,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Check.DownloadErrorCode"
-    enum="UpdateEngineDownloadErrorCode" expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEngineDownloadErrorCode" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     If unable to download a response from Omaha, a more detailed error code is
     reported in this metric.
@@ -181173,8 +181271,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Check.Reaction" enum="UpdateEngineCheckReaction"
-    expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     If there is an update available, this metric will track what the device does
     with the information. Possible values include &quot;Applying update&quot;,
@@ -181188,8 +181287,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Check.Result" enum="UpdateEngineCheckResult"
-    expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The response from Omaha. Possible values include &quot;No update
     available&quot;, &quot;Update available&quot;, &quot;Download error&quot;,
@@ -181240,8 +181340,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Check.TimeSinceLastCheckMinutes" units="minutes"
-    expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of minutes since the last check including the time the device
     spent sleeping.
@@ -181253,8 +181354,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Check.TimeSinceLastCheckUptimeMinutes"
-    units="minutes" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    units="minutes" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of minutes since the last check excluding the time the device
     spent sleeping.
@@ -181265,8 +181367,10 @@
   </summary>
 </histogram>
 
-<histogram name="UpdateEngine.Daily.OSAgeDays" units="days" expires_after="M85">
-  <owner>senj@chromium.org</owner>
+<histogram name="UpdateEngine.Daily.OSAgeDays" units="days"
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The age of the OS in days, defined as the age of the /etc/lsb-release file.
 
@@ -181313,8 +181417,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.FailedUpdateCount" units="count"
-    expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The number of consecutive times a device has failed to boot an update that
     successfully applied.
@@ -181327,8 +181432,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.InstallDateProvisioningSource"
-    enum="UpdateEngineInstallDateProvisioningSource" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEngineInstallDateProvisioningSource" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The source used to provision the install-date-days value sent to Omaha with
     every request.
@@ -181341,7 +181447,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.KernelKey.MaxRollforwardSetSuccess"
-    enum="BooleanSuccess" expires_after="M93">
+    enum="BooleanSuccess" expires_after="2021-12-29">
   <owner>poromov@chromium.org</owner>
   <summary>
     Whether setting the MaxKernelKeyRollforward value in the TPM succeeded.
@@ -181353,7 +181459,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.KernelKey.MaxRollforwardVersion" units="units"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>poromov@chromium.org</owner>
   <summary>
     Maximum value the device can roll forward the minimum kernel key version
@@ -181372,7 +181478,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.KernelKey.MinVersion" units="units"
-    expires_after="M93">
+    expires_after="2021-12-29">
   <owner>poromov@chromium.org</owner>
   <summary>
     Minimum kernel key version already set in the TPM. This value specifies
@@ -181385,8 +181491,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.Rollback.Result" enum="BooleanSuccess"
-    expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     Whether rollback worked.
 
@@ -181397,8 +181504,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.AttemptCount" units="count"
-    expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The total number of update attempts required to update the device.
 
@@ -181409,8 +181517,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.BytesDownloadedMiB" units="MiB"
-    expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The total number of bytes downloaded in mebibytes (1048576 bytes) using all
     available sources (e.g. HTTP, HTTPS, HTTP Peer).
@@ -181422,8 +181531,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.BytesDownloadedMiBHttpPeer"
-    units="MiB" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    units="MiB" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The total number of bytes downloaded in mebibytes (1048576 bytes) using HTTP
     from a local peer.
@@ -181435,8 +181545,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.BytesDownloadedMiBHttpServer"
-    units="MiB" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    units="MiB" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The total number of bytes downloaded in mebibytes (1048576 bytes) using
     HTTP.
@@ -181448,8 +181559,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.BytesDownloadedMiBHttpsServer"
-    units="MiB" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    units="MiB" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The total number of bytes downloaded in mebibytes (1048576 bytes) using
     HTTPS.
@@ -181461,8 +181573,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.DownloadOverheadPercentage"
-    units="%" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    units="%" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The ratio between bytes downloaded and payload size minus 100.
 
@@ -181473,8 +181586,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.DownloadSourcesUsed"
-    enum="UpdateEngineDownloadSources" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEngineDownloadSources" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The various download sources used - this is a combination of the values
     &quot;HTTPS Server&quot;, &quot;HTTP Server&quot; and &quot;HTTP Peer&quot;.
@@ -181515,8 +181629,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.PayloadSizeMiB" units="MiB"
-    expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The size of the payload, in mebibytes (1048576 bytes).
 
@@ -181527,8 +181642,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.PayloadType"
-    enum="UpdateEnginePayloadFormat" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    enum="UpdateEnginePayloadFormat" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The payload type (&quot;Delta&quot;, &quot;Full&quot;,
     &quot;ForcedFull&quot;) used.
@@ -181540,8 +181656,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.RebootCount" units="count"
-    expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The total number of reboots during the update.
 
@@ -181552,8 +181669,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.TotalDurationMinutes"
-    units="minutes" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    units="minutes" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The total number of minutes from when an update was detected until an update
     (possibly another update) was applied. This includes the time waiting for
@@ -181566,8 +181684,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.TotalDurationUptimeMinutes"
-    units="minutes" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    units="minutes" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The total number of minutes from when an update was detected until an update
     (possibly another update) was applied. This does not include the time
@@ -181580,8 +181699,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.UpdatesAbandonedCount"
-    units="count" expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    units="count" expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The total number of updates that were abandoned since the last successful
     update.
@@ -181593,8 +181713,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.UrlSwitchCount" units="count"
-    expires_after="M93">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The total number of times the URL was switched (from e.g. HTTPS to HTTP)
     because of failures.
@@ -181606,8 +181727,9 @@
 </histogram>
 
 <histogram name="UpdateEngine.TimeToRebootMinutes" units="minutes"
-    expires_after="M85">
-  <owner>senj@chromium.org</owner>
+    expires_after="2021-12-29">
+  <owner>ahassani@chromium.org</owner>
+  <owner>chromeos-core-services@google.com</owner>
   <summary>
     The duration between when an update has successfully completed and the user
     is presented with the &quot;reboot arrow&quot; and when the system has
@@ -213112,6 +213234,15 @@
   <affected-histogram name="WebRTC.Video.Encoded.Qp"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="VideoFrameRateRange" separator=".">
+  <suffix name="24fps" label="24 fps"/>
+  <suffix name="25fps" label="25 fps"/>
+  <suffix name="30fps" label="30 fps"/>
+  <suffix name="50fps" label="50 fps"/>
+  <suffix name="60fps" label="60 fps"/>
+  <affected-histogram name="Media.Video.Roughness"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="Visibility" separator=".">
   <suffix name="Hidden"
       label="The tab is not visible because it is in a minimized window
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index a965ad8..6104bf20 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,16 +1,16 @@
 {
     "trace_processor_shell": {
         "win": {
-            "hash": "e06a787271df733453da9e15a2098cdc208aeb34",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/ef521d3a5eccfccff8c06413214db455750069b9/trace_processor_shell.exe"
+            "hash": "c71caf6617372a9a3e3c8cbabe49b0713dd1af12",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/c047dd609ca72e220715da0648251c4bdcf02931/trace_processor_shell.exe"
         },
         "mac": {
             "hash": "59bcfd46ec4285b59af65f45df89539c43819b6e",
             "remote_path": "perfetto_binaries/trace_processor_shell/mac/5b621a9979038b4671cefd0255803724697d6d0f/trace_processor_shell"
         },
         "linux": {
-            "hash": "769649623930e9f40efa5d79ea01d9a9f341cfba",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/ef521d3a5eccfccff8c06413214db455750069b9/trace_processor_shell"
+            "hash": "c45069d88732b2138fe8c8876ed387a2cf34370a",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/c047dd609ca72e220715da0648251c4bdcf02931/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/aura/window_occlusion_tracker.cc b/ui/aura/window_occlusion_tracker.cc
index fe5146c3..8a13839 100644
--- a/ui/aura/window_occlusion_tracker.cc
+++ b/ui/aura/window_occlusion_tracker.cc
@@ -14,7 +14,6 @@
 #include "ui/aura/window_occlusion_change_builder.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/gfx/transform.h"
 
diff --git a/ui/base/x/x11_window.cc b/ui/base/x/x11_window.cc
index 5f54093..970b02c 100644
--- a/ui/base/x/x11_window.cc
+++ b/ui/base/x/x11_window.cc
@@ -574,9 +574,9 @@
         ->SetInputFocus({x11::InputFocus::Parent, xwindow_,
                          static_cast<x11::Time>(timestamp)})
         .IgnoreError();
-    // At this point, we know we will receive focus, and some
-    // webdriver tests depend on a window being IsActive() immediately
-    // after an Activate(), so just set this state now.
+    // At this point, we know we will receive focus, and some webdriver tests
+    // depend on a window being IsActive() immediately after an Activate(), so
+    // just set this state now.
     has_pointer_focus_ = false;
     has_window_focus_ = true;
     window_mapped_in_server_ = true;
@@ -603,6 +603,7 @@
   // stacking order in addition to changing the focus state.
   return (has_window_focus_ || has_pointer_focus_) && !ignore_keyboard_input_;
 }
+
 void XWindow::SetSize(const gfx::Size& size_in_pixels) {
   connection_->ConfigureWindow({.window = xwindow_,
                                 .width = size_in_pixels.width(),
@@ -1088,12 +1089,6 @@
   DoWMMoveResize(connection_, x_root_window_, xwindow_, location, direction);
 }
 
-// In Ozone, there are no *Event constructors receiving XEvent* as input,
-// in this case PlatformEvent is expected. Furthermore,
-// X11EventSourceLibevent is used in that case, which already translates
-// Mouse/Key/Touch/Scroll events into Events so they should not be handled
-// by PlatformWindow, which is supposed to use XWindow in Ozone builds. So
-// handling these events is disabled for Ozone.
 void XWindow::ProcessEvent(x11::Event* xev) {
   // We can lose track of the window's position when the window is reparented.
   // When the parent window is moved, we won't get an event, so the window's
@@ -1346,14 +1341,13 @@
   }
 }
 
-// Removes |delayed_resize_task_| from the task queue (if it's in
-// the queue) and adds it back at the end of the queue.
+// Removes |delayed_resize_task_| from the task queue (if it's in the queue) and
+// adds it back at the end of the queue.
 void XWindow::DispatchResize() {
   if (update_counter_ == x11::Sync::Counter{} ||
       configure_counter_value_ == 0) {
-    // WM doesn't support _NET_WM_SYNC_REQUEST.
-    // Or we are too slow, so _NET_WM_SYNC_REQUEST is disabled by the
-    // compositor.
+    // WM doesn't support _NET_WM_SYNC_REQUEST. Or we are too slow, so
+    // _NET_WM_SYNC_REQUEST is disabled by the compositor.
     delayed_resize_task_.Reset(base::BindOnce(
         &XWindow::DelayedResize, base::Unretained(this), bounds_in_pixels_));
     base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/ui/events/blink/blink_event_util.cc b/ui/events/blink/blink_event_util.cc
index e9710b2..c53efe7 100644
--- a/ui/events/blink/blink_event_util.cc
+++ b/ui/events/blink/blink_event_util.cc
@@ -26,7 +26,6 @@
 #include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/geometry/angle_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/transform.h"
 
diff --git a/ui/events/gestures/blink/web_gesture_curve_impl.cc b/ui/events/gestures/blink/web_gesture_curve_impl.cc
index 4037df3..1a73efb 100644
--- a/ui/events/gestures/blink/web_gesture_curve_impl.cc
+++ b/ui/events/gestures/blink/web_gesture_curve_impl.cc
@@ -15,7 +15,6 @@
 #include "ui/events/gestures/fling_curve.h"
 #include "ui/events/gestures/physics_based_fling_curve.h"
 #include "ui/events/mobile_scroller.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 
 #if defined(OS_WIN)
 #include "ui/display/win/screen_win.h"
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index f0c40812..8d287147 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -489,7 +489,7 @@
 }
 
 body.files-ng div.splitter:not(.splitter-active) .splitter-button .icon {
-  background-color: var(--google-grey-300);
+  background-color: var(--google-grey-400);
 }
 
 html:not(.col-resize) body.files-ng div.splitter:not(.splitter-active) .splitter-button:hover .icon {
@@ -497,7 +497,7 @@
 }
 
 html.pointer-active body.files-ng div.splitter:not(.splitter-active) .splitter-button:not(:active):hover .icon {
-  background-color: var(--google-grey-300);
+  background-color: var(--google-grey-400);
   cursor: default;
 }
 
diff --git a/ui/gfx/geometry/rect.h b/ui/gfx/geometry/rect.h
index 3ec89df..e089d4db 100644
--- a/ui/gfx/geometry/rect.h
+++ b/ui/gfx/geometry/rect.h
@@ -19,7 +19,6 @@
 #include "base/check.h"
 #include "build/build_config.h"
 #include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/vector2d.h"
 
diff --git a/ui/gfx/geometry/size.cc b/ui/gfx/geometry/size.cc
index 257425a5..14f1a778 100644
--- a/ui/gfx/geometry/size.cc
+++ b/ui/gfx/geometry/size.cc
@@ -16,7 +16,6 @@
 #include "base/numerics/safe_math.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
 
 namespace gfx {
diff --git a/ui/gfx/transform.cc b/ui/gfx/transform.cc
index 58b6621..2b3744b 100644
--- a/ui/gfx/transform.cc
+++ b/ui/gfx/transform.cc
@@ -12,7 +12,6 @@
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/quaternion.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/geometry/vector3d_f.h"
 #include "ui/gfx/rrect_f.h"
 #include "ui/gfx/skia_util.h"
diff --git a/ui/ozone/BUILD.gn b/ui/ozone/BUILD.gn
index ffc391f..e3eb1e1 100644
--- a/ui/ozone/BUILD.gn
+++ b/ui/ozone/BUILD.gn
@@ -122,6 +122,10 @@
     public_deps += [ "//gpu/vulkan" ]
   }
 
+  if (is_fuchsia) {
+    public_deps += [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.images" ]
+  }
+
   visibility += [
     # Everyone should depend on //ui/ozone instead except a handful of
     # things that would otherwise create a cycle.
diff --git a/ui/ozone/platform/scenic/scenic_surface.cc b/ui/ozone/platform/scenic/scenic_surface.cc
index 27a6c19..dc7fd2ec 100644
--- a/ui/ozone/platform/scenic/scenic_surface.cc
+++ b/ui/ozone/platform/scenic/scenic_surface.cc
@@ -35,7 +35,7 @@
   scenic_surface_factory_->RemoveSurface(window_);
 }
 
-void ScenicSurface::SetTextureToNewImagePipe(
+bool ScenicSurface::SetTextureToNewImagePipe(
     fidl::InterfaceRequest<fuchsia::images::ImagePipe2> image_pipe_request) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   uint32_t image_pipe_id = scenic_session_.AllocResourceId();
@@ -49,6 +49,7 @@
       /*requested_presentation_time=*/0,
       /*requested_prediction_span=*/0,
       [](fuchsia::scenic::scheduling::FuturePresentationTimes info) {});
+  return true;
 }
 
 void ScenicSurface::SetTextureToImage(const scenic::Image& image) {
diff --git a/ui/ozone/platform/scenic/scenic_surface.h b/ui/ozone/platform/scenic/scenic_surface.h
index 8f2f977..3264a594 100644
--- a/ui/ozone/platform/scenic/scenic_surface.h
+++ b/ui/ozone/platform/scenic/scenic_surface.h
@@ -35,8 +35,9 @@
   ~ScenicSurface() override;
 
   // Sets the texture of the surface to a new image pipe.
-  void SetTextureToNewImagePipe(
-      fidl::InterfaceRequest<fuchsia::images::ImagePipe2> image_pipe_request);
+  bool SetTextureToNewImagePipe(
+      fidl::InterfaceRequest<fuchsia::images::ImagePipe2> image_pipe_request)
+      override;
 
   // Sets the texture of the surface to an image resource.
   void SetTextureToImage(const scenic::Image& image);
diff --git a/ui/ozone/platform/scenic/sysmem_buffer_collection.cc b/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
index 97b88ca..d320987 100644
--- a/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
+++ b/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
@@ -116,7 +116,8 @@
                           format == gfx::BufferFormat::BGRX_8888;
   bool usage_supported = usage == gfx::BufferUsage::SCANOUT ||
                          usage == gfx::BufferUsage::SCANOUT_CPU_READ_WRITE ||
-                         usage == gfx::BufferUsage::GPU_READ_CPU_READ_WRITE;
+                         usage == gfx::BufferUsage::GPU_READ_CPU_READ_WRITE ||
+                         usage == gfx::BufferUsage::GPU_READ;
   return format_supported && usage_supported;
 }
 
@@ -142,7 +143,7 @@
   if (vk_device == VK_NULL_HANDLE)
     return false;
 
-  size_ = size;
+  min_size_ = size;
   format_ = format;
   usage_ = usage;
   vk_device_ = vk_device;
@@ -165,13 +166,22 @@
     VkDevice vk_device,
     zx::channel token_handle,
     gfx::BufferFormat format,
-    gfx::BufferUsage usage) {
+    gfx::BufferUsage usage,
+    bool force_protected) {
   DCHECK(!collection_);
   DCHECK(!vk_buffer_collection_);
 
+  // Set nominal size of 1x1, which will be used only for
+  // vkSetBufferCollectionConstraintsFUCHSIA(). The actual size of the allocated
+  // buffers is determined by constraints set by other sysmem clients for the
+  // same collection. Size of the Vulkan image is determined by the valus passed
+  // to CreateVkImage().
+  min_size_ = gfx::Size(1, 1);
+
+  vk_device_ = vk_device;
   format_ = format;
   usage_ = usage;
-  vk_device_ = vk_device;
+  is_protected_ = force_protected;
 
   fuchsia::sysmem::BufferCollectionTokenSyncPtr token;
   token.Bind(std::move(token_handle));
@@ -206,18 +216,12 @@
       buffers_info_.settings.image_format_constraints;
 
   // The logic should match LogicalBufferCollection::Allocate().
-  size_t width =
-      RoundUp(std::max(format.min_coded_width, format.required_max_coded_width),
-              format.coded_width_divisor);
-  size_t stride =
-      RoundUp(std::max(static_cast<size_t>(format.min_bytes_per_row),
-                       gfx::RowSizeForBufferFormat(width, format_, 0)),
-              format.bytes_per_row_divisor);
-  size_t height = RoundUp(
-      std::max(format.min_coded_height, format.required_max_coded_height),
-      format.coded_height_divisor);
+  size_t stride = RoundUp(
+      std::max(static_cast<size_t>(format.min_bytes_per_row),
+               gfx::RowSizeForBufferFormat(image_size_.width(), format_, 0)),
+      format.bytes_per_row_divisor);
   size_t plane_offset = buffers_info_.buffers[buffer_index].vmo_usable_start;
-  size_t plane_size = stride * height;
+  size_t plane_size = stride * image_size_.height();
   handle.planes.emplace_back(stride, plane_offset, plane_size,
                              std::move(main_plane_vmo));
 
@@ -421,7 +425,7 @@
   ignore_result(token_channel.release());
 
   VkImageCreateInfo image_create_info;
-  InitializeImageCreateInfo(&image_create_info, size_);
+  InitializeImageCreateInfo(&image_create_info, min_size_);
 
   if (vkSetBufferCollectionConstraintsFUCHSIA(vk_device_, vk_buffer_collection_,
                                               &image_create_info) !=
@@ -446,6 +450,16 @@
   DCHECK_GE(buffers_info_.buffer_count, buffers_for_camping);
   DCHECK(buffers_info_.settings.has_image_format_constraints);
 
+  // The logic should match LogicalBufferCollection::Allocate().
+  const fuchsia::sysmem::ImageFormatConstraints& format =
+      buffers_info_.settings.image_format_constraints;
+  size_t width =
+      RoundUp(std::max(format.min_coded_width, format.required_max_coded_width),
+              format.coded_width_divisor);
+  size_t height = RoundUp(
+      std::max(format.min_coded_height, format.required_max_coded_height),
+      format.coded_height_divisor);
+  image_size_ = gfx::Size(width, height);
   buffer_size_ = buffers_info_.settings.buffer_settings.size_bytes;
   is_protected_ = buffers_info_.settings.buffer_settings.is_secure;
 
@@ -469,7 +483,13 @@
   vk_image_info->samples = VK_SAMPLE_COUNT_1_BIT;
   vk_image_info->tiling =
       is_mappable() ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL;
+
   vk_image_info->usage = VK_IMAGE_USAGE_SAMPLED_BIT;
+  if (usage_ == gfx::BufferUsage::SCANOUT ||
+      usage_ == gfx::BufferUsage::SCANOUT_CPU_READ_WRITE) {
+    vk_image_info->usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+  }
+
   vk_image_info->sharingMode = VK_SHARING_MODE_EXCLUSIVE;
   vk_image_info->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
 }
diff --git a/ui/ozone/platform/scenic/sysmem_buffer_collection.h b/ui/ozone/platform/scenic/sysmem_buffer_collection.h
index 461fb171..3462868 100644
--- a/ui/ozone/platform/scenic/sysmem_buffer_collection.h
+++ b/ui/ozone/platform/scenic/sysmem_buffer_collection.h
@@ -50,7 +50,8 @@
                   VkDevice vk_device,
                   zx::channel token,
                   gfx::BufferFormat format,
-                  gfx::BufferUsage usage);
+                  gfx::BufferUsage usage,
+                  bool force_protected);
 
   // Must not be called more than once.
   void SetOnDeletedCallback(base::OnceClosure on_deleted);
@@ -72,7 +73,7 @@
 
   gfx::SysmemBufferCollectionId id() const { return id_; }
   size_t num_buffers() const { return buffers_info_.buffer_count; }
-  gfx::Size size() const { return size_; }
+  gfx::Size size() const { return image_size_; }
   gfx::BufferFormat format() const { return format_; }
   size_t buffer_size() const {
     return buffers_info_.settings.buffer_settings.size_bytes;
@@ -98,10 +99,11 @@
 
   const gfx::SysmemBufferCollectionId id_;
 
-  gfx::Size size_;
+  // Image size passed to vkSetBufferCollectionConstraintsFUCHSIA(). The actual
+  // buffers size may be larger depending on constraints set by other
+  // sysmem clients. Size of the image is passed to CreateVkImage().
+  gfx::Size min_size_;
 
-  // Valid only for owned buffer collections, i.e. those that  that were
-  // initialized using the first Initialize() methods.
   gfx::BufferFormat format_ = gfx::BufferFormat::RGBA_8888;
   gfx::BufferUsage usage_ = gfx::BufferUsage::GPU_READ_CPU_READ_WRITE;
 
@@ -120,6 +122,7 @@
   // threads.
   THREAD_CHECKER(vulkan_thread_checker_);
 
+  gfx::Size image_size_;
   size_t buffer_size_ = 0;
   bool is_protected_ = false;
 
diff --git a/ui/ozone/platform/scenic/sysmem_buffer_manager.cc b/ui/ozone/platform/scenic/sysmem_buffer_manager.cc
index 74ebe74..b1bdfb23 100644
--- a/ui/ozone/platform/scenic/sysmem_buffer_manager.cc
+++ b/ui/ozone/platform/scenic/sysmem_buffer_manager.cc
@@ -53,10 +53,11 @@
     gfx::SysmemBufferCollectionId id,
     zx::channel token,
     gfx::BufferFormat format,
-    gfx::BufferUsage usage) {
+    gfx::BufferUsage usage,
+    bool force_protected) {
   auto result = base::MakeRefCounted<SysmemBufferCollection>(id);
   if (!result->Initialize(allocator_.get(), vk_device, std::move(token), format,
-                          usage)) {
+                          usage, force_protected)) {
     return nullptr;
   }
   RegisterCollection(result.get());
diff --git a/ui/ozone/platform/scenic/sysmem_buffer_manager.h b/ui/ozone/platform/scenic/sysmem_buffer_manager.h
index 620583f..9bc6fe9b 100644
--- a/ui/ozone/platform/scenic/sysmem_buffer_manager.h
+++ b/ui/ozone/platform/scenic/sysmem_buffer_manager.h
@@ -48,7 +48,8 @@
       gfx::SysmemBufferCollectionId id,
       zx::channel token,
       gfx::BufferFormat format,
-      gfx::BufferUsage usage);
+      gfx::BufferUsage usage,
+      bool force_protected);
 
   scoped_refptr<SysmemBufferCollection> GetCollectionById(
       gfx::SysmemBufferCollectionId id);
diff --git a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
index d4e51ade..9152a959 100644
--- a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
+++ b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
@@ -269,7 +269,7 @@
   auto image = gpu::VulkanImage::Create(
       device_queue, vk_image, vk_device_memory, size, vk_image_info.format,
       vk_image_info.tiling, vk_device_size, 0 /* memory_type_index */,
-      ycbcr_info);
+      ycbcr_info, vk_image_info.flags);
 
   if (image->format() != vk_format) {
     DLOG(ERROR) << "Unexpected format " << vk_format << " vs "
@@ -301,9 +301,13 @@
     zx::channel token,
     gfx::BufferFormat format,
     gfx::BufferUsage usage) {
+  // SCANOUT images must be protected in protected mode.
+  bool force_protected =
+      usage == gfx::BufferUsage::SCANOUT && enforce_protected_memory();
+
   return std::make_unique<SysmemBufferCollectionImpl>(
       sysmem_buffer_manager_->ImportSysmemBufferCollection(
-          device, id, std::move(token), format, usage));
+          device, id, std::move(token), format, usage, force_protected));
 }
 
 }  // namespace ui
diff --git a/ui/ozone/public/platform_window_surface.h b/ui/ozone/public/platform_window_surface.h
index f804dd1f..b316e3e 100644
--- a/ui/ozone/public/platform_window_surface.h
+++ b/ui/ozone/public/platform_window_surface.h
@@ -6,6 +6,11 @@
 #define UI_OZONE_PUBLIC_PLATFORM_WINDOW_SURFACE_H_
 
 #include "base/component_export.h"
+#include "build/build_config.h"
+
+#if defined(OS_FUCHSIA)
+#include <fuchsia/images/cpp/fidl.h>
+#endif  // defined(OS_FUCHSIA)
 
 namespace ui {
 
@@ -28,9 +33,14 @@
  public:
   virtual ~PlatformWindowSurface() {}
 
-  // Note: GL & Vulkan surface are created through the GLOzone &
-  // VulkanImplementation interfaces, respectively.
-  //
+#if defined(OS_FUCHSIA)
+  // Sets the texture of the surface to a new image pipe.
+  virtual bool SetTextureToNewImagePipe(
+      fidl::InterfaceRequest<fuchsia::images::ImagePipe2>
+          image_pipe_request) = 0;
+#endif  // defined(OS_FUCHSIA)
+
+  // Note: GL surface may be created through the GLOzone interface.
   // However, you must still create a PlatformWindowSurface and keep it alive in
   // order to present.
 };
diff --git a/ui/platform_window/x11/x11_window.cc b/ui/platform_window/x11/x11_window.cc
index 6f14897..d6c5d0c6 100644
--- a/ui/platform_window/x11/x11_window.cc
+++ b/ui/platform_window/x11/x11_window.cc
@@ -546,11 +546,10 @@
 #endif
 }
 
-// CheckCanDispatchNextPlatformEvent is called by X11EventSourceLibevent to
-// determine whether X11Window instance (XEventDispatcher implementation) is
-// able to process next translated event sent by it. So, it's done through
-// |handle_next_event_| internal flag, used in subsequent CanDispatchEvent
-// call.
+// CheckCanDispatchNextPlatformEvent is called by X11EventSource so that
+// X11Window (XEventDispatcher implementation) can inspect |xev| and determine
+// whether it should be dispatched by this window once it gets translated into a
+// PlatformEvent.
 void X11Window::CheckCanDispatchNextPlatformEvent(x11::Event* xev) {
   if (is_shutting_down_)
     return;
diff --git a/ui/shell_dialogs/select_file_dialog_lacros.cc b/ui/shell_dialogs/select_file_dialog_lacros.cc
index 98dd934b..ac3ec95 100644
--- a/ui/shell_dialogs/select_file_dialog_lacros.cc
+++ b/ui/shell_dialogs/select_file_dialog_lacros.cc
@@ -72,17 +72,6 @@
     void* params) {
   params_ = params;
 
-  auto* lacros_chrome_service = chromeos::LacrosChromeServiceImpl::Get();
-  // TODO(https://crbug.com/1090587): Move LacrosChromeServiceImpl construction
-  // earlier and remove these checks. This function is racy with lacros-chrome
-  // startup. In practice, however, the remote is bound long before the user
-  // can trigger a select dialog.
-  if (!lacros_chrome_service ||
-      !lacros_chrome_service->select_file_remote().is_bound()) {
-    LOG(ERROR) << "Not connected to ash-chrome.";
-    return;
-  }
-
   lacros::mojom::SelectFileOptionsPtr options =
       lacros::mojom::SelectFileOptions::New();
   options->type = GetMojoType(type);
@@ -90,7 +79,7 @@
   options->default_path = default_path;
 
   // Send request to ash-chrome.
-  lacros_chrome_service->select_file_remote()->Select(
+  chromeos::LacrosChromeServiceImpl::Get()->select_file_remote()->Select(
       std::move(options),
       base::BindOnce(&SelectFileDialogLacros::OnSelected, this));
 }
diff --git a/ui/views/controls/focusable_border.cc b/ui/views/controls/focusable_border.cc
index bb8f04bf..d71c9e1 100644
--- a/ui/views/controls/focusable_border.cc
+++ b/ui/views/controls/focusable_border.cc
@@ -10,7 +10,6 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/native_theme/native_theme.h"
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 51d9ed9..5f1a254d 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -622,6 +622,11 @@
   return state_.context_menu;
 }
 
+void MenuController::SelectItemAndOpenSubmenu(MenuItemView* item) {
+  DCHECK(item);
+  SetSelection(item, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
+}
+
 bool MenuController::OnMousePressed(SubmenuView* source,
                                     const ui::MouseEvent& event) {
   // We should either have no current_mouse_event_target_, or should have a
diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h
index 1bc7e68e..2b3625c 100644
--- a/ui/views/controls/menu/menu_controller.h
+++ b/ui/views/controls/menu/menu_controller.h
@@ -129,7 +129,11 @@
   // selection has not been committed yet.
   views::MenuItemView* GetSelectedMenuItem() { return pending_state_.item; }
 
-  // Get the anchor position which is used to show this menu.
+  // Selects a menu-item and opens its sub-menu (if one exists) if not already
+  // so. Clears any selections within the submenu if it is already open.
+  void SelectItemAndOpenSubmenu(MenuItemView* item);
+
+  // Gets the anchor position which is used to show this menu.
   MenuAnchorPosition GetAnchorPosition() { return state_.anchor; }
 
   // Cancels the current Run. See ExitType for a description of what happens
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index e5752440..1fdd051 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -1123,6 +1123,27 @@
   ResetSelection();
 }
 
+// Tests that the APIs related to the current selected item work correctly.
+TEST_F(MenuControllerTest, CurrentSelectedItem) {
+  SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0));
+  EXPECT_EQ(1, pending_state_item()->GetCommand());
+
+  // Select the first menu-item.
+  DispatchKey(ui::VKEY_HOME);
+  EXPECT_EQ(pending_state_item(), menu_controller()->GetSelectedMenuItem());
+
+  // The API should let the submenu stay open if already so, but clear any
+  // selections within it.
+  EXPECT_TRUE(IsShowing());
+  EXPECT_EQ(1, pending_state_item()->GetCommand());
+  menu_controller()->SelectItemAndOpenSubmenu(menu_item());
+  EXPECT_TRUE(IsShowing());
+  EXPECT_EQ(0, pending_state_item()->GetCommand());
+
+  // Clear references in menu controller to the menu item that is going away.
+  ResetSelection();
+}
+
 // Tests that opening menu and calling SelectByChar works correctly.
 TEST_F(MenuControllerTest, SelectByChar) {
   SetComboboxType(MenuController::ComboboxType::kReadonly);
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.html b/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.html
index d30af96..a9f8f6b 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.html
@@ -13,6 +13,9 @@
       cr-policy-network-indicator-mojo {
         --cr-tooltip-icon-margin-start: var(--cr-controlled-by-spacing);
       }
+      cr-policy-network-indicator-mojo.left {
+        margin-inline-end: var(--cr-controlled-by-spacing);
+      }
       div.property-box {
         width: 100%;
       }
@@ -22,13 +25,20 @@
       <div class="start">
         [[label]]
       </div>
+      <template is="dom-if" if="[[policyOnLeft]]">
+        <cr-policy-network-indicator-mojo class="left"
+            property="[[property]]" tooltip-position="left">
+        </cr-policy-network-indicator-mojo>
+      </template>
       <cr-toggle checked="{{checked}}"
           disabled="[[getDisabled_(disabled, property)]]"
           aria-label$="[[label]]">
       </cr-toggle>
-      <cr-policy-network-indicator-mojo
-          property="[[property]]" tooltip-position="left">
-      </cr-policy-network-indicator-mojo>
+      <template is="dom-if" if="[[!policyOnLeft]]">
+        <cr-policy-network-indicator-mojo
+            property="[[property]]" tooltip-position="left">
+        </cr-policy-network-indicator-mojo>
+      </template>
     </div>
   </template>
   <script src="network_config_toggle.js"></script>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.js b/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.js
index fe2e459..64509b5 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config_toggle.js
@@ -22,6 +22,15 @@
       reflectToAttribute: true,
       notify: true,
     },
+
+    /**
+     * Uses Settings styling when true (policy icon is left of the toggle)
+     */
+    policyOnLeft: {
+      type: Boolean,
+      value: false,
+      reflectToAttribute: true,
+    },
   },
 
   listeners: {
diff --git a/ui/webui/resources/cr_elements/icons.html b/ui/webui/resources/cr_elements/icons.html
index 45ea1fcf..96ffb87e 100644
--- a/ui/webui/resources/cr_elements/icons.html
+++ b/ui/webui/resources/cr_elements/icons.html
@@ -238,12 +238,6 @@
       <g id="star">
         <path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"></path>
       </g>
-      <g id="supervisor-account" viewBox="0 0 48 48">
-        <path d="M0 0h48v48H0z" fill="none"></path>
-        <path
-          d="M33 24c2.76 0 4.98-2.24 4.98-5s-2.22-5-4.98-5c-2.76 0-5 2.24-5 5s2.24 5 5 5zm-15-2c3.31 0 5.98-2.69 5.98-6s-2.67-6-5.98-6c-3.31 0-6 2.69-6 6s2.69 6 6 6zm15 6c-3.67 0-11 1.84-11 5.5V38h22v-4.5c0-3.66-7.33-5.5-11-5.5zm-15-2c-4.67 0-14 2.34-14 7v5h14v-4.5c0-1.7.67-4.67 4.74-6.94C21 26.19 19.31 26 18 26z">
-        </path>
-      </g>
       <g id="sync">
         <path
           d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z">