diff --git a/DEPS b/DEPS
index dcb2363..493c291 100644
--- a/DEPS
+++ b/DEPS
@@ -162,11 +162,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': 'a268dfb74add0aeb299800b4b2d88ed9c8c1ca4c',
+  'skia_revision': 'a4b448831cee38ea9718031b62eaf87851d7c87e',
   # 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': '6c9bdb566f5c66d3f92bb373196709d5d86d4f2e',
+  'v8_revision': '5c64cbb4ac8b7a3ef9a755e7d499a98decfb964c',
   # 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.
@@ -174,11 +174,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '83785d063c61674b40b0d0eb88bc2ef3bb3f202f',
+  'angle_revision': '5d27a69616018803cd2b2b98cd9006f5b2cb994f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '458736449e062d129998df506a802ad23dfafffd',
+  'swiftshader_revision': 'c72c8d769e87f40913bdf82afc3f971c81b7a4f9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -225,7 +225,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': '8e42ececca717dadf1bbe0fc56c92848576af23b',
+  'catapult_revision': '4b46042d2a25254603d33e31bebeddff16227634',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -297,7 +297,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '650859b4200800be3b2970f0af262e1114a40208',
+  'dawn_revision': 'ca0eac314b807170eb60d0cc2f7c97362477058f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -842,7 +842,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7a7555ea7c86aa6622680810fd740b02bd15bc4a',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'b72c39d07778d7f1ee335bd9e5f44d11374384e4',
       'condition': 'checkout_linux',
   },
 
@@ -867,7 +867,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '217195ca2ce80762044c35aac068e446470a6bd0',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ee8d9ce83d7c26d09283efc88d381bd2505bf836',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1246,7 +1246,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '043d1dc002f70f6f02ce69ec503f43e0bde9c41a',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '635d14bb29d09f7bea85a213c051061ce52d1a96',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1414,7 +1414,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'abaae129d9a0c6e1e092067e0b105475df43352e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '10b636138d77e7d89497d5f7fde848a9bc33c648',
+    Var('webrtc_git') + '/src.git' + '@' + '703ea953f0f0c352de47166625db5b4459951ce3',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1455,7 +1455,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@88d42e9dea21417c2a26c49d6737486c807b102a',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@68f2584d937d3efba6eafcf0f47779aaf7c7fc13',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index cf5a5793..1175cf6 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -330,17 +330,21 @@
                            AppsGridViewFolderDelegate* folder_delegate)
     : folder_delegate_(folder_delegate),
       contents_view_(contents_view),
-      bounds_animator_(this),
       page_flip_delay_in_ms_(kPageFlipDelayInMsFullscreen),
       pagination_animation_start_frame_number_(0),
       view_structure_(this) {
   DCHECK(contents_view_);
-  SetPaintToLayer();
-  layer()->SetFillsBoundsOpaquely(false);
+  SetPaintToLayer(ui::LAYER_NOT_DRAWN);
+  layer()->SetMasksToBounds(true);
   // Clip any icons that are outside the grid view's bounds. These icons would
   // otherwise be visible to the user when the grid view is off screen.
   layer()->SetMasksToBounds(true);
 
+  items_container_ = AddChildView(std::make_unique<views::View>());
+  items_container_->SetPaintToLayer();
+  items_container_->layer()->SetFillsBoundsOpaquely(false);
+  bounds_animator_ = std::make_unique<views::BoundsAnimator>(items_container_);
+
   if (!folder_delegate)
     SetBorder(views::CreateEmptyBorder(gfx::Insets(kFadeoutZoneHeight, 0)));
 
@@ -355,11 +359,11 @@
                        : ash::PaginationController::SCROLL_AXIS_VERTICAL,
       base::BindRepeating(&RecordPageSwitcherSourceByEventType),
       IsTabletMode());
-  bounds_animator_.AddObserver(this);
+  bounds_animator_->AddObserver(this);
 }
 
 AppsGridView::~AppsGridView() {
-  bounds_animator_.RemoveObserver(this);
+  bounds_animator_->RemoveObserver(this);
   // Coming here |drag_view_| should already be canceled since otherwise the
   // drag would disappear after the app list got animated away and closed,
   // which would look odd.
@@ -378,7 +382,7 @@
   // ViewHierarchyChanged() during removal, which can lead to double deletes
   // (because ViewHierarchyChanged() may attempt to delete a view that is part
   // way through deletion).
-  bounds_animator_.Cancel();
+  bounds_animator_->Cancel();
 
   view_model_.Clear();
   RemoveAllChildViews(true);
@@ -566,8 +570,8 @@
 
   drag_pointer_ = pointer;
   // Move the view to the front so that it appears on top of other views.
-  ReorderChildView(drag_view_, -1);
-  bounds_animator_.StopAnimatingView(drag_view_);
+  items_container_->ReorderChildView(drag_view_, -1);
+  bounds_animator_->StopAnimatingView(drag_view_);
 
   if (!dragging_for_reparent_item_)
     StartDragAndDropHostDrag(grid_location);
@@ -832,7 +836,7 @@
       new AppListItemView(this, original_drag_view->item(),
                           contents_view_->GetAppListMainView()->view_delegate(),
                           false /* is_in_folder */);
-  AddChildView(view);
+  items_container_->AddChildView(view);
   for (int i = 0; i < view_model_.view_size(); ++i)
     view_model_.view_at(i)->EnsureLayer();
   view->EnsureLayer();
@@ -841,7 +845,9 @@
   // Dragged view should have focus. This also fixed the issue
   // https://crbug.com/834682.
   drag_view_->RequestFocus();
-  drag_view_->SetBoundsRect(drag_view_rect);
+  gfx::Point converted_origin = drag_view_rect.origin();
+  ConvertPointToTarget(this, items_container_, &converted_origin);
+  drag_view_->SetBoundsRect(gfx::Rect(converted_origin, drag_view_rect.size()));
   drag_view_->SetDragUIState();  // Hide the title of the drag_view_.
 
   // Hide the drag_view_ for drag icon proxy when a native drag is responsible
@@ -918,7 +924,7 @@
 }
 
 bool AppsGridView::IsAnimatingView(AppListItemView* view) {
-  return bounds_animator_.IsAnimating(view);
+  return bounds_animator_->IsAnimating(view);
 }
 
 gfx::Size AppsGridView::CalculatePreferredSize() const {
@@ -949,15 +955,32 @@
   if (ignore_layout_)
     return;
 
-  if (bounds_animator_.IsAnimating())
-    bounds_animator_.Cancel();
+  if (bounds_animator_->IsAnimating())
+    bounds_animator_->Cancel();
+
+  // Prepare |page_size| * number-of-pages for |items_container_|, and sets the
+  // origin properly to show the correct page.
+  const gfx::Size page_size = GetTileGridSize();
+  const int pages = pagination_model_.total_pages();
+  const int current_page = pagination_model_.selected_page();
+  if (pagination_controller_->scroll_axis() ==
+      ash::PaginationController::SCROLL_AXIS_HORIZONTAL) {
+    const int page_width =
+        page_size.width() + GetAppListConfig().page_spacing();
+    items_container_->SetBoundsRect(gfx::Rect(-page_width * current_page, 0,
+                                              page_width * pages,
+                                              GetContentsBounds().height()));
+  } else {
+    const int page_height =
+        page_size.height() + GetAppListConfig().page_spacing();
+    items_container_->SetBoundsRect(gfx::Rect(0, -page_height * current_page,
+                                              GetContentsBounds().width(),
+                                              page_height * pages));
+  }
 
   if (GetContentsBounds().IsEmpty())
     return;
 
-  if (presentation_time_recorder_)
-    presentation_time_recorder_->RequestNext();
-
   if (fadeout_layer_delegate_)
     fadeout_layer_delegate_->layer()->SetBounds(layer()->bounds());
 
@@ -1026,7 +1049,7 @@
 
 void AppsGridView::ViewHierarchyChanged(
     const views::ViewHierarchyChangedDetails& details) {
-  if (!details.is_add && details.parent == this) {
+  if (!details.is_add && details.parent == items_container_) {
     // The view being delete should not have reference in |view_model_|.
     CHECK_EQ(-1, view_model_.GetIndexOfView(details.child));
 
@@ -1045,7 +1068,7 @@
         last_ghost_view_ = nullptr;
     }
 
-    bounds_animator_.StopAnimatingView(details.child);
+    bounds_animator_->StopAnimatingView(details.child);
   }
 }
 
@@ -1082,7 +1105,8 @@
   }
 
   gfx::Point point_in_screen = event->location();
-  ConvertPointToScreen(this, &point_in_screen);
+  ConvertPointToScreen(static_cast<views::View*>(event->target()),
+                       &point_in_screen);
 
   switch (event->type()) {
     case ui::ET_MOUSE_PRESSED:
@@ -1177,7 +1201,7 @@
       continue;
     AppListItemView* view = CreateViewForItemAtIndex(i);
     view_model_.Add(view, view_model_.view_size());
-    AddChildView(view);
+    items_container_->AddChildView(view);
   }
   if (!folder_delegate_)
     view_structure_.LoadFromMetadata();
@@ -1242,7 +1266,7 @@
   while (pulsing_blocks_model_.view_size() < desired) {
     PulsingBlockView* view = new PulsingBlockView(GetTotalTileSize(), true);
     pulsing_blocks_model_.Add(view, 0);
-    AddChildView(view);
+    items_container_->AddChildView(view);
   }
 }
 
@@ -1316,11 +1340,17 @@
       pagination_model_.transition();
   const bool is_valid = pagination_model_.is_valid_page(transition.target_page);
 
-  // Transition to previous page means negative offset.
-  const int dir = transition.target_page > current_page ? -1 : 1;
-
-  int x_offset = 0;
-  int y_offset = 0;
+  int multiplier = page_of_view;
+  if (is_valid && abs(transition.target_page - current_page) > 1) {
+    if (page_of_view == transition.target_page) {
+      if (transition.target_page > current_page)
+        multiplier = current_page + 1;
+      else
+        multiplier = current_page - 1;
+    } else if (page_of_view != current_page) {
+      multiplier = -1;
+    }
+  }
 
   if (pagination_controller_->scroll_axis() ==
       ash::PaginationController::SCROLL_AXIS_HORIZONTAL) {
@@ -1328,34 +1358,12 @@
     // tile slot in the next page.
     const int page_width =
         grid_size.width() + GetAppListConfig().page_spacing();
-    if (page_of_view < current_page)
-      x_offset = -page_width;
-    else if (page_of_view > current_page)
-      x_offset = page_width;
-
-    if (is_valid) {
-      if (page_of_view == current_page ||
-          page_of_view == transition.target_page) {
-        x_offset += transition.progress * page_width * dir;
-      }
-    }
-  } else {
-    const int page_height =
-        grid_size.height() + GetAppListConfig().page_spacing();
-    if (page_of_view < current_page)
-      y_offset = -page_height;
-    else if (page_of_view > current_page)
-      y_offset = page_height;
-
-    if (is_valid) {
-      if (page_of_view == current_page ||
-          page_of_view == transition.target_page) {
-        y_offset += transition.progress * page_height * dir;
-      }
-    }
+    return gfx::Vector2d(page_width * multiplier, 0);
   }
 
-  return gfx::Vector2d(x_offset, y_offset);
+  const int page_height =
+      grid_size.height() + GetAppListConfig().page_spacing();
+  return gfx::Vector2d(0, page_height * multiplier);
 }
 
 void AppsGridView::CalculateIdealBoundsForFolder() {
@@ -1393,7 +1401,10 @@
 }
 
 void AppsGridView::AnimateToIdealBounds(AppListItemView* released_drag_view) {
-  const gfx::Rect visible_bounds(GetVisibleBounds());
+  gfx::Rect visible_bounds(GetVisibleBounds());
+  gfx::Point visible_origin = visible_bounds.origin();
+  ConvertPointToTarget(this, items_container_, &visible_origin);
+  visible_bounds.set_origin(visible_origin);
 
   CalculateIdealBoundsForFolder();
   for (int i = 0; i < view_model_.view_size(); ++i) {
@@ -1402,7 +1413,7 @@
       continue;
 
     const gfx::Rect& target = view_model_.ideal_bounds(i);
-    if (bounds_animator_.GetTargetBounds(view) == target)
+    if (bounds_animator_->GetTargetBounds(view) == target)
       continue;
 
     const gfx::Rect& current = view->bounds();
@@ -1414,9 +1425,9 @@
     if (visible && y_diff && y_diff % GetTotalTileSize().height() == 0) {
       AnimationBetweenRows(view, current_visible, current, target_visible,
                            target);
-    } else if (visible || bounds_animator_.IsAnimating(view)) {
-      bounds_animator_.AnimateViewTo(view, target);
-      bounds_animator_.SetAnimationDelegate(
+    } else if (visible || bounds_animator_->IsAnimating(view)) {
+      bounds_animator_->AnimateViewTo(view, target);
+      bounds_animator_->SetAnimationDelegate(
           view,
           std::make_unique<ItemMoveAnimationDelegate>(
               view, view == released_drag_view /* is_released_drag_view */));
@@ -1463,9 +1474,9 @@
   if (animate_target)
     target_in.Offset(-dir * total_tile_size.width(), 0);
   view->SetBoundsRect(target_in);
-  bounds_animator_.AnimateViewTo(view, target);
+  bounds_animator_->AnimateViewTo(view, target);
 
-  bounds_animator_.SetAnimationDelegate(
+  bounds_animator_->SetAnimationDelegate(
       view, std::make_unique<RowMoveAnimationDelegate>(view, layer.release(),
                                                        current_out));
 }
@@ -1993,8 +2004,8 @@
   float opacity = 0.f;
   for (size_t i = 0; i < current_page.size(); i += cols_) {
     AppListItemView* item_view = current_page[i];
-    gfx::Rect view_bounds = item_view->bounds();
-    views::View::ConvertRectToScreen(this, &view_bounds);
+    gfx::Rect view_bounds = item_view->GetLocalBounds();
+    views::View::ConvertRectToScreen(item_view, &view_bounds);
     centerline_above_work_area = std::max<float>(
         app_list_view->GetScreenBottom() - view_bounds.CenterPoint().y(), 0.f);
     const float start_px = GetAppListConfig().all_apps_opacity_start_px();
@@ -2038,7 +2049,7 @@
                           contents_view_->GetAppListMainView()->view_delegate(),
                           false /* is_in_folder */);
 
-  AddChildView(reparented_view_in_root_grid);
+  items_container_->AddChildView(reparented_view_in_root_grid);
   view_model_.Add(reparented_view_in_root_grid, view_model_.view_size());
   view_structure_.Add(reparented_view_in_root_grid, GetLastTargetIndex());
 
@@ -2221,7 +2232,7 @@
     view_structure_.Move(item_view, target, clear_overflow);
 
   // Reorder the app list item views in accordance with |view_model_|.
-  ReorderChildView(item_view, target_model_index);
+  items_container_->ReorderChildView(item_view, target_model_index);
 
   if (target_item_list_index == current_item_list_index)
     return;
@@ -2275,7 +2286,8 @@
                                             target_model_index)
                              ? 1
                              : 0;
-      AddChildViewAt(target_view, target_model_index - offset);
+      items_container_->AddChildViewAt(target_view,
+                                       target_model_index - offset);
     } else {
       LOG(ERROR) << "Folder no longer in item_list: " << folder_item_id;
     }
@@ -2294,8 +2306,8 @@
   view_model_.Remove(model_index);
   if (!folder_delegate_)
     view_structure_.Remove(item_view);
-  bounds_animator_.AnimateViewTo(item_view, item_view->bounds());
-  bounds_animator_.SetAnimationDelegate(
+  bounds_animator_->AnimateViewTo(item_view, item_view->bounds());
+  bounds_animator_->SetAnimationDelegate(
       item_view, std::unique_ptr<gfx::AnimationDelegate>(
                      new ItemRemoveAnimationDelegate(item_view)));
 }
@@ -2351,7 +2363,7 @@
   view_model_.Move(current_model_index, target_model_index);
   if (!folder_delegate_)
     view_structure_.Move(item_view, target_override);
-  ReorderChildView(item_view, target_model_index);
+  items_container_->ReorderChildView(item_view, target_model_index);
 
   RemoveLastItemFromReparentItemFolderIfNecessary(source_folder_id);
 
@@ -2419,7 +2431,7 @@
       view_model_.Add(new_folder_view, target_model_index);
       if (!folder_delegate_)
         view_structure_.Add(new_folder_view, target_index);
-      AddChildViewAt(new_folder_view, target_model_index);
+      items_container_->AddChildViewAt(new_folder_view, target_model_index);
     } else {
       LOG(ERROR) << "Folder no longer in item_list: " << new_folder_id;
     }
@@ -2434,8 +2446,8 @@
   view_model_.Remove(drag_model_index);
   if (!folder_delegate_)
     view_structure_.Remove(drag_view_);
-  bounds_animator_.AnimateViewTo(drag_view_, drag_view_->bounds());
-  bounds_animator_.SetAnimationDelegate(
+  bounds_animator_->AnimateViewTo(drag_view_, drag_view_->bounds());
+  bounds_animator_->SetAnimationDelegate(
       drag_view_, std::unique_ptr<gfx::AnimationDelegate>(
                       new ItemRemoveAnimationDelegate(drag_view_)));
   UpdatePaging();
@@ -2489,7 +2501,7 @@
   view_model_.Add(last_item_view, target_model_index);
   if (!folder_delegate_)
     view_structure_.Add(last_item_view, target_index);
-  AddChildViewAt(last_item_view, target_model_index);
+  items_container_->AddChildViewAt(last_item_view, target_model_index);
 }
 
 void AppsGridView::CancelContextMenusOnCurrentPage() {
@@ -2580,7 +2592,7 @@
     AppListItemView* view = CreateViewForItemAtIndex(index);
     int model_index = GetTargetModelIndexFromItemIndex(index);
     view_model_.Add(view, model_index);
-    AddChildViewAt(view, model_index);
+    items_container_->AddChildViewAt(view, model_index);
   }
 
   if (!folder_delegate_)
@@ -2621,7 +2633,8 @@
     int from_model_index = GetModelIndexOfItem(item);
     int to_model_index = GetTargetModelIndexFromItemIndex(to_index);
     view_model_.Move(from_model_index, to_model_index);
-    ReorderChildView(view_model_.view_at(to_model_index), to_model_index);
+    items_container_->ReorderChildView(view_model_.view_at(to_model_index),
+                                       to_model_index);
   }
 
   if (!folder_delegate_)
@@ -2644,7 +2657,25 @@
 void AppsGridView::TotalPagesChanged() {}
 
 void AppsGridView::SelectedPageChanged(int old_selected, int new_selected) {
+  items_container_->layer()->SetTransform(gfx::Transform());
   if (dragging()) {
+    drag_view_->layer()->SetTransform(gfx::Transform());
+
+    // Sets the transform to locate the scrolled content.
+    gfx::Size grid_size = GetTileGridSize();
+    gfx::Vector2d update;
+    if (pagination_controller_->scroll_axis() ==
+        ash::PaginationController::SCROLL_AXIS_HORIZONTAL) {
+      const int page_width =
+          grid_size.width() + GetAppListConfig().page_spacing();
+      update.set_x(page_width * (new_selected - old_selected));
+    } else {
+      const int page_height =
+          grid_size.height() + GetAppListConfig().page_spacing();
+      update.set_y(page_height * (new_selected - old_selected));
+    }
+    drag_view_start_ += update;
+    drag_view_->SetPosition(drag_view_->origin() + update);
     UpdateDropTargetRegion();
     Layout();
     MaybeStartPageFlipTimer(last_drag_point_);
@@ -2674,13 +2705,48 @@
       GetCompositorActivatedFrameCount(layer()->GetCompositor());
 }
 
+void AppsGridView::TransitionStarted() {
+  if (abs(pagination_model_.transition().target_page -
+          pagination_model_.selected_page()) > 1) {
+    Layout();
+  }
+}
+
 void AppsGridView::TransitionChanged() {
-  // Update layout for valid page transition only since over-scroll no longer
-  // animates app icons.
   const ash::PaginationModel::Transition& transition =
       pagination_model_.transition();
-  if (pagination_model_.is_valid_page(transition.target_page))
-    Layout();
+  if (!pagination_model_.is_valid_page(transition.target_page))
+    return;
+
+  // Sets the transform to locate the scrolled content.
+  gfx::Size grid_size = GetTileGridSize();
+  gfx::Vector2d translate;
+  const int dir =
+      transition.target_page > pagination_model_.selected_page() ? -1 : 1;
+  if (pagination_controller_->scroll_axis() ==
+      ash::PaginationController::SCROLL_AXIS_HORIZONTAL) {
+    const int page_width =
+        grid_size.width() + GetAppListConfig().page_spacing();
+    translate.set_x(page_width * transition.progress * dir);
+  } else {
+    const int page_height =
+        grid_size.height() + GetAppListConfig().page_spacing();
+    translate.set_y(page_height * transition.progress * dir);
+  }
+  gfx::Transform transform;
+  transform.Translate(translate);
+  items_container_->layer()->SetTransform(transform);
+
+  // |drag_view_| should stay in the same location in the screen, so makes
+  // the opposite effect of the transform.
+  if (drag_view_) {
+    gfx::Transform drag_view_transform;
+    drag_view_transform.Translate(-translate);
+    drag_view_->layer()->SetTransform(drag_view_transform);
+  }
+
+  if (presentation_time_recorder_)
+    presentation_time_recorder_->RequestNext();
 }
 
 void AppsGridView::TransitionEnded() {
@@ -2801,6 +2867,8 @@
   // Calculate the original bound of the tile at |index|.
   gfx::Rect tile_rect =
       GetExpectedTileBounds(GridIndex(pagination_model_.selected_page(), slot));
+  tile_rect.Offset(
+      CalculateTransitionOffset(pagination_model_.selected_page()));
 
   for (int i = 0; i < view_model_.view_size(); ++i) {
     AppListItemView* view = GetItemViewAt(i);
@@ -3137,18 +3205,6 @@
     pulsing_blocks_model_.set_ideal_bounds(i, tile_slot);
     ++pulsing_block_index.slot;
   }
-
-  // Ensure GhostImageView's transition during page change.
-  if (app_list_features::IsAppGridGhostEnabled()) {
-    if (current_ghost_view_) {
-      current_ghost_view_->SetTransitionOffset(
-          CalculateTransitionOffset(current_ghost_view_->page()));
-    }
-    if (last_ghost_view_) {
-      last_ghost_view_->SetTransitionOffset(
-          CalculateTransitionOffset(last_ghost_view_->page()));
-    }
-  }
 }
 
 int AppsGridView::GetModelIndexOfItem(const AppListItem* item) {
@@ -3250,7 +3306,7 @@
   // Start animation.
   TopIconAnimationView* animation_view = new TopIconAnimationView(
       this, drag_item->icon(), base::string16(), target_bounds, false, true);
-  AddChildView(animation_view);
+  items_container_->AddChildView(animation_view);
   animation_view->SetBoundsRect(source_bounds);
   animation_view->AddObserver(
       new FolderDroppingAnimationObserver(model_, folder_item->id()));
@@ -3362,9 +3418,11 @@
   current_ghost_view_ =
       new GhostImageView(IsFolderItem(drag_view_->item()) /* is_folder */,
                          folder_delegate_, reorder_placeholder_.page);
-  current_ghost_view_->Init(drag_view_,
-                            GetExpectedTileBounds(reorder_placeholder_));
-  AddChildView(current_ghost_view_);
+  gfx::Rect ghost_view_bounds = GetExpectedTileBounds(reorder_placeholder_);
+  ghost_view_bounds.Offset(
+      CalculateTransitionOffset(reorder_placeholder_.page));
+  current_ghost_view_->Init(drag_view_, ghost_view_bounds);
+  items_container_->AddChildView(current_ghost_view_);
   current_ghost_view_->FadeIn();
 }
 
@@ -3391,7 +3449,8 @@
     if (event.IsGestureEvent())
       return event.AsGestureEvent()->details().scroll_y_hint();
     gfx::Point screen_location = event.location();
-    ConvertPointToScreen(this, &screen_location);
+    ConvertPointToScreen(static_cast<views::View*>(event.target()),
+                         &screen_location);
     return screen_location.y() - mouse_drag_start_point_.y();
   };
   if (!folder_delegate_ &&
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index 5cf7458..b9d9f86b 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -47,6 +47,7 @@
 namespace app_list {
 
 namespace test {
+class AppsGridViewTest;
 class AppsGridViewTestApi;
 }
 
@@ -323,6 +324,7 @@
  private:
   class FadeoutLayerDelegate;
   friend class test::AppsGridViewTestApi;
+  friend class test::AppsGridViewTest;
   friend class PagedViewStructure;
 
   enum DropTargetRegion {
@@ -487,6 +489,7 @@
   void TotalPagesChanged() override;
   void SelectedPageChanged(int old_selected, int new_selected) override;
   void TransitionStarting() override;
+  void TransitionStarted() override;
   void TransitionChanged() override;
   void TransitionEnded() override;
   void ScrollStarted() override;
@@ -701,6 +704,9 @@
   // Created by AppListMainView, owned by views hierarchy.
   ContentsView* contents_view_ = nullptr;
 
+  // Keeps the individual AppListItemView. Owned by views hierarchy.
+  views::View* items_container_ = nullptr;
+
   int cols_ = 0;
   int rows_per_page_ = 0;
 
@@ -770,7 +776,7 @@
   // Target page to switch to when |page_flip_timer_| fires.
   int page_flip_target_ = -1;
 
-  views::BoundsAnimator bounds_animator_;
+  std::unique_ptr<views::BoundsAnimator> bounds_animator_;
 
   // The most recent activated folder item view.
   AppListItemView* activated_folder_item_view_ = nullptr;
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index 799ddcef..7ca3e49 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -203,7 +203,10 @@
   AppListItemView* GetItemViewForPoint(const gfx::Point& point) const {
     for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i) {
       AppListItemView* view = GetItemViewAt(i);
-      if (view->bounds().Contains(point))
+      gfx::Point view_origin = view->origin();
+      views::View::ConvertPointToTarget(view->parent(), apps_grid_view_,
+                                        &view_origin);
+      if (gfx::Rect(view_origin, view->size()).Contains(point))
         return view;
     }
     return nullptr;
@@ -272,15 +275,24 @@
     const views::ViewModelT<AppListItemView>* view_model =
         apps_grid_view_->view_model();
     DCHECK_GT(view_model->view_size(), 0);
-    auto app_iter = apps_grid_view_->FindChild(view_model->view_at(0));
-    DCHECK(app_iter != apps_grid_view_->children().cend());
+    views::View* items_container = apps_grid_view_->items_container_;
+    auto app_iter = items_container->FindChild(view_model->view_at(0));
+    DCHECK(app_iter != items_container->children().cend());
     for (int i = 1; i < view_model->view_size(); ++i) {
       ++app_iter;
-      ASSERT_NE(apps_grid_view_->children().cend(), app_iter);
+      ASSERT_NE(items_container->children().cend(), app_iter);
       EXPECT_EQ(view_model->view_at(i), *app_iter);
     }
   }
 
+  gfx::Point GetDragViewCenter() {
+    gfx::Point drag_view_center =
+        apps_grid_view_->drag_view()->GetLocalBounds().CenterPoint();
+    views::View::ConvertPointToTarget(apps_grid_view_->drag_view(),
+                                      apps_grid_view_, &drag_view_center);
+    return drag_view_center;
+  }
+
   AppListView* app_list_view_ = nullptr;    // Owned by native widget.
   AppsGridView* apps_grid_view_ = nullptr;  // Owned by |app_list_view_|.
   ContentsView* contents_view_ = nullptr;   // Owned by |app_list_view_|.
@@ -1618,6 +1630,8 @@
   page_flip_waiter.Reset();
   SimulateDrag(AppsGridView::MOUSE, from, to);
 
+  EXPECT_EQ(to, GetDragViewCenter());
+
   // Page should be flipped after sometime to hit page 1 and 2 then stop.
   while (test_api_->HasPendingPageFlip()) {
     page_flip_waiter.Wait();
@@ -1627,6 +1641,7 @@
   // created at the end.
   EXPECT_EQ("1,2,3", page_flip_waiter.selected_pages());
   EXPECT_EQ(3, GetPaginationModel()->selected_page());
+  EXPECT_EQ(to, GetDragViewCenter());
 
   // Cancel drag and put the dragged view back to its ideal position so that
   // the next drag would pick it up.
@@ -1639,12 +1654,15 @@
   page_flip_waiter.Reset();
   SimulateDrag(AppsGridView::MOUSE, from, to);
 
+  EXPECT_EQ(to, GetDragViewCenter());
+
   while (test_api_->HasPendingPageFlip()) {
     page_flip_waiter.Wait();
   }
 
   EXPECT_EQ("1,0", page_flip_waiter.selected_pages());
   EXPECT_EQ(0, GetPaginationModel()->selected_page());
+  EXPECT_EQ(to, GetDragViewCenter());
 
   apps_grid_view_->EndDrag(true);
 }
diff --git a/ash/app_list/views/test/apps_grid_view_test_api.cc b/ash/app_list/views/test/apps_grid_view_test_api.cc
index a6d4223..60b6568 100644
--- a/ash/app_list/views/test/apps_grid_view_test_api.cc
+++ b/ash/app_list/views/test/apps_grid_view_test_api.cc
@@ -69,7 +69,7 @@
     view_->folder_dropping_timer_.Stop();
     view_->OnFolderDroppingTimer();
   }
-  view_->bounds_animator_.Cancel();
+  view_->bounds_animator_->Cancel();
   view_->Layout();
 }
 
@@ -119,7 +119,7 @@
 }
 
 void AppsGridViewTestApi::WaitForItemMoveAnimationDone() {
-  BoundsAnimatorWaiter waiter(&view_->bounds_animator_);
+  BoundsAnimatorWaiter waiter(view_->bounds_animator_.get());
   waiter.Wait();
 }
 
diff --git a/ash/public/cpp/app_list/app_list_features.cc b/ash/public/cpp/app_list/app_list_features.cc
index 7241f36..0d19a3c 100644
--- a/ash/public/cpp/app_list/app_list_features.cc
+++ b/ash/public/cpp/app_list/app_list_features.cc
@@ -43,6 +43,10 @@
     "EnableAppListLaunchRecording", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kEnableSearchBoxSelection{"EnableSearchBoxSelection",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kEnableAggregatedMlAppRanking{
+    "EnableAggregatedMlAppRanking", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kScalableAppList{"ScalableAppList",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
 bool IsAnswerCardEnabled() {
   // Not using local static variable to allow tests to change this value.
@@ -108,6 +112,14 @@
   return base::FeatureList::IsEnabled(kEnableSearchBoxSelection);
 }
 
+bool IsAggregatedMlAppRankingEnabled() {
+  return base::FeatureList::IsEnabled(kEnableAggregatedMlAppRanking);
+}
+
+bool IsScalableAppListEnabled() {
+  return base::FeatureList::IsEnabled(kScalableAppList);
+}
+
 std::string AnswerServerUrl() {
   const std::string experiment_url =
       base::GetFieldTrialParamValueByFeature(kEnableAnswerCard, "ServerUrl");
diff --git a/ash/public/cpp/app_list/app_list_features.h b/ash/public/cpp/app_list/app_list_features.h
index d48b73b..5acdb603 100644
--- a/ash/public/cpp/app_list/app_list_features.h
+++ b/ash/public/cpp/app_list/app_list_features.h
@@ -71,6 +71,14 @@
 
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableSearchBoxSelection;
 
+// Enables using the aggregated Ml model to rank suggested apps.
+ASH_PUBLIC_EXPORT extern const base::Feature kEnableAggregatedMlAppRanking;
+
+// If enabled, app list will support separate configurations (for app list items
+// sizing and spacing) for smaller screens (instead of a single configuration
+// that optionally gets scaled down).
+ASH_PUBLIC_EXPORT extern const base::Feature kScalableAppList;
+
 bool ASH_PUBLIC_EXPORT IsAnswerCardEnabled();
 bool ASH_PUBLIC_EXPORT IsBackgroundBlurEnabled();
 bool ASH_PUBLIC_EXPORT IsPlayStoreAppSearchEnabled();
@@ -87,6 +95,8 @@
 bool ASH_PUBLIC_EXPORT IsAppGridGhostEnabled();
 bool ASH_PUBLIC_EXPORT IsAppListLaunchRecordingEnabled();
 bool ASH_PUBLIC_EXPORT IsSearchBoxSelectionEnabled();
+bool ASH_PUBLIC_EXPORT IsAggregatedMlAppRankingEnabled();
+bool ASH_PUBLIC_EXPORT IsScalableAppListEnabled();
 
 std::string ASH_PUBLIC_EXPORT AnswerServerUrl();
 std::string ASH_PUBLIC_EXPORT AnswerServerQuerySuffix();
diff --git a/ash/style/ash_color_provider.cc b/ash/style/ash_color_provider.cc
index 5c3c666..8504a1a 100644
--- a/ash/style/ash_color_provider.cc
+++ b/ash/style/ash_color_provider.cc
@@ -228,6 +228,10 @@
     case ContentLayerType::kIconSecondary:
       light_color = dark_color = gfx::kGoogleGrey500;
       break;
+    case ContentLayerType::kIconRed:
+      light_color = gfx::kGoogleRed600;
+      dark_color = gfx::kGoogleRed300;
+      break;
   }
   return color_mode == AshColorMode::kLight ? light_color : dark_color;
 }
diff --git a/ash/style/ash_color_provider.h b/ash/style/ash_color_provider.h
index 7dcb323..b679230 100644
--- a/ash/style/ash_color_provider.h
+++ b/ash/style/ash_color_provider.h
@@ -69,6 +69,7 @@
     kTextSecondary,
     kIconPrimary,
     kIconSecondary,
+    kIconRed,
   };
 
   // Attributes of ripple, includes the base color, opacity of inkdrop and
diff --git a/ash/system/accessibility/autoclick_scroll_view.cc b/ash/system/accessibility/autoclick_scroll_view.cc
index 77e82020..3dd5ae0 100644
--- a/ash/system/accessibility/autoclick_scroll_view.cc
+++ b/ash/system/accessibility/autoclick_scroll_view.cc
@@ -9,10 +9,10 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/default_color_constants.h"
 #include "ash/system/accessibility/autoclick_menu_bubble_controller.h"
 #include "ash/system/unified/custom_shape_button.h"
 #include "ash/system/unified/top_shortcut_button.h"
+#include "ash/system/unified/unified_system_tray_view.h"
 #include "base/macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/timer/timer.h"
@@ -38,12 +38,11 @@
 constexpr int kScrollPadButtonHypotenuseDips = 192;
 constexpr int kScrollPadIconPadding = 30;
 
-// TODO(crbug.com/982950): Get color from AshColorProvider::GetRippleAttributes
-// instead.
 SkColor HoveredButtonColor() {
-  return AshColorProvider::Get()->DeprecatedGetControlsLayerColor(
-      AshColorProvider::ControlsLayerType::kInactiveControlBackground,
-      SkColorSetA(kUnifiedMenuButtonColor, 0x29));
+  const AshColorProvider::RippleAttributes attributes =
+      AshColorProvider::Get()->GetRippleAttributes(
+          UnifiedSystemTrayView::GetBackgroundColor());
+  return SkColorSetA(attributes.base_color, 255 * attributes.highlight_opacity);
 }
 
 }  // namespace
diff --git a/ash/system/network/network_feature_pod_button.cc b/ash/system/network/network_feature_pod_button.cc
index ee1756c..0dbeb00 100644
--- a/ash/system/network/network_feature_pod_button.cc
+++ b/ash/system/network/network_feature_pod_button.cc
@@ -193,15 +193,25 @@
       ->GetConnectionStatusStrings(ActiveNetworkIcon::Type::kSingle,
                                    /*a11y_name=*/nullptr,
                                    /*a11y_desc=*/nullptr, &tooltip);
-  SetTooltipState(tooltip);
+  UpdateTooltip(tooltip);
 }
 
-void NetworkFeaturePodButton::SetTooltipState(
-    const base::string16& tooltip_state) {
+void NetworkFeaturePodButton::UpdateTooltip(
+    const base::string16& connection_state_message) {
+  // When the button is enabled, use tooltips to alert the user of the actions
+  // that will be taken when interacting with the button/toggle. However, if the
+  // button is disabled, those actions cannot be taken, so simply display the
+  // state of the connection as a tooltip
+  if (!GetEnabled()) {
+    SetIconTooltip(connection_state_message);
+    SetLabelTooltip(connection_state_message);
+    return;
+  }
+
   SetIconTooltip(l10n_util::GetStringFUTF16(
-      IDS_ASH_STATUS_TRAY_NETWORK_TOGGLE_TOOLTIP, tooltip_state));
+      IDS_ASH_STATUS_TRAY_NETWORK_TOGGLE_TOOLTIP, connection_state_message));
   SetLabelTooltip(l10n_util::GetStringFUTF16(
-      IDS_ASH_STATUS_TRAY_NETWORK_SETTINGS_TOOLTIP, tooltip_state));
+      IDS_ASH_STATUS_TRAY_NETWORK_SETTINGS_TOOLTIP, connection_state_message));
 }
 
 }  // namespace ash
diff --git a/ash/system/network/network_feature_pod_button.h b/ash/system/network/network_feature_pod_button.h
index 9a738ed..5d30cf4 100644
--- a/ash/system/network/network_feature_pod_button.h
+++ b/ash/system/network/network_feature_pod_button.h
@@ -20,6 +20,10 @@
   explicit NetworkFeaturePodButton(FeaturePodControllerBase* controller);
   ~NetworkFeaturePodButton() override;
 
+  // Updates the button's icon and tooltip based on the current state of the
+  // system.
+  void Update();
+
   // network_icon::AnimationObserver:
   void NetworkIconChanged() override;
 
@@ -30,8 +34,7 @@
   const char* GetClassName() const override;
 
  private:
-  void Update();
-  void SetTooltipState(const base::string16& tooltip_state);
+  void UpdateTooltip(const base::string16& connection_state_message);
 
   DISALLOW_COPY_AND_ASSIGN(NetworkFeaturePodButton);
 };
diff --git a/ash/system/network/network_feature_pod_controller.cc b/ash/system/network/network_feature_pod_controller.cc
index 1749a7df..dfe936f 100644
--- a/ash/system/network/network_feature_pod_controller.cc
+++ b/ash/system/network/network_feature_pod_controller.cc
@@ -82,6 +82,7 @@
   SessionControllerImpl* session_controller =
       Shell::Get()->session_controller();
   button_->SetEnabled(!session_controller->IsScreenLocked());
+  button_->Update();
 }
 
 }  // namespace ash
diff --git a/ash/system/network/network_feature_pod_controller.h b/ash/system/network/network_feature_pod_controller.h
index a7ebd36..ec9dfde 100644
--- a/ash/system/network/network_feature_pod_controller.h
+++ b/ash/system/network/network_feature_pod_controller.h
@@ -11,6 +11,7 @@
 
 namespace ash {
 
+class NetworkFeaturePodButton;
 class UnifiedSystemTrayController;
 
 // Controller of network feature pod button.
@@ -30,7 +31,7 @@
 
   // Unowned.
   UnifiedSystemTrayController* tray_controller_;
-  FeaturePodButton* button_ = nullptr;
+  NetworkFeaturePodButton* button_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkFeaturePodController);
 };
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index bcd816b..1d0ed74 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -8,13 +8,14 @@
 #include "ash/ash_export.h"
 #include "base/macros.h"
 #include "chromeos/constants/chromeos_switches.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace ash {
 
+// Do not add constant colors in this file. Get the colors from AshColorProvider
+// instead.
+
 // The size delta between the default font and the font size found in tray
 // items like labels and buttons.
 extern const int kTrayTextFontSizeIncrease;
@@ -71,8 +72,7 @@
 // in tray detailed views.
 constexpr int kTraySeparatorWidth = 0;
 
-// The size and foreground color of the icons appearing in the material design
-// system tray.
+// The size of the icons appearing in the material design system tray.
 constexpr int kTrayIconSize = 16;
 extern const int kTrayIconBackgroundAlpha;
 
@@ -80,8 +80,7 @@
 constexpr int kTrayNetworkIconPadding = 2;
 constexpr int kUnifiedTrayNetworkIconPadding = 4;
 
-// The size and foreground color of the icons appearing in the material design
-// system menu.
+// The size of the icons appearing in the material design system menu.
 extern const int kMenuIconSize;
 // The size of buttons in the system menu.
 ASH_EXPORT extern const int kMenuButtonSize;
@@ -100,7 +99,6 @@
 extern const int kTrayPopupInkDropCornerRadius;
 
 constexpr float kUnifiedMenuBackgroundBlur = 30.f;
-constexpr SkColor kUnifiedRecordingIconColor = gfx::kGoogleRed300;
 
 constexpr gfx::Insets kUnifiedMenuItemPadding(0, 16, 16, 16);
 constexpr gfx::Insets kUnifiedSystemInfoViewPadding(4, 16, 16, 16);
@@ -167,14 +165,6 @@
 constexpr int kUnifiedSystemTrayOverScrollPageTransitionDurationMs = 50;
 constexpr double kCollapseThreshold = 0.3;
 
-// Constants used in PageIndicatorView of UnifiedSystemTray.
-constexpr int kUnifiedPageIndicatorButtonRadius = 3;
-constexpr SkColor kUnifiedPageIndicatorButtonColor =
-    SkColorSetRGB(0xF1, 0xF3, 0xF4);
-constexpr SkColor kUnifiedPageIndicatorButtonAlpha = 0x6E;
-constexpr SkColor kUnifiedPageIndicatorButtonInkDropColor =
-    SkColorSetRGB(0xFF, 0xFF, 0xFF);
-
 // Separators between multiple users are shorter than the full width.
 constexpr int kUnifiedUserChooserSeparatorSideMargin = 64;
 // Additional gap above and below the longer separator between user list and
diff --git a/ash/system/unified/page_indicator_view.cc b/ash/system/unified/page_indicator_view.cc
index 306546efd..16688411 100644
--- a/ash/system/unified/page_indicator_view.cc
+++ b/ash/system/unified/page_indicator_view.cc
@@ -10,8 +10,10 @@
 
 #include "ash/app_list/app_list_metrics.h"
 #include "ash/public/cpp/pagination/pagination_model.h"
-#include "ash/system/tray/tray_constants.h"
+#include "ash/style/ash_color_provider.h"
+#include "ash/system/tray/tray_popup_utils.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
+#include "ash/system/unified/unified_system_tray_view.h"
 #include "base/i18n/number_formatting.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
@@ -20,6 +22,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/animation/throb_animation.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/strings/grit/ui_strings.h"
@@ -36,13 +39,9 @@
 
 namespace {
 
+constexpr int kUnifiedPageIndicatorButtonRadius = 3;
 constexpr int kInkDropRadius = 3 * kUnifiedPageIndicatorButtonRadius;
 
-constexpr SkColor kInkDropRippleColor =
-    SkColorSetA(kUnifiedPageIndicatorButtonInkDropColor, 0xF);
-constexpr SkColor kInkDropHighlightColor =
-    SkColorSetA(kUnifiedPageIndicatorButtonInkDropColor, 0x14);
-
 }  // namespace
 
 // Button internally used in PageIndicatorView. Each button
@@ -54,6 +53,13 @@
                                int page)
       : views::Button(this), controller_(controller), page_number_(page) {
     SetInkDropMode(InkDropMode::ON);
+
+    const AshColorProvider::RippleAttributes ripple_attributes =
+        AshColorProvider::Get()->GetRippleAttributes(
+            UnifiedSystemTrayView::GetBackgroundColor());
+    ripple_base_color_ = ripple_attributes.base_color;
+    highlight_opacity_ = ripple_attributes.highlight_opacity;
+    inkdrop_opacity_ = ripple_attributes.inkdrop_opacity;
   }
 
   ~PageIndicatorButton() override {}
@@ -68,6 +74,7 @@
       NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
   }
 
+  // views::View:
   gfx::Size CalculatePreferredSize() const override {
     return gfx::Size(kInkDropRadius * 2, kInkDropRadius * 2);
   }
@@ -75,21 +82,25 @@
   // views::Button:
   const char* GetClassName() const override { return "PageIndicatorView"; }
 
+  // views::Button:
   void PaintButtonContents(gfx::Canvas* canvas) override {
     gfx::Rect rect(GetContentsBounds());
 
-    SkColor current_color = selected_
-                                ? kUnifiedPageIndicatorButtonColor
-                                : SkColorSetA(kUnifiedPageIndicatorButtonColor,
-                                              kUnifiedPageIndicatorButtonAlpha);
-
+    AshColorProvider* color_provider = AshColorProvider::Get();
+    const SkColor selected_color = color_provider->GetContentLayerColor(
+        AshColorProvider::ContentLayerType::kIconPrimary,
+        AshColorProvider::AshColorMode::kDark);
     cc::PaintFlags flags;
     flags.setAntiAlias(true);
     flags.setStyle(cc::PaintFlags::kFill_Style);
-    flags.setColor(current_color);
+    flags.setColor(selected_
+                       ? selected_color
+                       : color_provider->GetDisabledColor(selected_color));
     canvas->DrawCircle(rect.CenterPoint(), kUnifiedPageIndicatorButtonRadius,
                        flags);
   }
+
+  // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override {
     DCHECK(controller_);
     controller_->HandlePageSwitchAction(page_number_);
@@ -98,37 +109,42 @@
   bool selected() { return selected_; }
 
  protected:
+  // views::Button:
   std::unique_ptr<views::InkDrop> CreateInkDrop() override {
-    std::unique_ptr<views::InkDropImpl> ink_drop =
-        Button::CreateDefaultInkDropImpl();
+    auto ink_drop = TrayPopupUtils::CreateInkDrop(this);
     ink_drop->SetShowHighlightOnHover(true);
-    ink_drop->SetAutoHighlightMode(
-        views::InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE);
-    return std::move(ink_drop);
+    return ink_drop;
   }
 
+  // views::Button:
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override {
     return std::make_unique<views::CircleInkDropMask>(
         size(), GetLocalBounds().CenterPoint(), kInkDropRadius);
   }
 
+  // views::Button:
   std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override {
     gfx::Point center = GetLocalBounds().CenterPoint();
     gfx::Rect bounds(center.x() - kInkDropRadius, center.y() - kInkDropRadius,
                      2 * kInkDropRadius, 2 * kInkDropRadius);
     return std::make_unique<views::FloodFillInkDropRipple>(
         size(), GetLocalBounds().InsetsFrom(bounds),
-        GetInkDropCenterBasedOnLastEvent(), kInkDropRippleColor, 1.0f);
+        GetInkDropCenterBasedOnLastEvent(), ripple_base_color_,
+        inkdrop_opacity_);
   }
 
+  // views::Button:
   std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
       const override {
-    return std::make_unique<views::InkDropHighlight>(
+    auto highlight = std::make_unique<views::InkDropHighlight>(
         gfx::PointF(GetLocalBounds().CenterPoint()),
-        std::make_unique<views::CircleLayerDelegate>(kInkDropHighlightColor,
+        std::make_unique<views::CircleLayerDelegate>(ripple_base_color_,
                                                      kInkDropRadius));
+    highlight->set_visible_opacity(highlight_opacity_);
+    return highlight;
   }
 
+  // views::Button:
   void NotifyClick(const ui::Event& event) override {
     Button::NotifyClick(event);
     GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
@@ -139,6 +155,10 @@
   UnifiedSystemTrayController* const controller_;
   const int page_number_ = 0;
 
+  SkColor ripple_base_color_ = gfx::kPlaceholderColor;
+  float highlight_opacity_ = 0.f;
+  float inkdrop_opacity_ = 0.f;
+
   DISALLOW_COPY_AND_ASSIGN(PageIndicatorButton);
 };
 
@@ -230,4 +250,4 @@
   return GetButtonByIndex(index)->selected();
 }
 
-}  // namespace ash
\ No newline at end of file
+}  // namespace ash
diff --git a/ash/system/unified/user_chooser_view.cc b/ash/system/unified/user_chooser_view.cc
index 5c0b672..da438f2 100644
--- a/ash/system/unified/user_chooser_view.cc
+++ b/ash/system/unified/user_chooser_view.cc
@@ -245,8 +245,10 @@
   AddChildView(vertical_labels);
   layout->SetFlexForView(vertical_labels, 1);
 
-  capture_icon_->SetImage(gfx::CreateVectorIcon(kSystemTrayRecordingIcon,
-                                                kUnifiedRecordingIconColor));
+  capture_icon_->SetImage(gfx::CreateVectorIcon(
+      kSystemTrayRecordingIcon,
+      AshColorProvider::Get()->GetContentLayerColor(ContentLayerType::kIconRed,
+                                                    AshColorMode::kDark)));
   if (!has_close_button) {
     // Add a padding with the same size as the close button,
     // so as to align all media indicators in a column.
diff --git a/ash/wm/desks/desk_mini_view.cc b/ash/wm/desks/desk_mini_view.cc
index a387436..1c65ac0 100644
--- a/ash/wm/desks/desk_mini_view.cc
+++ b/ash/wm/desks/desk_mini_view.cc
@@ -15,6 +15,7 @@
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
@@ -135,6 +136,8 @@
   if (owner_bar_->dragged_item_over_bar() &&
       IsPointOnMiniView(owner_bar_->last_dragged_item_screen_location())) {
     desk_preview_->SetBorderColor(kDraggedOverColor);
+  } else if (IsViewHighlighted()) {
+    desk_preview_->SetBorderColor(gfx::kGoogleBlue300);
   } else {
     desk_preview_->SetBorderColor(desk_->is_active() ? kActiveColor
                                                      : kInactiveColor);
@@ -250,6 +253,15 @@
   OnCloseButtonPressed();
 }
 
+bool DeskMiniView::OnViewHighlighted() {
+  UpdateBorderColor();
+  return true;
+}
+
+void DeskMiniView::OnViewUnhighlighted() {
+  UpdateBorderColor();
+}
+
 bool DeskMiniView::IsPointOnMiniView(const gfx::Point& screen_location) const {
   gfx::Point point_in_view = screen_location;
   ConvertPointFromScreen(this, &point_in_view);
diff --git a/ash/wm/desks/desk_mini_view.h b/ash/wm/desks/desk_mini_view.h
index 1ef220e..bdb4c4b2 100644
--- a/ash/wm/desks/desk_mini_view.h
+++ b/ash/wm/desks/desk_mini_view.h
@@ -81,6 +81,8 @@
   gfx::Rect GetHighlightBoundsInScreen() override;
   void MaybeActivateHighlightedView() override;
   void MaybeCloseHighlightedView() override;
+  bool OnViewHighlighted() override;
+  void OnViewUnhighlighted() override;
 
   bool IsPointOnMiniView(const gfx::Point& screen_location) const;
 
diff --git a/ash/wm/desks/desks_bar_view.cc b/ash/wm/desks/desks_bar_view.cc
index 03649a97..3f99c13 100644
--- a/ash/wm/desks/desks_bar_view.cc
+++ b/ash/wm/desks/desks_bar_view.cc
@@ -275,8 +275,7 @@
   // Let the highlight controller know the view is destroying before it is
   // removed from the collection because it needs to know the index of the mini
   // view relative to other traversable views.
-  auto* highlight_controller = GetHighlightController();
-  highlight_controller->OnViewDestroyingOrDisabling(iter->get());
+  GetHighlightController()->OnViewDestroyingOrDisabling(iter->get());
 
   const int begin_x = GetFirstMiniViewXOffset();
   std::unique_ptr<DeskMiniView> removed_mini_view = std::move(*iter);
@@ -286,11 +285,6 @@
   UpdateMiniViewsLabels();
   new_desk_button_->UpdateButtonState();
 
-  // Once the remaining mini views have their bounds updated, notify the
-  // overview highlight controller so that it can update the focus highlight, if
-  // needed.
-  highlight_controller->OnWindowsRepositioned(removed_mini_view->root_window());
-
   std::vector<DeskMiniView*> mini_views_before;
   std::vector<DeskMiniView*> mini_views_after;
   const auto transform_lambda =
@@ -356,10 +350,6 @@
 
   Layout();
 
-  // Update the overview highlight if needed since adding a desk will shift the
-  // mini views from their current positions.
-  GetHighlightController()->OnWindowsRepositioned(root_window);
-
   if (!animate)
     return;
 
diff --git a/ash/wm/desks/new_desk_button.cc b/ash/wm/desks/new_desk_button.cc
index 48fdd243..16fb452 100644
--- a/ash/wm/desks/new_desk_button.cc
+++ b/ash/wm/desks/new_desk_button.cc
@@ -23,6 +23,7 @@
 #include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/animation/ink_drop_mask.h"
 #include "ui/views/background.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/button/label_button_border.h"
 #include "ui/views/style/platform_style.h"
 
@@ -44,6 +45,10 @@
 constexpr SkColor kDisabledTextAndIconColor =
     SkColorSetA(kTextAndIconColor, 0x61);
 
+// The color of the highlight when this button is selected via
+// tabbing.
+constexpr int kHighlightThicknessDp = 2;
+
 }  // namespace
 
 NewDeskButton::NewDeskButton(views::ButtonListener* listener)
@@ -65,6 +70,7 @@
   set_ink_drop_visible_opacity(kInkDropVisibleOpacity);
   SetFocusPainter(nullptr);
   UpdateButtonState();
+  UpdateBorderState();
 }
 
 void NewDeskButton::UpdateButtonState() {
@@ -152,4 +158,26 @@
 
 void NewDeskButton::MaybeCloseHighlightedView() {}
 
+bool NewDeskButton::OnViewHighlighted() {
+  UpdateBorderState();
+  return true;
+}
+
+void NewDeskButton::OnViewUnhighlighted() {
+  UpdateBorderState();
+}
+
+void NewDeskButton::UpdateBorderState() {
+  if (IsViewHighlighted()) {
+    SetBorder(views::CreateRoundedRectBorder(
+        kHighlightThicknessDp, kCornerRadius,
+        GetNativeTheme()->GetSystemColor(
+            ui::NativeTheme::kColorId_FocusedBorderColor)));
+  } else {
+    // Use an empty border when this view is not highlighted otherwise the text
+    // will shift when unhighlighted.
+    SetBorder(views::CreateEmptyBorder(gfx::Insets(kHighlightThicknessDp)));
+  }
+}
+
 }  // namespace ash
diff --git a/ash/wm/desks/new_desk_button.h b/ash/wm/desks/new_desk_button.h
index 0dcc383..780f78a 100644
--- a/ash/wm/desks/new_desk_button.h
+++ b/ash/wm/desks/new_desk_button.h
@@ -44,8 +44,12 @@
   gfx::RoundedCornersF GetRoundedCornersRadii() const override;
   void MaybeActivateHighlightedView() override;
   void MaybeCloseHighlightedView() override;
+  bool OnViewHighlighted() override;
+  void OnViewUnhighlighted() override;
 
  private:
+  void UpdateBorderState();
+
   DISALLOW_COPY_AND_ASSIGN(NewDeskButton);
 };
 
diff --git a/ash/wm/gestures/wm_gesture_handler.cc b/ash/wm/gestures/wm_gesture_handler.cc
index aceafd0..f9867ec 100644
--- a/ash/wm/gestures/wm_gesture_handler.cc
+++ b/ash/wm/gestures/wm_gesture_handler.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/gestures/wm_gesture_handler.h"
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/desks/desks_histogram_enums.h"
@@ -44,6 +45,9 @@
 // Handles horizontal 3-finger scroll by switching desks if possible.
 // Returns true if the gesture was handled.
 bool Handle3FingerHorizontalScroll(float scroll_x) {
+  if (!features::IsVirtualDesksEnabled())
+    return false;
+
   if (std::fabs(scroll_x) < WmGestureHandler::kHorizontalThresholdDp)
     return false;
 
diff --git a/ash/wm/overview/caption_container_view.cc b/ash/wm/overview/caption_container_view.cc
index 31d539a..5a057bf5 100644
--- a/ash/wm/overview/caption_container_view.cc
+++ b/ash/wm/overview/caption_container_view.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/wm/overview/overview_constants.h"
 #include "ash/wm/overview/rounded_rect_view.h"
 #include "ash/wm/window_preview_view.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -43,9 +44,6 @@
 constexpr int kBackdropRoundingDp = 4;
 constexpr SkColor kBackdropColor = SkColorSetA(SK_ColorWHITE, 0x24);
 
-constexpr int kHeaderPreferredHeightDp = 40;
-constexpr int kMarginDp = 10;
-
 // Duration of the show/hide animation of the header.
 constexpr base::TimeDelta kHeaderFadeDuration =
     base::TimeDelta::FromMilliseconds(167);
@@ -261,6 +259,7 @@
   auto* window = GetWidget()->GetNativeWindow();
   gfx::Rect target_bounds = window->GetTargetBounds();
   target_bounds.set_origin(window->GetBoundsInScreen().origin());
+  target_bounds.Inset(kWindowMargin, kWindowMargin);
   return target_bounds;
 }
 
@@ -276,25 +275,25 @@
 
 void CaptionContainerView::Layout() {
   gfx::Rect bounds(GetLocalBounds());
-  bounds.Inset(kMarginDp, kMarginDp);
+  bounds.Inset(kOverviewMargin, kOverviewMargin);
 
   if (backdrop_view_) {
     gfx::Rect backdrop_bounds = bounds;
-    backdrop_bounds.Inset(0, kHeaderPreferredHeightDp, 0, 0);
+    backdrop_bounds.Inset(0, kHeaderHeightDp, 0, 0);
     backdrop_view_->SetBoundsRect(backdrop_bounds);
   }
 
   if (preview_view_) {
     gfx::Rect preview_bounds = bounds;
-    preview_bounds.Inset(0, kHeaderPreferredHeightDp, 0, 0);
+    preview_bounds.Inset(0, kHeaderHeightDp, 0, 0);
     preview_bounds.ClampToCenteredSize(preview_view_->CalculatePreferredSize());
     preview_view_->SetBoundsRect(preview_bounds);
   }
 
   // Position the header at the top.
-  const gfx::Rect header_bounds(kMarginDp, kMarginDp,
-                                GetLocalBounds().width() - kMarginDp,
-                                kHeaderPreferredHeightDp);
+  const gfx::Rect header_bounds(kOverviewMargin, kOverviewMargin,
+                                GetLocalBounds().width() - kOverviewMargin,
+                                kHeaderHeightDp);
   header_view_->SetBoundsRect(header_bounds);
 }
 
@@ -344,7 +343,7 @@
                                         ui::ET_MOUSE_PRESSED};
   if (event.IsLocatedEvent() && base::Contains(press_types, event.type())) {
     gfx::Rect inset_bounds = GetLocalBounds();
-    inset_bounds.Inset(gfx::Insets(kMarginDp));
+    inset_bounds.Inset(gfx::Insets(kOverviewMargin));
     if (!inset_bounds.Contains(event.AsLocatedEvent()->location()))
       accept_events = false;
   }
diff --git a/ash/wm/overview/overview_constants.h b/ash/wm/overview/overview_constants.h
index cdbcdf1..c35fa24 100644
--- a/ash/wm/overview/overview_constants.h
+++ b/ash/wm/overview/overview_constants.h
@@ -21,7 +21,7 @@
 // Cover the transformed window including the gaps between the windows with a
 // transparent shield to block the input events from reaching the transformed
 // window while in overview.
-constexpr int kOverviewMargin = kWindowMargin * 2;
+ASH_EXPORT constexpr int kOverviewMargin = kWindowMargin * 2;
 
 // Height of an item header.
 constexpr int kHeaderHeightDp = 40;
diff --git a/ash/wm/overview/overview_highlight_controller.cc b/ash/wm/overview/overview_highlight_controller.cc
index 81a04ba..d1e6316 100644
--- a/ash/wm/overview/overview_highlight_controller.cc
+++ b/ash/wm/overview/overview_highlight_controller.cc
@@ -12,6 +12,7 @@
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/desks/new_desk_button.h"
 #include "ash/wm/overview/cleanup_animation_observer.h"
+#include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_delegate.h"
 #include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/overview/overview_item.h"
@@ -162,6 +163,48 @@
   return kHighlightCornerRadii;
 }
 
+bool OverviewHighlightController::OverviewHighlightableView::
+    OnViewHighlighted() {
+  return false;
+}
+
+void OverviewHighlightController::OverviewHighlightableView::
+    OnViewUnhighlighted() {}
+
+bool OverviewHighlightController::OverviewHighlightableView::
+    IsViewHighlighted() {
+  auto* overview_session =
+      Shell::Get()->overview_controller()->overview_session();
+  DCHECK(overview_session);
+  return overview_session->highlight_controller()->highlighted_view_ == this;
+}
+
+// -----------------------------------------------------------------------------
+// OverviewHighlightController::TestApi
+
+OverviewHighlightController::TestApi::TestApi(
+    OverviewHighlightController* highlight_controller)
+    : highlight_controller_(highlight_controller) {}
+
+OverviewHighlightController::TestApi::~TestApi() = default;
+
+gfx::Rect OverviewHighlightController::TestApi::GetHighlightBoundsInScreen()
+    const {
+  if (!GetHighlightWidget())
+    return gfx::Rect();
+  return GetHighlightWidget()->GetNativeWindow()->GetBoundsInScreen();
+}
+
+OverviewHighlightController::OverviewHighlightableView*
+OverviewHighlightController::TestApi::GetHighlightView() const {
+  return highlight_controller_->highlighted_view_;
+}
+
+OverviewHighlightController::HighlightWidget*
+OverviewHighlightController::TestApi::GetHighlightWidget() const {
+  return highlight_controller_->highlight_widget_.get();
+}
+
 // -----------------------------------------------------------------------------
 // OverviewHighlightController
 
@@ -300,14 +343,6 @@
       highlighted_view_->GetHighlightBoundsInScreen());
 }
 
-gfx::Rect OverviewHighlightController::GetHighlightBoundsInScreenForTesting()
-    const {
-  if (!highlight_widget_)
-    return gfx::Rect();
-
-  return highlight_widget_->GetNativeWindow()->GetBoundsInScreen();
-}
-
 std::vector<OverviewHighlightController::OverviewHighlightableView*>
 OverviewHighlightController::GetTraversableViews() const {
   std::vector<OverviewHighlightableView*> traversable_views;
@@ -342,6 +377,8 @@
   highlighted_view_ = view_to_be_highlighted;
   highlighted_view_->GetView()->NotifyAccessibilityEvent(
       ax::mojom::Event::kSelection, true);
+  if (previous_view)
+    previous_view->OnViewUnhighlighted();
 
   const bool create_highlight =
       ShouldCreateHighlight(previous_view, highlighted_view_, reverse);
@@ -368,6 +405,9 @@
     old_highlight_window->SetTransform(transform);
   }
 
+  if (highlighted_view_->OnViewHighlighted())
+    return;
+
   gfx::Rect target_screen_bounds =
       highlighted_view_->GetHighlightBoundsInScreen();
   if (!highlight_widget_) {
diff --git a/ash/wm/overview/overview_highlight_controller.h b/ash/wm/overview/overview_highlight_controller.h
index 0e52203..b76a392 100644
--- a/ash/wm/overview/overview_highlight_controller.h
+++ b/ash/wm/overview/overview_highlight_controller.h
@@ -30,6 +30,8 @@
 // or tab keys, or when users are tab dragging.
 class ASH_EXPORT OverviewHighlightController {
  public:
+  class HighlightWidget;
+
   // An interface that must be implemented by classes that want to be
   // highlighted in overview.
   class OverviewHighlightableView {
@@ -48,10 +50,33 @@
     virtual void MaybeActivateHighlightedView() = 0;
     virtual void MaybeCloseHighlightedView() = 0;
 
+    // Subclasses can override these if they wish to have custom behavior when
+    // they're highlighted. They should return true if overridden, otherwise the
+    // default highlight will show up.
+    virtual bool OnViewHighlighted();
+    virtual void OnViewUnhighlighted();
+
+    // Returns true if this is the current highlighted view.
+    bool IsViewHighlighted();
+
    protected:
     virtual ~OverviewHighlightableView() {}
   };
 
+  // TestApi is used for tests to get internal implementation details.
+  class ASH_EXPORT TestApi {
+   public:
+    explicit TestApi(OverviewHighlightController* highlight_controller);
+    ~TestApi();
+
+    gfx::Rect GetHighlightBoundsInScreen() const;
+    OverviewHighlightableView* GetHighlightView() const;
+    HighlightWidget* GetHighlightWidget() const;
+
+   private:
+    OverviewHighlightController* const highlight_controller_;
+  };
+
   explicit OverviewHighlightController(OverviewSession* overview_session);
   ~OverviewHighlightController();
 
@@ -85,13 +110,7 @@
   // highlight widget without animation.
   void OnWindowsRepositioned(aura::Window* root_window);
 
-  gfx::Rect GetHighlightBoundsInScreenForTesting() const;
-
  private:
-  class HighlightWidget;
-  friend class DesksOverviewHighlightControllerTest;
-  friend class OverviewHighlightControllerTest;
-
   // Returns a vector of views that can be traversed via overview tabbing.
   // Includes desk mini views, the new desk button and overview items.
   std::vector<OverviewHighlightableView*> GetTraversableViews() const;
@@ -106,11 +125,13 @@
   // If an item that is selected is deleted, store its index, so the next
   // traversal can pick up where it left off.
   base::Optional<int> deleted_index_ = base::nullopt;
-  // The current view that |highlight_widget_| is highlighting. This will be
-  // non-null if |highlight_widget_| is.
+
+  // The current view that |highlight_widget_| is highlighting.
   OverviewHighlightableView* highlighted_view_ = nullptr;
+
   // A background highlight that shows up when using keyboard traversal with tab
-  // or arrow keys.
+  // or arrow keys. This may not exist if the current highlighted view has its
+  // own highlighting override.
   std::unique_ptr<HighlightWidget> highlight_widget_;
 
   // A background highlight that shows up when dragging a tab towards a chrome
diff --git a/ash/wm/overview/overview_highlight_controller_unittest.cc b/ash/wm/overview/overview_highlight_controller_unittest.cc
index 6ccea30..c8f52fb 100644
--- a/ash/wm/overview/overview_highlight_controller_unittest.cc
+++ b/ash/wm/overview/overview_highlight_controller_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/wm/desks/desks_bar_view.h"
 #include "ash/wm/desks/desks_test_util.h"
 #include "ash/wm/desks/new_desk_button.h"
+#include "ash/wm/overview/overview_constants.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/overview/overview_item.h"
@@ -327,7 +328,8 @@
   }
 
   OverviewHighlightController::OverviewHighlightableView* GetHighlightedView() {
-    return GetHighlightController()->highlighted_view_;
+    return OverviewHighlightController::TestApi(GetHighlightController())
+        .GetHighlightView();
   }
 
   const DesksBarView* GetDesksBarViewForRoot(aura::Window* root_window) {
@@ -337,15 +339,30 @@
     return grid->desks_bar_view();
   }
 
-  // Checks to see if a view is completely covered by the overview highlight.
-  bool CoveredByOverviewHighlight(views::View* view) {
+  bool OverviewHighlightShown() {
     if (!Shell::Get()->overview_controller()->InOverviewSession())
       return false;
 
-    gfx::Rect view_bounds = view->GetBoundsInScreen();
-    return GetHighlightController()
-        ->GetHighlightBoundsInScreenForTesting()
-        .Contains(view_bounds);
+    OverviewHighlightController::TestApi test_api(GetHighlightController());
+    return !!test_api.GetHighlightWidget();
+  }
+
+  // Checks to see if a view is completely covered by the overview highlight.
+  bool CoveredByOverviewHighlight(views::View* view) {
+    if (!OverviewHighlightShown())
+      return false;
+
+    const gfx::Rect highlight_bounds =
+        OverviewHighlightController::TestApi(GetHighlightController())
+            .GetHighlightBoundsInScreen();
+    DCHECK(!highlight_bounds.IsEmpty());
+
+    // The highlight bounds will be a bit smaller than the view it
+    // highlights, because it is meant to highlight the visible area of the
+    // view.
+    const int tolerance = kOverviewMargin;
+    const gfx::Rect view_bounds = view->GetBoundsInScreen();
+    return highlight_bounds.ApproximatelyEqual(view_bounds, tolerance);
   }
 
  private:
@@ -354,7 +371,8 @@
 };
 
 // Tests that we can tab through the desk mini views, new desk button and
-// overview items in the correct order.
+// overview items in the correct order. Overview items will have the overview
+// highlight shown when highlighted, but desks items will not.
 TEST_F(DesksOverviewHighlightControllerTest, TabbingBasic) {
   std::unique_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(200, 200)));
   std::unique_ptr<aura::Window> window2(CreateTestWindow(gfx::Rect(200, 200)));
@@ -367,23 +385,27 @@
   // Tests that the first highlighted item is the first mini view.
   SendKey(ui::VKEY_TAB);
   EXPECT_EQ(desk_bar_view->mini_views()[0].get(), GetHighlightedView());
+  EXPECT_FALSE(OverviewHighlightShown());
 
   // Tests that after tabbing through the mini views, we highlight the new desk
   // button.
   SendKey(ui::VKEY_TAB);
   SendKey(ui::VKEY_TAB);
   EXPECT_EQ(desk_bar_view->new_desk_button(), GetHighlightedView());
+  EXPECT_FALSE(OverviewHighlightShown());
 
   // Tests that the overview item gets highlighted after the new desk button.
   SendKey(ui::VKEY_TAB);
   auto* item2 = GetOverviewItemForWindow(window2.get());
   EXPECT_EQ(item2->caption_container_view(), GetHighlightedView());
+  EXPECT_TRUE(OverviewHighlightShown());
 
   // Tests that after tabbing through the overview items, we go back to the
   // first mini view.
   SendKey(ui::VKEY_TAB);
   SendKey(ui::VKEY_TAB);
   EXPECT_EQ(desk_bar_view->mini_views()[0].get(), GetHighlightedView());
+  EXPECT_FALSE(OverviewHighlightShown());
 }
 
 // Tests that we can reverse tab through the desk mini views, new desk button
@@ -514,39 +536,42 @@
   ToggleOverview();
   const auto* desk_bar_view1 = GetDesksBarViewForRoot(roots[0]);
   EXPECT_EQ(2u, desk_bar_view1->mini_views().size());
+  EXPECT_FALSE(OverviewHighlightShown());
 
   SendKey(ui::VKEY_TAB);
-  EXPECT_TRUE(
-      CoveredByOverviewHighlight(desk_bar_view1->mini_views()[0].get()));
   SendKey(ui::VKEY_TAB);
+  EXPECT_FALSE(OverviewHighlightShown());
+
   SendKey(ui::VKEY_TAB);
-  EXPECT_TRUE(CoveredByOverviewHighlight(desk_bar_view1->new_desk_button()));
+  EXPECT_EQ(desk_bar_view1->new_desk_button(), GetHighlightedView());
+  EXPECT_FALSE(OverviewHighlightShown());
+
   SendKey(ui::VKEY_TAB);
   auto* item1 = GetOverviewItemForWindow(window1.get());
   EXPECT_TRUE(CoveredByOverviewHighlight(item1->caption_container_view()));
 
   const auto* desk_bar_view2 = GetDesksBarViewForRoot(roots[1]);
   SendKey(ui::VKEY_TAB);
-  EXPECT_TRUE(
-      CoveredByOverviewHighlight(desk_bar_view2->mini_views()[0].get()));
+  EXPECT_FALSE(OverviewHighlightShown());
   SendKey(ui::VKEY_TAB);
   SendKey(ui::VKEY_TAB);
-  EXPECT_TRUE(CoveredByOverviewHighlight(desk_bar_view2->new_desk_button()));
+  EXPECT_EQ(desk_bar_view2->new_desk_button(), GetHighlightedView());
+  EXPECT_FALSE(OverviewHighlightShown());
   SendKey(ui::VKEY_TAB);
   auto* item2 = GetOverviewItemForWindow(window2.get());
   EXPECT_TRUE(CoveredByOverviewHighlight(item2->caption_container_view()));
 
   const auto* desk_bar_view3 = GetDesksBarViewForRoot(roots[2]);
   SendKey(ui::VKEY_TAB);
-  EXPECT_TRUE(
-      CoveredByOverviewHighlight(desk_bar_view3->mini_views()[0].get()));
+  EXPECT_FALSE(OverviewHighlightShown());
   SendKey(ui::VKEY_TAB);
   SendKey(ui::VKEY_TAB);
-  EXPECT_TRUE(CoveredByOverviewHighlight(desk_bar_view3->new_desk_button()));
+  EXPECT_EQ(desk_bar_view3->new_desk_button(), GetHighlightedView());
+  EXPECT_FALSE(OverviewHighlightShown());
 }
 
 TEST_F(DesksOverviewHighlightControllerTest,
-       TabbingMDisplayHighlightLocationAfterItemRemoval) {
+       TabbingDisplayHighlightLocationAfterItemRemoval) {
   std::unique_ptr<views::Widget> widget3(CreateTestWidget());
   std::unique_ptr<aura::Window> window2(CreateTestWindow(gfx::Rect(200, 200)));
   std::unique_ptr<views::Widget> widget1(CreateTestWidget());
@@ -556,20 +581,14 @@
       GetDesksBarViewForRoot(Shell::GetAllRootWindows()[0]);
   EXPECT_EQ(2u, desk_bar_view->mini_views().size());
 
-  // Tests that if we delete desk2 while desk1 is highlighted, the overview
-  // highlight bounds still contains desk1's bounds.
-  SendKey(ui::VKEY_TAB);
-  EXPECT_TRUE(CoveredByOverviewHighlight(desk_bar_view->mini_views()[0].get()));
-  RemoveDesk(DesksController::Get()->desks()[1].get());
-  EXPECT_TRUE(CoveredByOverviewHighlight(desk_bar_view->mini_views()[0].get()));
-
-  // Tests that if we delete items on the right and left of item2, the overview
-  // highlight bounds still contains item2's bounds.
-  SendKey(ui::VKEY_TAB);
-  SendKey(ui::VKEY_TAB);
+  // Tab until we highlight |window2|.
+  SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB);
   SendKey(ui::VKEY_TAB);
   auto* item2 = GetOverviewItemForWindow(window2.get());
   EXPECT_TRUE(CoveredByOverviewHighlight(item2->caption_container_view()));
+
+  // Tests that if we delete items on the right and left of item2, the overview
+  // highlight bounds still contains item2's bounds.
   auto* item1 = GetOverviewItemForWindow(widget1->GetNativeWindow());
   item1->CloseWindow();
   EXPECT_TRUE(CoveredByOverviewHighlight(item2->caption_container_view()));
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 58f04fa..93bc577 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1588,6 +1588,7 @@
     deps += [
       "//third_party/fuchsia-sdk/sdk:async_default",
       "//third_party/fuchsia-sdk/sdk:async_loop_cpp",
+      "//third_party/fuchsia-sdk/sdk:async_loop_default",
       "//third_party/fuchsia-sdk/sdk:deprecatedtimezone",
       "//third_party/fuchsia-sdk/sdk:fidl",
       "//third_party/fuchsia-sdk/sdk:sys",
@@ -1969,7 +1970,6 @@
       "task/thread_pool/thread_group_native_mac.mm",
       "threading/platform_thread_mac.mm",
       "time/time_conversion_posix.cc",
-      "time/time_exploded_posix.cc",
       "time/time_mac.cc",
     ]
 
@@ -2071,6 +2071,8 @@
   header_dir = "base/debug"
   enable_gdbinit_warning =
       is_debug && (strip_absolute_paths_from_debug_symbols || use_custom_libcxx)
+  enable_lldbinit_warning =
+      is_debug && strip_absolute_paths_from_debug_symbols && is_mac
 
   flags = [
     "ENABLE_LOCATION_SOURCE=$enable_location_source",
@@ -2079,6 +2081,7 @@
     "UNSAFE_DEVELOPER_BUILD=$is_unsafe_developer_build",
     "CAN_UNWIND_WITH_CFI_TABLE=$can_unwind_with_cfi_table",
     "ENABLE_GDBINIT_WARNING=$enable_gdbinit_warning",
+    "ENABLE_LLDBINIT_WARNING=$enable_lldbinit_warning",
   ]
 }
 
diff --git a/base/debug/debugger.h b/base/debug/debugger.h
index 318aa880..efc9b40 100644
--- a/base/debug/debugger.h
+++ b/base/debug/debugger.h
@@ -40,7 +40,8 @@
 
 // If a debugger is present, verifies that it is properly set up, and DCHECK()s
 // if misconfigured.  Currently only verifies that //tools/gdb/gdbinit has been
-// sourced when using gdb on Linux.
+// sourced when using gdb on Linux and //tools/lldb/lldbinit.py has been sourced
+// when using lldb on macOS.
 BASE_EXPORT void VerifyDebugger();
 
 }  // namespace debug
diff --git a/base/debug/debugger_posix.cc b/base/debug/debugger_posix.cc
index 45bf1da..5a22bed9c 100644
--- a/base/debug/debugger_posix.cc
+++ b/base/debug/debugger_posix.cc
@@ -128,7 +128,21 @@
   return being_debugged;
 }
 
-void VerifyDebugger() {}
+void VerifyDebugger() {
+#if BUILDFLAG(ENABLE_LLDBINIT_WARNING)
+  // Quick check before potentially slower GetDebuggerProcess().
+  if (Environment::Create()->HasVar("CHROMIUM_LLDBINIT_SOURCED"))
+    return;
+  DCHECK(false)
+      << "Detected lldb without sourcing //tools/lldb/lldbinit.py. lldb may "
+         "not be able to find debug symbols. Please see debug instructions for "
+         "using //tools/lldb/lldbinit.py:\n"
+         "https://chromium.googlesource.com/chromium/src/+/master/docs/"
+         "lldbinit.md\n"
+         "To continue anyway, type 'continue' in lldb. To always skip this "
+         "check, define an environment variable CHROMIUM_LLDBINIT_SOURCED=1";
+#endif
+}
 
 #elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX)
 
diff --git a/base/message_loop/message_pump_fuchsia.cc b/base/message_loop/message_pump_fuchsia.cc
index 948785d..2c77679 100644
--- a/base/message_loop/message_pump_fuchsia.cc
+++ b/base/message_loop/message_pump_fuchsia.cc
@@ -5,6 +5,7 @@
 #include "base/message_loop/message_pump_fuchsia.h"
 
 #include <lib/async-loop/cpp/loop.h>
+#include <lib/async-loop/default.h>
 #include <lib/fdio/io.h>
 #include <lib/fdio/unsafe.h>
 #include <lib/zx/time.h>
@@ -172,7 +173,7 @@
 }
 
 MessagePumpFuchsia::MessagePumpFuchsia()
-    : async_loop_(new async::Loop(&kAsyncLoopConfigAttachToThread)),
+    : async_loop_(new async::Loop(&kAsyncLoopConfigAttachToCurrentThread)),
       weak_factory_(this) {}
 MessagePumpFuchsia::~MessagePumpFuchsia() = default;
 
diff --git a/base/strings/string_split_unittest.cc b/base/strings/string_split_unittest.cc
index 089398a..8488e946 100644
--- a/base/strings/string_split_unittest.cc
+++ b/base/strings/string_split_unittest.cc
@@ -47,7 +47,7 @@
 }
 
 TEST_F(SplitStringIntoKeyValuePairsUsingSubstrTest,
-       MissingKeyValuePairDelimeter) {
+       MissingKeyValuePairDelimiter) {
   EXPECT_TRUE(SplitStringIntoKeyValuePairsUsingSubstr(
       "key1:value1,,key3:value3",
       ':',    // Key-value delimiter
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index d4092ab..3aa9d24 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -113,6 +113,10 @@
 class RTCVideoDecoderAdapter;
 class SourceStream;
 class VideoFrameResourceProvider;
+class WorkerThread;
+namespace scheduler {
+class WorkerThread;
+}
 }
 namespace cc {
 class CompletionEvent;
@@ -392,6 +396,8 @@
   friend class SimpleThread;
   friend class base::GetAppOutputScopedAllowBaseSyncPrimitives;
   friend class blink::SourceStream;
+  friend class blink::WorkerThread;
+  friend class blink::scheduler::WorkerThread;
   friend class chrome_cleaner::SystemReportComponent;
   friend class content::BrowserMainLoop;
   friend class content::BrowserProcessSubThread;
diff --git a/base/time/time.h b/base/time/time.h
index 192be16..6abe4112 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -492,7 +492,10 @@
 #if defined(OS_WIN)
   static constexpr int kExplodedMinYear = 1601;
   static constexpr int kExplodedMaxYear = 30827;
-#elif defined(OS_IOS) || defined(OS_MACOSX)
+#elif defined(OS_IOS)
+  static constexpr int kExplodedMinYear = std::numeric_limits<int>::min();
+  static constexpr int kExplodedMaxYear = std::numeric_limits<int>::max();
+#elif defined(OS_MACOSX)
   static constexpr int kExplodedMinYear = 1902;
   static constexpr int kExplodedMaxYear = std::numeric_limits<int>::max();
 #elif defined(OS_ANDROID)
diff --git a/base/time/time_exploded_posix.cc b/base/time/time_exploded_posix.cc
index a37ced1..0655703a 100644
--- a/base/time/time_exploded_posix.cc
+++ b/base/time/time_exploded_posix.cc
@@ -32,7 +32,7 @@
 #include "base/numerics/clamped_math.h"
 #endif
 
-#if defined(OS_MACOSX) || defined(OS_IOS)
+#if defined(OS_MACOSX)
 static_assert(sizeof(time_t) >= 8, "Y2038 problem!");
 #endif
 
diff --git a/base/time/time_mac.cc b/base/time/time_mac.cc
index 272a317..7d8e6c6 100644
--- a/base/time/time_mac.cc
+++ b/base/time/time_mac.cc
@@ -175,6 +175,102 @@
          kCFAbsoluteTimeIntervalSince1970;
 }
 
+// Note: These implementations of Time::FromExploded() and Time::Explode() are
+// only used on iOS now. Since Mac is now always 64-bit, we can use the POSIX
+// versions of these functions as time_t is not capped at year 2038 on 64-bit
+// builds. The POSIX functions are preferred since they don't suffer from some
+// performance problems that are present in these implementations.
+// See crbug.com/781601 for more details.
+#if defined(OS_IOS)
+// static
+bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) {
+  base::ScopedCFTypeRef<CFTimeZoneRef> time_zone(
+      is_local
+          ? CFTimeZoneCopySystem()
+          : CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, 0));
+  base::ScopedCFTypeRef<CFCalendarRef> gregorian(CFCalendarCreateWithIdentifier(
+      kCFAllocatorDefault, kCFGregorianCalendar));
+  CFCalendarSetTimeZone(gregorian, time_zone);
+  CFAbsoluteTime absolute_time;
+  // 'S' is not defined in componentDesc in Apple documentation, but can be
+  // found at http://www.opensource.apple.com/source/CF/CF-855.17/CFCalendar.c
+  CFCalendarComposeAbsoluteTime(
+      gregorian, &absolute_time, "yMdHmsS", exploded.year, exploded.month,
+      exploded.day_of_month, exploded.hour, exploded.minute, exploded.second,
+      exploded.millisecond);
+  CFAbsoluteTime seconds = absolute_time + kCFAbsoluteTimeIntervalSince1970;
+
+  // CFAbsolutTime is typedef of double. Convert seconds to
+  // microseconds and then cast to int64. If
+  // it cannot be suited to int64, then fail to avoid overflows.
+  double microseconds =
+      (seconds * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset;
+  if (microseconds > std::numeric_limits<int64_t>::max() ||
+      microseconds < std::numeric_limits<int64_t>::min()) {
+    *time = Time(0);
+    return false;
+  }
+
+  base::Time converted_time = Time(static_cast<int64_t>(microseconds));
+
+  // If |exploded.day_of_month| is set to 31
+  // on a 28-30 day month, it will return the first day of the next month.
+  // Thus round-trip the time and compare the initial |exploded| with
+  // |utc_to_exploded| time.
+  base::Time::Exploded to_exploded;
+  if (!is_local)
+    converted_time.UTCExplode(&to_exploded);
+  else
+    converted_time.LocalExplode(&to_exploded);
+
+  if (ExplodedMostlyEquals(to_exploded, exploded)) {
+    *time = converted_time;
+    return true;
+  }
+
+  *time = Time(0);
+  return false;
+}
+
+void Time::Explode(bool is_local, Exploded* exploded) const {
+  // Avoid rounding issues, by only putting the integral number of seconds
+  // (rounded towards -infinity) into a |CFAbsoluteTime| (which is a |double|).
+  int64_t microsecond = us_ % kMicrosecondsPerSecond;
+  if (microsecond < 0)
+    microsecond += kMicrosecondsPerSecond;
+  CFAbsoluteTime seconds = ((us_ - microsecond - kTimeTToMicrosecondsOffset) /
+                            kMicrosecondsPerSecond) -
+                           kCFAbsoluteTimeIntervalSince1970;
+
+  base::ScopedCFTypeRef<CFTimeZoneRef> time_zone(
+      is_local
+          ? CFTimeZoneCopySystem()
+          : CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, 0));
+  base::ScopedCFTypeRef<CFCalendarRef> gregorian(CFCalendarCreateWithIdentifier(
+      kCFAllocatorDefault, kCFGregorianCalendar));
+  CFCalendarSetTimeZone(gregorian, time_zone);
+  int second, day_of_week;
+  // 'E' sets the day of week, but is not defined in componentDesc in Apple
+  // documentation. It can be found in open source code here:
+  // http://www.opensource.apple.com/source/CF/CF-855.17/CFCalendar.c
+  CFCalendarDecomposeAbsoluteTime(gregorian, seconds, "yMdHmsE",
+                                  &exploded->year, &exploded->month,
+                                  &exploded->day_of_month, &exploded->hour,
+                                  &exploded->minute, &second, &day_of_week);
+  // Make sure seconds are rounded down towards -infinity.
+  exploded->second = floor(second);
+  // |Exploded|'s convention for day of week is 0 = Sunday, i.e. different
+  // from CF's 1 = Sunday.
+  exploded->day_of_week = (day_of_week - 1) % 7;
+  // Calculate milliseconds ourselves, since we rounded the |seconds|, making
+  // sure to round towards -infinity.
+  exploded->millisecond =
+      (microsecond >= 0) ? microsecond / kMicrosecondsPerMillisecond :
+                           (microsecond - kMicrosecondsPerMillisecond + 1) /
+                               kMicrosecondsPerMillisecond;
+}
+#endif  // OS_IOS
+
 // TimeTicks ------------------------------------------------------------------
 
 namespace subtle {
diff --git a/build/config/fuchsia/OWNERS b/build/config/fuchsia/OWNERS
index e7034ea..c1b5845 100644
--- a/build/config/fuchsia/OWNERS
+++ b/build/config/fuchsia/OWNERS
@@ -1 +1,4 @@
 file://build/fuchsia/OWNERS
+# COMPONENT: Fuchsia
+# OS: Fuchsia
+# TEAM: cr-fuchsia@chromium.org
diff --git a/build/linux/sysroot_scripts/install-sysroot.py b/build/linux/sysroot_scripts/install-sysroot.py
index 2e756f7..165551a2 100755
--- a/build/linux/sysroot_scripts/install-sysroot.py
+++ b/build/linux/sysroot_scripts/install-sysroot.py
@@ -144,7 +144,7 @@
       with open(tarball, "wb") as f:
         f.write(response.read())
       break
-    except:
+    except Exception:  # Ignore exceptions.
       pass
   else:
     raise Error('Failed to download %s' % url)
diff --git a/build/linux/sysroot_scripts/sysroot-creator-sid.sh b/build/linux/sysroot_scripts/sysroot-creator-sid.sh
index 6f471d83..f3bf1de2 100755
--- a/build/linux/sysroot_scripts/sysroot-creator-sid.sh
+++ b/build/linux/sysroot_scripts/sysroot-creator-sid.sh
@@ -26,7 +26,6 @@
 KEYRING_FILE="${SCRIPT_DIR}/debian-archive-sid-stable.gpg"
 
 HAS_ARCH_AMD64=1
-HAS_ARCH_AMD64MULTILIB=1
 HAS_ARCH_I386=1
 HAS_ARCH_ARM=1
 HAS_ARCH_ARM64=1
@@ -365,27 +364,6 @@
   libtsan0
 "
 
-DEBIAN_PACKAGES_AMD64MULTILIB="
-  gcc-multilib
-  lib32asan3
-  lib32atomic1
-  lib32cilkrts5
-  lib32gcc1
-  lib32gcc-6-dev
-  lib32gomp1
-  lib32itm1
-  lib32mpx2
-  lib32quadmath0
-  lib32stdc++6
-  lib32stdc++-6-dev
-  lib32ubsan0
-  lib32z1
-  lib32z1-dev
-  libc6-dev-i386
-  libc6-dev-i386-cross
-  libc6-i386
-"
-
 DEBIAN_PACKAGES_X86="
   libasan3
   libcilkrts5
diff --git a/build/linux/sysroot_scripts/sysroot-creator.sh b/build/linux/sysroot_scripts/sysroot-creator.sh
index 6a0914c..88645dad3 100644
--- a/build/linux/sysroot_scripts/sysroot-creator.sh
+++ b/build/linux/sysroot_scripts/sysroot-creator.sh
@@ -45,7 +45,6 @@
 fi
 
 readonly HAS_ARCH_AMD64=${HAS_ARCH_AMD64:=0}
-readonly HAS_ARCH_AMD64MULTILIB=${HAS_ARCH_AMD64MULTILIB:=0}
 readonly HAS_ARCH_I386=${HAS_ARCH_I386:=0}
 readonly HAS_ARCH_ARM=${HAS_ARCH_ARM:=0}
 readonly HAS_ARCH_ARM64=${HAS_ARCH_ARM64:=0}
@@ -63,7 +62,6 @@
 readonly RELEASE_FILE_GPG="Release.gpg"
 
 readonly DEBIAN_DEP_LIST_AMD64="generated_package_lists/${DIST}.amd64"
-readonly DEBIAN_DEP_LIST_AMD64MULTILIB="generated_package_lists/${DIST}.amd64-multilib"
 readonly DEBIAN_DEP_LIST_I386="generated_package_lists/${DIST}.i386"
 readonly DEBIAN_DEP_LIST_ARM="generated_package_lists/${DIST}.arm"
 readonly DEBIAN_DEP_LIST_ARM64="generated_package_lists/${DIST}.arm64"
@@ -135,9 +133,6 @@
     *Amd64)
       ARCH=AMD64
       ;;
-    *Amd64Multilib)
-      ARCH=AMD64MULTILIB
-      ;;
     *I386)
       ARCH=I386
       ;;
@@ -257,12 +252,6 @@
     ${DEBIAN_PACKAGES_X86:=} ${DEBIAN_PACKAGES_AMD64:=}"
 }
 
-GeneratePackageListAmd64Multilib() {
-  GeneratePackageListCommon "$1" amd64 "${DEBIAN_PACKAGES}
-    ${DEBIAN_PACKAGES_X86:=} ${DEBIAN_PACKAGES_AMD64:=}
-    ${DEBIAN_PACKAGES_AMD64MULTILIB:=}"
-}
-
 GeneratePackageListI386() {
   GeneratePackageListCommon "$1" i386 "${DEBIAN_PACKAGES}
     ${DEBIAN_PACKAGES_X86:=}"
@@ -352,11 +341,6 @@
 }
 
 
-HacksAndPatchesAmd64Multilib() {
-  HacksAndPatchesCommon x86_64 linux-gnu strip
-}
-
-
 HacksAndPatchesI386() {
   HacksAndPatchesCommon i386 linux-gnu strip
 }
@@ -487,11 +471,6 @@
 }
 
 
-VerifyLibraryDepsAmd64Multilib() {
-  VerifyLibraryDepsCommon x86_64 linux-gnu
-}
-
-
 VerifyLibraryDepsI386() {
   VerifyLibraryDepsCommon i386 linux-gnu
 }
@@ -538,26 +517,6 @@
 }
 
 #@
-#@ BuildSysrootAmd64Multilib
-#@
-#@    Build everything and package it
-BuildSysrootAmd64Multilib() {
-  if [ "$HAS_ARCH_AMD64MULTILIB" = "0" ]; then
-    return
-  fi
-  ClearInstallDir
-  local package_file="${DEBIAN_DEP_LIST_AMD64MULTILIB}"
-  GeneratePackageListAmd64Multilib "$package_file"
-  local files_and_sha256sums="$(cat ${package_file})"
-  StripChecksumsFromPackageList "$package_file"
-  InstallIntoSysroot ${files_and_sha256sums}
-  CleanupJailSymlinks
-  HacksAndPatchesAmd64Multilib
-  VerifyLibraryDepsAmd64Multilib
-  CreateTarBall
-}
-
-#@
 #@ BuildSysrootI386
 #@
 #@    Build everything and package it
@@ -663,7 +622,6 @@
 #@    Build sysroot images for all architectures
 BuildSysrootAll() {
   RunCommand BuildSysrootAmd64
-  RunCommand BuildSysrootAmd64Multilib
   RunCommand BuildSysrootI386
   RunCommand BuildSysrootARM
   RunCommand BuildSysrootARM64
@@ -690,16 +648,6 @@
 }
 
 #@
-#@ UploadSysrootAmd64Multilib
-#@
-UploadSysrootAmd64Multilib() {
-  if [ "$HAS_ARCH_AMD64MULTILIB" = "0" ]; then
-    return
-  fi
-  UploadSysroot "$@"
-}
-
-#@
 #@ UploadSysrootI386
 #@
 UploadSysrootI386() {
@@ -755,7 +703,6 @@
 #@    Upload sysroot image for all architectures
 UploadSysrootAll() {
   RunCommand UploadSysrootAmd64 "$@"
-  RunCommand UploadSysrootAmd64Multilib "$@"
   RunCommand UploadSysrootI386 "$@"
   RunCommand UploadSysrootARM "$@"
   RunCommand UploadSysrootARM64 "$@"
@@ -856,9 +803,6 @@
   if [ "$HAS_ARCH_AMD64" = "1" ]; then
     echo Amd64
   fi
-  if [ "$HAS_ARCH_AMD64MULTILIB" = "1" ]; then
-    echo Amd64Multilib
-  fi
   if [ "$HAS_ARCH_I386" = "1" ]; then
     echo I386
   fi
diff --git a/chrome/VERSION b/chrome/VERSION
index fb186a9..109c2f82 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=78
 MINOR=0
-BUILD=3896
+BUILD=3897
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 62d6bdb..f7823cde 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -566,6 +566,7 @@
   sources = [
     "//chrome/browser/android/customtabs/detached_resource_request.h",
     "//chrome/browser/android/digital_asset_links/digital_asset_links_handler.h",
+    "//chrome/browser/android/download/download_open_source.h",
     "//chrome/browser/android/explore_sites/explore_sites_bridge.h",
     "//chrome/browser/android/explore_sites/explore_sites_feature.h",
     "//chrome/browser/android/feedback/connectivity_checker.cc",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 8b43c56..035c5be 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1804,7 +1804,6 @@
   "java/src/org/chromium/chrome/browser/widget/animation/FocusAnimator.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java",
-  "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetMetrics.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetSwipeDetector.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/EmptyBottomSheetObserver.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index c24256bf..7605f1a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -174,6 +174,7 @@
     public static final String AUTOFILL_KEYBOARD_ACCESSORY = "AutofillKeyboardAccessory";
     public static final String BACKGROUND_TASK_SCHEDULER_FOR_BACKGROUND_SYNC =
             "BackgroundTaskSchedulerForBackgroundSync";
+    public static final String BOOKMARKS_SHOW_IN_FOLDER = "BookmarksShowInFolder";
     public static final String CAPTION_SETTINGS = "CaptionSettings";
     public static final String CAPTIVE_PORTAL_CERTIFICATE_LIST = "CaptivePortalCertificateList";
     public static final String CCT_BACKGROUND_TAB = "CCTBackgroundTab";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 71b877df..309e5da 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -69,6 +69,7 @@
 import org.chromium.chrome.browser.device.DeviceClassManager;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.dom_distiller.ReaderModeManager;
+import org.chromium.chrome.browser.download.DownloadOpenSource;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.feature_engagement.ScreenshotMonitor;
 import org.chromium.chrome.browser.feature_engagement.ScreenshotMonitorDelegate;
@@ -1799,7 +1800,7 @@
                 getToolbarManager().setUrlBarFocus(true);
             }
         } else if (id == R.id.downloads_menu_id) {
-            DownloadUtils.showDownloadManager(this, currentTab);
+            DownloadUtils.showDownloadManager(this, currentTab, DownloadOpenSource.MENU);
             if (currentTabIsNtp) {
                 NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_DOWNLOADS_MANAGER);
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
index 9d663ae..ee54733 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
@@ -38,6 +38,7 @@
     protected BookmarkId mBookmarkId;
     private boolean mIsAttachedToWindow;
     private final boolean mReorderBookmarksEnabled;
+    private final boolean mShowInFolderEnabled;
     private PopupMenuShownListener mPopupListener;
     @Location
     private int mLocation;
@@ -57,6 +58,8 @@
     public BookmarkRow(Context context, AttributeSet attrs) {
         super(context, attrs);
         mReorderBookmarksEnabled = ChromeFeatureList.isEnabled(ChromeFeatureList.REORDER_BOOKMARKS);
+        mShowInFolderEnabled = mReorderBookmarksEnabled
+                && ChromeFeatureList.isEnabled(ChromeFeatureList.BOOKMARKS_SHOW_IN_FOLDER);
     }
 
     /**
@@ -161,7 +164,9 @@
                         new Item(getContext(), R.string.bookmark_item_delete, true)));
         if (mReorderBookmarksEnabled) {
             if (mDelegate.getCurrentState() == BookmarkUIState.STATE_SEARCHING) {
-                menuItems.add(new Item(getContext(), R.string.bookmark_show_in_folder, true));
+                if (mShowInFolderEnabled) {
+                    menuItems.add(new Item(getContext(), R.string.bookmark_show_in_folder, true));
+                }
             } else if (mDelegate.getCurrentState() == BookmarkUIState.STATE_FOLDER
                     && mLocation != Location.SOLO) {
                 // Only add move up / move down buttons if there is more than 1 item
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java
index f987e70d..0a989ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReorderBookmarkItemsAdapter.java
@@ -218,8 +218,10 @@
             });
             // Turn on the highlight for the currently highlighted bookmark.
             if (id.equals(mHighlightedBookmark)) {
-                ViewHighlighter.turnOnHighlight(holder.itemView, false);
+                ViewHighlighter.pulseHighlight(holder.itemView, false, 1);
+                clearHighlight();
             } else {
+                // We need this in case we are change state during a pulse.
                 ViewHighlighter.turnOffHighlight(holder.itemView);
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerImpl.java
index 01c7522a..fab9e95 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerImpl.java
@@ -13,6 +13,8 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import org.chromium.base.Log;
+
 /**
  * Manage multiple SurfaceViews for the compositor, so that transitions between
  * surfaces with and without an alpha channel can be visually smooth.
@@ -89,6 +91,7 @@
         }
 
         public void detachFromParent() {
+            Log.e(TAG, "SurfaceState : detach from parent : " + format);
             final ViewGroup parent = mParent;
             // Since removeView can call surfaceDestroyed before returning, be sure that isAttached
             // will return false.
@@ -101,6 +104,8 @@
         }
     }
 
+    private static final String TAG = "CompositorSurfaceMgr";
+
     // SurfaceView with a translucent PixelFormat.
     private final SurfaceState mTranslucent;
 
@@ -145,6 +150,7 @@
 
     @Override
     public void requestSurface(int format) {
+        Log.e(TAG, "Transitioning to surface with format : " + format);
         mRequestedByClient = (format == PixelFormat.TRANSLUCENT) ? mTranslucent : mOpaque;
 
         // If destruction is pending, then we must wait for it to complete.  When we're notified
@@ -266,6 +272,7 @@
         // Note that |createPending| might not be set, if Android destroyed and recreated this
         // surface on its own.
 
+        Log.e(TAG, "surfaceCreated format : " + state.format);
         if (state != mRequestedByClient) {
             // Surface is created, but it's not the one that's been requested most recently.  Just
             // destroy it again.
@@ -302,6 +309,7 @@
         // and we can clear |destroyPending|.  Otherwise, Android has destroyed this surface while
         // our destroy was posted, and might even return it before it runs.  When the post runs, it
         // can sort that out based on whether the surface is valid or not.
+        Log.e(TAG, "surfaceDestroyed format : " + state.format);
         if (!state.destroyPending) {
             state.createPending = true;
         } else if (!state.isAttached()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
index 734f14d..4ddd715 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
@@ -330,9 +330,11 @@
     }
 
     /**
-     * Called to open a particular download item.  Falls back to opening Download Home.
+     * Called to open a particular download item. Falls back to opening Download Home if
+     * the download cannot be found by android DownloadManager.
      * @param context Context of the receiver.
-     * @param intent Intent from the android DownloadManager.
+     * @param intent Intent from the notification.
+     * @param contentId Content ID of the download.
      */
     private void openDownload(Context context, Intent intent, ContentId contentId) {
         String downloadFilePath = IntentUtils.safeGetStringExtra(
@@ -344,14 +346,15 @@
             long ids[] =
                     intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
             if (ids == null || ids.length == 0) {
-                DownloadManagerService.openDownloadsPage(context);
+                DownloadManagerService.openDownloadsPage(context, DownloadOpenSource.NOTIFICATION);
                 return;
             }
 
             long id = ids[0];
             DownloadManagerBridge.queryDownloadResult(id, result -> {
                 if (result.contentUri == null) {
-                    DownloadManagerService.openDownloadsPage(context);
+                    DownloadManagerService.openDownloadsPage(
+                            context, DownloadOpenSource.NOTIFICATION);
                     return;
                 }
                 openDownloadWithId(context, intent, id, contentId);
@@ -377,7 +380,7 @@
         String referrer = IntentUtils.safeGetStringExtra(intent, Intent.EXTRA_REFERRER);
         DownloadManagerService.openDownloadedContent(context, downloadFilePath, isSupportedMimeType,
                 isOffTheRecord, contentId.id, id, originalUrl, referrer,
-                DownloadMetrics.DownloadOpenSource.NOTIFICATION, null);
+                DownloadOpenSource.NOTIFICATION, null);
     }
 
     @Nullable
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java
index 09c04972..9593539 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java
@@ -928,10 +928,11 @@
             mTrackedItems.remove(itemId);
             removeNotification(itemId);
             if (itemId != null) {
-                DownloadUtils.openItem(itemId, mIsIncognito,
-                        DownloadMetrics.DownloadOpenSource.DOWNLOAD_PROGRESS_INFO_BAR);
+                DownloadUtils.openItem(
+                        itemId, mIsIncognito, DownloadOpenSource.DOWNLOAD_PROGRESS_INFO_BAR);
             } else {
-                DownloadManagerService.getDownloadManagerService().openDownloadsPage(getContext());
+                DownloadManagerService.getDownloadManagerService().openDownloadsPage(
+                        getContext(), DownloadOpenSource.DOWNLOAD_PROGRESS_INFO_BAR);
             }
             recordLinkClicked(itemId != null);
             closePreviousInfoBar();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index 5f2b0a6..0af94eeb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -36,7 +36,6 @@
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.download.DownloadManagerBridge.DownloadEnqueueRequest;
 import org.chromium.chrome.browser.download.DownloadManagerBridge.DownloadEnqueueResponse;
-import org.chromium.chrome.browser.download.DownloadMetrics.DownloadOpenSource;
 import org.chromium.chrome.browser.download.DownloadNotificationUmaHelper.UmaBackgroundDownload;
 import org.chromium.chrome.browser.download.DownloadNotificationUmaHelper.UmaDownloadResumption;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
@@ -657,7 +656,7 @@
             return;
         }
         openDownloadedContent(download.getDownloadInfo(), download.getSystemDownloadId(),
-                DownloadMetrics.DownloadOpenSource.AUTO_OPEN);
+                DownloadOpenSource.AUTO_OPEN);
     }
 
     /**
@@ -985,7 +984,7 @@
                         && DownloadUtils.fireOpenIntentForDownload(context, intent);
 
                 if (!didLaunchIntent) {
-                    openDownloadsPage(context);
+                    openDownloadsPage(context, source);
                     return;
                 }
 
@@ -1037,9 +1036,10 @@
     /**
      * Open the Activity which shows a list of all downloads.
      * @param context Application context
+     * @param source The source where the user action coming from.
      */
-    public static void openDownloadsPage(Context context) {
-        if (DownloadUtils.showDownloadManager(null, null)) return;
+    public static void openDownloadsPage(Context context, @DownloadOpenSource int source) {
+        if (DownloadUtils.showDownloadManager(null, null, source)) return;
 
         // Open the Android Download Manager.
         Intent pageView = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
@@ -1806,20 +1806,14 @@
     }
 
     @CalledByNative
-    private void showDownloadManager(boolean showPrefetchedContent) {
-        DownloadUtils.showDownloadManager(null, null, showPrefetchedContent);
-    }
-
-    @CalledByNative
-    private void openDownloadItem(
-            DownloadItem downloadItem, @DownloadMetrics.DownloadOpenSource int source) {
+    private void openDownloadItem(DownloadItem downloadItem, @DownloadOpenSource int source) {
         DownloadInfo downloadInfo = downloadItem.getDownloadInfo();
         boolean canOpen =
                 DownloadUtils.openFile(downloadInfo.getFilePath(), downloadInfo.getMimeType(),
                         downloadInfo.getDownloadGuid(), downloadInfo.isOffTheRecord(),
                         downloadInfo.getOriginalUrl(), downloadInfo.getReferrer(), source);
         if (!canOpen) {
-            openDownloadsPage(ContextUtils.getApplicationContext());
+            openDownloadsPage(ContextUtils.getApplicationContext(), source);
         }
     }
 
@@ -1828,8 +1822,7 @@
      * @param id The {@link ContentId} of the download to be opened.
      * @param source The source where the user opened this download.
      */
-    public void openDownload(
-            ContentId id, boolean isOffTheRecord, @DownloadMetrics.DownloadOpenSource int source) {
+    public void openDownload(ContentId id, boolean isOffTheRecord, @DownloadOpenSource int source) {
         nativeOpenDownload(getNativeDownloadManagerService(), id.id, isOffTheRecord, source);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMetrics.java
index b9c039a..9fd4824f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMetrics.java
@@ -4,44 +4,18 @@
 
 package org.chromium.chrome.browser.download;
 
-import android.support.annotation.IntDef;
-
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.download.ui.DownloadFilter;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 
 /**
  * Records download related metrics on Android.
  */
 public class DownloadMetrics {
-    // Tracks where the users interact with download files on Android. Used in histogram.
-    // See AndroidDownloadOpenSource in enums.xml. The values used by this enum will be persisted
-    // to server logs and should not be deleted, changed or reused.
-    @IntDef({DownloadOpenSource.UNKNOWN, DownloadOpenSource.ANDROID_DOWNLOAD_MANAGER,
-            DownloadOpenSource.DOWNLOAD_HOME, DownloadOpenSource.NOTIFICATION,
-            DownloadOpenSource.NEW_TAP_PAGE, DownloadOpenSource.INFO_BAR,
-            DownloadOpenSource.SNACK_BAR, DownloadOpenSource.AUTO_OPEN,
-            DownloadOpenSource.DOWNLOAD_PROGRESS_INFO_BAR})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DownloadOpenSource {
-        int UNKNOWN = 0;
-        int ANDROID_DOWNLOAD_MANAGER = 1;
-        int DOWNLOAD_HOME = 2;
-        int NOTIFICATION = 3;
-        int NEW_TAP_PAGE = 4;
-        int INFO_BAR = 5;
-        int SNACK_BAR = 6;
-        int AUTO_OPEN = 7;
-        int DOWNLOAD_PROGRESS_INFO_BAR = 8;
-        int NUM_ENTRIES = 9;
-    }
-
     private static final String TAG = "DownloadMetrics";
     private static final int MAX_VIEW_RETENTION_MINUTES = 30 * 24 * 60;
 
@@ -60,13 +34,25 @@
         int type = DownloadFilter.fromMimeType(mimeType);
         if (type == DownloadFilter.Type.VIDEO) {
             RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.OpenSource.Video",
-                    source, DownloadOpenSource.NUM_ENTRIES);
+                    source, DownloadOpenSource.MAX_VALUE);
         } else if (type == DownloadFilter.Type.AUDIO) {
             RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.OpenSource.Audio",
-                    source, DownloadOpenSource.NUM_ENTRIES);
+                    source, DownloadOpenSource.MAX_VALUE);
+        } else {
+            RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.OpenSource.Other",
+                    source, DownloadOpenSource.MAX_VALUE);
         }
     }
 
+    public static void recordDownloadPageOpen(@DownloadOpenSource int source) {
+        if (!isNativeLoaded()) {
+            Log.w(TAG, "Native is not loaded, dropping download open metrics.");
+            return;
+        }
+        RecordHistogram.recordEnumeratedHistogram(
+                "Android.DownloadPage.OpenSource", source, DownloadOpenSource.MAX_VALUE);
+    }
+
     /**
      * Records how long does the user keep the download file on disk when the user tries to open
      * the file.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
index 4bb7eca4..de7b709 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
@@ -45,7 +45,8 @@
     @Override
     public void onAction(Object actionData) {
         if (!(actionData instanceof ActionDataInfo)) {
-            DownloadManagerService.openDownloadsPage(ContextUtils.getApplicationContext());
+            DownloadManagerService.openDownloadsPage(
+                    ContextUtils.getApplicationContext(), DownloadOpenSource.SNACK_BAR);
             return;
         }
 
@@ -58,7 +59,7 @@
                                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
             } else {
                 manager.openDownloadedContent(download.downloadInfo, download.systemDownloadId,
-                        DownloadMetrics.DownloadOpenSource.SNACK_BAR);
+                        DownloadOpenSource.SNACK_BAR);
             }
         } else {
             OfflineContentAggregatorNotificationBridgeUiFactory.instance().openItem(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 8c33b46..dbd4e1f7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -148,22 +148,26 @@
      * Displays the download manager UI. Note the UI is different on tablets and on phones.
      * @param activity The current activity is available.
      * @param tab The current tab if it exists.
+     * @param source The source where the user action is coming from.
      * @return Whether the UI was shown.
      */
-    public static boolean showDownloadManager(@Nullable Activity activity, @Nullable Tab tab) {
-        return showDownloadManager(activity, tab, false);
+    public static boolean showDownloadManager(
+            @Nullable Activity activity, @Nullable Tab tab, @DownloadOpenSource int source) {
+        return showDownloadManager(activity, tab, source, false);
     }
 
     /**
      * Displays the download manager UI. Note the UI is different on tablets and on phones.
      * @param activity The current activity is available.
      * @param tab The current tab if it exists.
+     * @param source The source where the user action is coming from.
      * @param showPrefetchedContent Whether the manager should start with prefetched content section
      * expanded.
      * @return Whether the UI was shown.
      */
-    public static boolean showDownloadManager(
-            @Nullable Activity activity, @Nullable Tab tab, boolean showPrefetchedContent) {
+    @CalledByNative
+    public static boolean showDownloadManager(@Nullable Activity activity, @Nullable Tab tab,
+            @DownloadOpenSource int source, boolean showPrefetchedContent) {
         if (FeatureUtilities.isNoTouchModeEnabled()) return false;
         // Figure out what tab was last being viewed by the user.
         if (activity == null) activity = ApplicationStatus.getLastTrackedFocusedActivity();
@@ -223,7 +227,7 @@
             Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
             tracker.notifyEvent(EventConstants.DOWNLOAD_HOME_OPENED);
         }
-
+        DownloadMetrics.recordDownloadPageOpen(source);
         return true;
     }
 
@@ -674,8 +678,8 @@
      * Falls back to open download home.
      * @param contentId The {@link ContentId} of the associated offline item.
      */
-    public static void openItem(ContentId contentId, boolean isOffTheRecord,
-            @DownloadMetrics.DownloadOpenSource int source) {
+    public static void openItem(
+            ContentId contentId, boolean isOffTheRecord, @DownloadOpenSource int source) {
         if (LegacyHelpers.isLegacyOfflinePage(contentId)) {
             OfflineContentAggregatorFactory.get().openItem(LaunchLocation.PROGRESS_BAR, contentId);
         } else {
@@ -697,7 +701,7 @@
      */
     public static boolean openFile(String filePath, String mimeType, String downloadGuid,
             boolean isOffTheRecord, String originalUrl, String referrer,
-            @DownloadMetrics.DownloadOpenSource int source) {
+            @DownloadOpenSource int source) {
         DownloadMetrics.recordDownloadOpen(source, mimeType);
         Context context = ContextUtils.getApplicationContext();
         DownloadManagerService service = DownloadManagerService.getDownloadManagerService();
@@ -736,7 +740,7 @@
             return true;
         } catch (Exception e) {
             // Can't launch the Intent.
-            if (source != DownloadMetrics.DownloadOpenSource.DOWNLOAD_PROGRESS_INFO_BAR) {
+            if (source != DownloadOpenSource.DOWNLOAD_PROGRESS_INFO_BAR) {
                 Toast.makeText(context, context.getString(R.string.download_cant_open_file),
                              Toast.LENGTH_SHORT)
                         .show();
@@ -748,11 +752,11 @@
     @CalledByNative
     private static void openDownload(String filePath, String mimeType, String downloadGuid,
             boolean isOffTheRecord, String originalUrl, String referer,
-            @DownloadMetrics.DownloadOpenSource int source) {
+            @DownloadOpenSource int source) {
         boolean canOpen = DownloadUtils.openFile(
                 filePath, mimeType, downloadGuid, isOffTheRecord, originalUrl, referer, source);
         if (!canOpen) {
-            DownloadUtils.showDownloadManager(null, null);
+            DownloadUtils.showDownloadManager(null, null, source);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
index 4dc981f2..d929060 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -321,8 +321,7 @@
             }
             mFreeSpace = Environment.getExternalStorageDirectory().getUsableSpace();
             DownloadMetrics.recordDownloadOpen(
-                    DownloadMetrics.DownloadOpenSource.ANDROID_DOWNLOAD_MANAGER,
-                    mDownloadInfo.getMimeType());
+                    DownloadOpenSource.ANDROID_DOWNLOAD_MANAGER, mDownloadInfo.getMimeType());
             return omaInfo;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java
index 28a0c6f..efd6f57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java
@@ -13,7 +13,7 @@
 import org.chromium.chrome.browser.download.DownloadItem;
 import org.chromium.chrome.browser.download.DownloadManagerService;
 import org.chromium.chrome.browser.download.DownloadManagerService.DownloadObserver;
-import org.chromium.chrome.browser.download.DownloadMetrics;
+import org.chromium.chrome.browser.download.DownloadOpenSource;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.LegacyHelpers;
@@ -108,7 +108,7 @@
     public void openItem(OfflineItem item) {
         // TODO(shaktisahu): May be pass metrics as a param.
         DownloadManagerService.getDownloadManagerService().openDownload(
-                item.id, item.isOffTheRecord, DownloadMetrics.DownloadOpenSource.DOWNLOAD_HOME);
+                item.id, item.isOffTheRecord, DownloadOpenSource.DOWNLOAD_HOME);
     }
 
     /** @see OfflineContentProvider#removeItem(ContentId) */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
index d35396e..66f1b642 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
@@ -14,6 +14,7 @@
 import org.chromium.chrome.browser.download.DownloadItem;
 import org.chromium.chrome.browser.download.DownloadMetrics;
 import org.chromium.chrome.browser.download.DownloadNotificationService;
+import org.chromium.chrome.browser.download.DownloadOpenSource;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.download.home.metrics.FileExtensions;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
@@ -297,8 +298,7 @@
             if (DownloadUtils.openFile(getFilePath(), getMimeType(),
                         mItem.getDownloadInfo().getDownloadGuid(), isOffTheRecord(),
                         mItem.getDownloadInfo().getOriginalUrl(),
-                        mItem.getDownloadInfo().getReferrer(),
-                        DownloadMetrics.DownloadOpenSource.DOWNLOAD_HOME)) {
+                        mItem.getDownloadInfo().getReferrer(), DownloadOpenSource.DOWNLOAD_HOME)) {
                 recordOpenSuccess();
                 DownloadMetrics.recordDownloadViewRetentionTime(
                         getMimeType(), getItem().getStartTime());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
index da7b94b8..25a2bda 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
@@ -22,7 +22,7 @@
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.download.DownloadManagerService;
-import org.chromium.chrome.browser.download.DownloadMetrics;
+import org.chromium.chrome.browser.download.DownloadOpenSource;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.components.download.DownloadCollectionBridge;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
@@ -102,10 +102,11 @@
                     protected void onPostExecute(String filePath) {
                         if (filePath != null) {
                             DownloadUtils.openFile(filePath, mimeType, null, mIsIncognito, null,
-                                    null, DownloadMetrics.DownloadOpenSource.INFO_BAR);
+                                    null, DownloadOpenSource.INFO_BAR);
                         } else {
                             DownloadManagerService.openDownloadsPage(
-                                    ContextUtils.getApplicationContext());
+                                    ContextUtils.getApplicationContext(),
+                                    DownloadOpenSource.INFO_BAR);
                         }
                     }
                 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorController.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorController.java
index f0b117da..6ff3b4a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorController.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.download.DownloadOpenSource;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.snackbar.Snackbar;
@@ -110,7 +111,8 @@
     @Override
     public void onAction(Object actionData) {
         mIsShowingOfflineIndicator = false;
-        DownloadUtils.showDownloadManager(null, null, true /*showPrefetchedContent*/);
+        DownloadUtils.showDownloadManager(
+                null, null, DownloadOpenSource.OFFLINE_INDICATOR, true /*showPrefetchedContent*/);
         RecordHistogram.recordEnumeratedHistogram(
                 "OfflineIndicator.CTR", OFFLINE_INDICATOR_CTR_CLICKED, OFFLINE_INDICATOR_CTR_COUNT);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchedPagesNotifier.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchedPagesNotifier.java
index 008a13c..0adcffae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchedPagesNotifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchedPagesNotifier.java
@@ -15,6 +15,7 @@
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.download.DownloadOpenSource;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.notifications.ChromeNotification;
 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
@@ -68,7 +69,9 @@
             // TODO(dewittj): Handle the case where we somehow get this broadcast but the Chrome
             // download manager is unavailable.  Today, if this happens then the Android download
             // manager will be launched, and that will not contain any prefetched content.
-            DownloadUtils.showDownloadManager(null, null, true /*showPrefetchedContent*/);
+            DownloadUtils.showDownloadManager(null, null,
+                    DownloadOpenSource.OFFLINE_CONTENT_NOTIFICATION,
+                    true /*showPrefetchedContent*/);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
index c87b5f88..d9cfb5de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
@@ -34,6 +34,32 @@
     private static final long FRAME_RATE = 60;
 
     /**
+     * Informs the PulseDrawable about whether it can continue pulsing, and specifies a callback to
+     * be run when the PulseDrawable is finished pulsing.
+     */
+    public interface PulseEndAuthority {
+        /**
+         * Called at the end of one pulse animation, to decide whether the PulseDrawable can pulse
+         * again.
+         *
+         * @return True iff the PulseDrawable can continue pulsing.
+         */
+        boolean canPulseAgain();
+    }
+
+    /**
+     * A PulseEndAuthority which allows the PulseDrawable to pulse forever.
+     */
+    private static class EndlessPulser implements PulseEndAuthority {
+        // PulseEndAuthority implementation.
+
+        @Override
+        public boolean canPulseAgain() {
+            return true;
+        }
+    }
+
+    /**
      * An interface that does the actual drawing work for this {@link Drawable}.  Not meant to be
      * stateful, as this could be shared across multiple instances of this drawable if it gets
      * copied or mutated.
@@ -80,7 +106,7 @@
     }
 
     private static Painter createCirclePainter(Bounds boundsFn) {
-        return new PulseDrawable.Painter() {
+        return new Painter() {
             @Override
             public void modifyDrawable(PulseDrawable drawable, float interpolation) {
                 drawable.invalidateSelf();
@@ -93,7 +119,8 @@
 
                 float minRadiusPx = boundsFn.getMinRadiusPx(bounds);
                 float maxRadiusPx = boundsFn.getMaxRadiusPx(bounds);
-                float radius = MathUtils.interpolate(minRadiusPx, maxRadiusPx, interpolation);
+                float radius =
+                        MathUtils.interpolate(minRadiusPx, maxRadiusPx, 1.0f - interpolation);
 
                 canvas.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), radius, paint);
             }
@@ -103,13 +130,15 @@
     /**
      * Creates a {@link PulseDrawable} that will fill the bounds with a pulsing color.
      * @param context The {@link Context} under which the drawable is created.
+     * @param pulseEndAuthority The {@link PulseEndAuthority} associated with this drawable.
      * @return A new {@link PulseDrawable} instance.
      */
-    public static PulseDrawable createHighlight(Context context) {
-        PulseDrawable.Painter painter = new PulseDrawable.Painter() {
+    public static PulseDrawable createHighlight(
+            Context context, PulseEndAuthority pulseEndAuthority) {
+        Painter painter = new Painter() {
             @Override
             public void modifyDrawable(PulseDrawable drawable, float interpolation) {
-                drawable.setAlpha((int) MathUtils.interpolate(12, 75, 1.f - interpolation));
+                drawable.setAlpha((int) MathUtils.interpolate(12, 75, interpolation));
             }
 
             @Override
@@ -119,15 +148,28 @@
             }
         };
 
-        return new PulseDrawable(context, new FastOutSlowInInterpolator(), painter);
+        return new PulseDrawable(
+                context, new FastOutSlowInInterpolator(), painter, pulseEndAuthority);
+    }
+
+    /**
+     * Creates a {@link PulseDrawable} that will fill the bounds with a pulsing color. The {@link
+     * PulseDrawable} will continue pulsing forever (if this is not the desired behavior, please use
+     * {@link PulseEndAuthority}).
+     * @param context The {@link Context} under which the drawable is created.
+     * @return A new {@link PulseDrawable} instance.
+     */
+    public static PulseDrawable createHighlight(Context context) {
+        return createHighlight(context, new EndlessPulser());
     }
 
     /**
      * Creates a {@link PulseDrawable} that will draw a pulsing circle inside the bounds.
      * @param context The {@link Context} under which the drawable is created.
+     * @param pulseEndAuthority The {@link PulseEndAuthority} associated with this drawable.
      * @return A new {@link PulseDrawable} instance.
      */
-    public static PulseDrawable createCircle(Context context) {
+    public static PulseDrawable createCircle(Context context, PulseEndAuthority pulseEndAuthority) {
         final int startingPulseRadiusPx =
                 context.getResources().getDimensionPixelSize(R.dimen.iph_pulse_baseline_radius);
 
@@ -142,7 +184,34 @@
                 return Math.min(
                         startingPulseRadiusPx, Math.min(bounds.width(), bounds.height()) / 2.f);
             }
-        });
+        }, pulseEndAuthority);
+    }
+
+    /**
+     * Creates a {@link PulseDrawable} that will draw a pulsing circle inside the bounds. The {@link
+     * PulseDrawable} will continue pulsing forever (if this is not the desired behavior, please use
+     * {@link PulseEndAuthority}).
+     * @param context The {@link Context} under which the drawable is created.
+     * @return A new {@link PulseDrawable} instance.
+     */
+    public static PulseDrawable createCircle(Context context) {
+        return createCircle(context, new EndlessPulser());
+    }
+
+    /**
+     * Creates a {@link PulseDrawable} that will draw a pulsing circle as large as possible inside
+     * the bounds.
+     * @param context The {@link Context} under which the drawable is created.
+     * @return A new {@link PulseDrawable} instance.
+     */
+    public static PulseDrawable createCustomCircle(
+            Context context, Bounds boundsfn, PulseEndAuthority pulseEndAuthority) {
+        Painter painter = createCirclePainter(boundsfn);
+
+        PulseDrawable drawable = new PulseDrawable(context,
+                PathInterpolatorCompat.create(.8f, 0.f, .6f, 1.f), painter, pulseEndAuthority);
+        drawable.setAlpha(76);
+        return drawable;
     }
 
     /**
@@ -152,12 +221,7 @@
      * @return A new {@link PulseDrawable} instance.
      */
     public static PulseDrawable createCustomCircle(Context context, Bounds boundsfn) {
-        Painter painter = createCirclePainter(boundsfn);
-
-        PulseDrawable drawable = new PulseDrawable(
-                context, PathInterpolatorCompat.create(.8f, 0.f, .6f, 1.f), painter);
-        drawable.setAlpha(76);
-        return drawable;
+        return createCustomCircle(context, boundsfn, new EndlessPulser());
     }
 
     private final Runnable mNextFrame = new Runnable() {
@@ -176,20 +240,30 @@
     private PulseState mState;
     private boolean mMutated;
     private boolean mRunning;
+    private long mLastUpdateTime;
+
+    private final PulseEndAuthority mPulseEndAuthority;
 
     /**
      * Creates a new {@link PulseDrawable} instance.
      * @param context The {@link Context} under which the drawable is created.
      * @param interpolator An {@link Interpolator} that defines how the pulse will fade in and out.
      * @param painter      The {@link Painter} that will be responsible for drawing the pulse.
+     * @param pulseEndAuthority The {@link PulseEndAuthority} that is associated with this drawable.
      */
-    private PulseDrawable(Context context, Interpolator interpolator, Painter painter) {
-        this(new PulseState(interpolator, painter));
+    private PulseDrawable(Context context, Interpolator interpolator, Painter painter,
+            PulseEndAuthority pulseEndAuthority) {
+        this(new PulseState(interpolator, painter), pulseEndAuthority);
         setUseLightPulseColor(context.getResources(), false);
     }
 
-    private PulseDrawable(PulseState state) {
+    private PulseDrawable(PulseState state, PulseEndAuthority pulseEndAuthority) {
         mState = state;
+        mPulseEndAuthority = pulseEndAuthority;
+    }
+
+    private PulseDrawable(PulseState state) {
+        this(state, new EndlessPulser());
     }
 
     /**
@@ -222,7 +296,10 @@
             scheduleSelf(mNextFrame, SystemClock.uptimeMillis() + 1000 / FRAME_RATE);
         } else {
             mRunning = true;
-            if (mState.startTime == 0) mState.startTime = SystemClock.uptimeMillis();
+            if (mState.startTime == 0) {
+                mState.startTime = SystemClock.uptimeMillis();
+                mLastUpdateTime = mState.startTime;
+            }
             mNextFrame.run();
         }
     }
@@ -312,16 +389,25 @@
 
     private void stepPulse() {
         long curTime = SystemClock.uptimeMillis();
+        // If we are on a new pulse
+        if ((mLastUpdateTime - mState.startTime) / PULSE_DURATION_MS
+                != (curTime - mState.startTime) / PULSE_DURATION_MS) {
+            if (!(mPulseEndAuthority.canPulseAgain())) {
+                stop();
+                return;
+            }
+        }
         long msIntoAnim = (curTime - mState.startTime) % PULSE_DURATION_MS;
-        float progress = ((float) msIntoAnim) / ((float) PULSE_DURATION_MS);
-        mState.progress = mState.interpolator.getInterpolation(progress);
+        float timeProgress = ((float) msIntoAnim) / ((float) PULSE_DURATION_MS);
+        mState.progress = mState.interpolator.getInterpolation(timeProgress);
         mState.painter.modifyDrawable(PulseDrawable.this, mState.progress);
+        mLastUpdateTime = curTime;
     }
 
     /**
      * The {@link ConstantState} subclass for this {@link PulseDrawable}.
      */
-    private static final class PulseState extends ConstantState {
+    static final class PulseState extends ConstantState {
         // Current Paint State.
         /** The current color, including alpha, to draw. */
         public int drawColor;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseInterpolator.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseInterpolator.java
index 169208d6..be8f108 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseInterpolator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseInterpolator.java
@@ -23,8 +23,8 @@
 
     @Override
     public float getInterpolation(float input) {
-        if (input < 0.4f) return 0.f;
-        if (input < 0.8f) return mInterpolator.getInterpolation((input - 0.4f) / 0.4f);
-        return mInterpolator.getInterpolation(1.f - (input - 0.8f) / 0.2f);
+        if (input < 0.2) return mInterpolator.getInterpolation(input / 0.2f);
+        if (input < 0.6) return 1.f;
+        return mInterpolator.getInterpolation(1.f - (input - 0.6f) / 0.4f);
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ViewHighlighter.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ViewHighlighter.java
index 3267efa..1061c62 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/ViewHighlighter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ViewHighlighter.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.widget;
 
+import static org.chromium.chrome.browser.widget.PulseDrawable.createCircle;
+
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
@@ -28,6 +30,38 @@
     public static final int IPH_MIN_DELAY_BETWEEN_TWO_HIGHLIGHTS = 200;
 
     /**
+     * Allows its associated PulseDrawable to pulse a specified number of times, then turns off the
+     * PulseDrawable highlight.
+     */
+    public static class NumberPulser implements PulseDrawable.PulseEndAuthority {
+        private final View mView;
+        private int mNumPulsesRemaining;
+
+        NumberPulser(View view, int numPulses) {
+            mView = view;
+            mNumPulsesRemaining = numPulses;
+        }
+
+        @Override
+        public boolean canPulseAgain() {
+            mNumPulsesRemaining--;
+            if (mNumPulsesRemaining == 0) ViewHighlighter.turnOffHighlight(mView);
+            return mNumPulsesRemaining > 0;
+        }
+    }
+
+    public static void pulseHighlight(View view, boolean circular, int numPulses) {
+        if (view == null) return;
+
+        PulseDrawable pulseDrawable = circular
+                ? createCircle(view.getContext(), new NumberPulser(view, numPulses))
+                : PulseDrawable.createHighlight(
+                        view.getContext(), new NumberPulser(view, numPulses));
+
+        attachViewAsHighlight(view, pulseDrawable);
+    }
+
+    /**
      * Create a highlight layer over the view.
      * @param view The view to be highlighted.
      * @param circular Whether the highlight should be a circle or rectangle.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
index 030c9e60..a831f6b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -143,9 +143,6 @@
     /** The height of the shadow that sits above the toolbar. */
     private final int mToolbarShadowHeight;
 
-    /** The {@link BottomSheetMetrics} used to record user actions and histograms. */
-    private final BottomSheetMetrics mMetrics;
-
     /** The view that contains the sheet. */
     private ViewGroup mSheetContainer;
 
@@ -412,9 +409,6 @@
         mToolbarShadowHeight =
                 getResources().getDimensionPixelOffset(R.dimen.toolbar_shadow_height);
 
-        mMetrics = new BottomSheetMetrics();
-        addObserver(mMetrics);
-
         mGestureDetector = new BottomSheetSwipeDetector(context, this);
         mIsTouchEnabled = true;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetMetrics.java
deleted file mode 100644
index 379d89b5..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetMetrics.java
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.widget.bottomsheet;
-
-import android.support.annotation.IntDef;
-
-import org.chromium.base.metrics.CachedMetrics;
-import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.BottomSheetContent;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.StateChangeReason;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Records user actions and histograms related to the {@link BottomSheet}.
- */
-public class BottomSheetMetrics extends EmptyBottomSheetObserver {
-    /**
-     * The different ways that the bottom sheet can be opened. This is used to back a UMA
-     * histogram and should therefore be treated as append-only.
-     */
-    @IntDef({SheetOpenReason.OPENED_BY_SWIPE})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface SheetOpenReason {
-        int OPENED_BY_SWIPE = 0;
-        // Obsolete: int OPENED_BY_OMNIBOX_FOCUS = 1;
-        // Obsolete: int OPENED_BY_NEW_TAB_CREATION = 2;
-        // Obsolete: int OPENED_BY_EXPAND_BUTTON = 3;
-        // Obsolete: int OPENED_BY_STARTUP = 4;
-        int NUM_ENTRIES = 5;
-    }
-
-    private static final CachedMetrics.TimesHistogramSample TIMES_FIRST_OPEN =
-            new CachedMetrics.TimesHistogramSample("Android.ChromeHome.TimeToFirstOpen");
-
-    private static final CachedMetrics.TimesHistogramSample TIMES_BETWEEN_CLOSE_AND_NEXT_OPEN =
-            new CachedMetrics.TimesHistogramSample(
-                    "Android.ChromeHome.TimeBetweenCloseAndNextOpen");
-
-    private static final CachedMetrics.TimesHistogramSample TIMES_DURATION_OPEN =
-            new CachedMetrics.TimesHistogramSample("Android.ChromeHome.DurationOpen");
-
-    private static final CachedMetrics.EnumeratedHistogramSample ENUMERATED_OPEN_REASON =
-            new CachedMetrics.EnumeratedHistogramSample(
-                    "Android.ChromeHome.OpenReason", SheetOpenReason.NUM_ENTRIES);
-
-    private static final CachedMetrics.ActionEvent ACTION_HALF_STATE =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.HalfState");
-    private static final CachedMetrics.ActionEvent ACTION_FULL_STATE =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.FullState");
-
-    private static final CachedMetrics.ActionEvent ACTION_OPENED_BY_SWIPE =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.OpenedBySwipe");
-
-    private static final CachedMetrics.ActionEvent ACTION_CLOSED_BY_SWIPE =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.ClosedBySwipe");
-    private static final CachedMetrics.ActionEvent ACTION_CLOSED_BY_BACK_PRESS =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.ClosedByBackPress");
-    private static final CachedMetrics.ActionEvent ACTION_CLOSED_BY_TAP_SCRIM =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.ClosedByTapScrim");
-    private static final CachedMetrics.ActionEvent ACTION_CLOSED_BY_NAVIGATION =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.ClosedByNavigation");
-    private static final CachedMetrics.ActionEvent ACTION_CLOSED =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.Closed");
-
-    /** Whether the sheet is currently open. */
-    private boolean mIsSheetOpen;
-
-    /** The last {@link BottomSheetContent} that was displayed. */
-    private BottomSheetContent mLastContent;
-
-    /** When this class was created. Used as a proxy for when the app was started. */
-    private long mCreationTime;
-
-    /** The last time the sheet was opened. */
-    private long mLastOpenTime;
-
-    /** The last time the sheet was closed. */
-    private long mLastCloseTime;
-
-    public BottomSheetMetrics() {
-        mCreationTime = System.currentTimeMillis();
-    }
-
-    @Override
-    public void onSheetOpened(int reason) {
-        mIsSheetOpen = true;
-
-        boolean isFirstOpen = mLastOpenTime == 0;
-        mLastOpenTime = System.currentTimeMillis();
-
-        if (isFirstOpen) {
-            TIMES_FIRST_OPEN.record(mLastOpenTime - mCreationTime);
-        } else {
-            TIMES_BETWEEN_CLOSE_AND_NEXT_OPEN.record(mLastOpenTime - mLastCloseTime);
-        }
-
-        recordSheetOpenReason(reason);
-    }
-
-    @Override
-    public void onSheetClosed(@StateChangeReason int reason) {
-        mIsSheetOpen = false;
-
-        recordSheetCloseReason(reason);
-
-        mLastCloseTime = System.currentTimeMillis();
-        TIMES_DURATION_OPEN.record(mLastCloseTime - mLastOpenTime);
-    }
-
-    @Override
-    public void onSheetStateChanged(int newState) {
-        if (newState == BottomSheet.SheetState.HALF) {
-            ACTION_HALF_STATE.record();
-        } else if (newState == BottomSheet.SheetState.FULL) {
-            ACTION_FULL_STATE.record();
-        }
-    }
-
-    /**
-     * Records the reason the sheet was opened.
-     * @param reason The {@link StateChangeReason} that caused the bottom sheet to open.
-     */
-    public void recordSheetOpenReason(@StateChangeReason int reason) {
-        @SheetOpenReason
-        int metricsReason = SheetOpenReason.OPENED_BY_SWIPE;
-        switch (reason) {
-            case StateChangeReason.SWIPE:
-                metricsReason = SheetOpenReason.OPENED_BY_SWIPE;
-                ACTION_OPENED_BY_SWIPE.record();
-                break;
-            case StateChangeReason.NONE:
-                // Intentionally empty.
-                break;
-            default:
-                assert false;
-        }
-
-        ENUMERATED_OPEN_REASON.record(metricsReason);
-    }
-
-    /**
-     * Records the reason the sheet was closed.
-     * @param reason The {@link StateChangeReason} that cause the bottom sheet to close.
-     */
-    private void recordSheetCloseReason(@StateChangeReason int reason) {
-        switch (reason) {
-            case StateChangeReason.SWIPE:
-                ACTION_CLOSED_BY_SWIPE.record();
-                break;
-            case StateChangeReason.BACK_PRESS:
-                ACTION_CLOSED_BY_BACK_PRESS.record();
-                break;
-            case StateChangeReason.TAP_SCRIM:
-                ACTION_CLOSED_BY_TAP_SCRIM.record();
-                break;
-            case StateChangeReason.NAVIGATION:
-                ACTION_CLOSED_BY_NAVIGATION.record();
-                break;
-            case StateChangeReason.NONE:
-                ACTION_CLOSED.record();
-                break;
-            default:
-                assert false;
-        }
-    }
-
-    /**
-     * Records that a user navigation instructed the NativePageFactory to create a native page for
-     * the NTP. This may occur if the user has NTP URLs in a tab's navigation history.
-     */
-    public void recordNativeNewTabPageShown() {
-        RecordUserAction.record("Android.ChromeHome.NativeNTPShown");
-    }
-
-    /**
-     * Records that the user tapped the app menu item that triggers the in-product help bubble.
-     */
-    public void recordInProductHelpMenuItemClicked() {
-        RecordUserAction.record("Android.ChromeHome.IPHMenuItemClicked");
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ViewHighlighterTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ViewHighlighterTestUtils.java
index bc27f18..96728a4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ViewHighlighterTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ViewHighlighterTestUtils.java
@@ -9,6 +9,7 @@
 import android.view.View;
 
 import org.chromium.chrome.browser.widget.PulseDrawable;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
 
 /**
  * Allows for testing of views which are highlightable via ViewHighlighter.
@@ -40,4 +41,39 @@
     public static boolean checkHighlightOff(View view) {
         return !(view.getBackground() instanceof LayerDrawable);
     }
+
+    /**
+     * Checks that the view is highlighted with a pulse highlight.
+     *
+     * @param view The view of interest.
+     * @param timeoutDuration The timeout duration (should be set depending on the number of pulses
+     *         and the pulse duration).
+     * @return True iff the view was highlighted, and then turned off.
+     */
+    public static boolean checkHighlightPulse(View view, long timeoutDuration) {
+        try {
+            CriteriaHelper.pollUiThread(()
+                                                -> checkHighlightOn(view),
+                    "Expected highlight to pulse on!", timeoutDuration,
+                    CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+            CriteriaHelper.pollUiThread(()
+                                                -> checkHighlightOff(view),
+                    "Expected highlight to turn off!", timeoutDuration,
+                    CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+        } catch (AssertionError e) {
+            e.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Checks that the view is highlighted with a pulse highlight.
+     *
+     * @param view The view of interest.
+     * @return True iff the view was highlighted, and then turned off.
+     */
+    public static boolean checkHighlightPulse(View view) {
+        return checkHighlightPulse(view, CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL);
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
index fc4a4825..6ce7938 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkReorderTest.java
@@ -10,7 +10,7 @@
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
 
 import static org.chromium.chrome.browser.ViewHighlighterTestUtils.checkHighlightOff;
-import static org.chromium.chrome.browser.ViewHighlighterTestUtils.checkHighlightOn;
+import static org.chromium.chrome.browser.ViewHighlighterTestUtils.checkHighlightPulse;
 
 import android.support.test.filters.MediumTest;
 import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -609,47 +609,49 @@
 
     @Test
     @MediumTest
+    @Features.DisableFeatures(ChromeFeatureList.BOOKMARKS_SHOW_IN_FOLDER)
+    public void testShowInFolderDisabled() throws Exception {
+        addFolder(TEST_FOLDER_TITLE);
+        forceSyncHeaderState();
+        openBookmarkManager();
+        enterSearch();
+        clickMoreButtonOnFirstItem(TEST_FOLDER_TITLE);
+
+        onView(withText("Show in folder")).check(doesNotExist());
+    }
+
+    @Test
+    @MediumTest
     public void testShowInFolder_NoScroll() throws Exception {
         addFolder(TEST_FOLDER_TITLE);
         forceSyncHeaderState();
         openBookmarkManager();
 
         // Enter search mode.
-        View searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id);
-        TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick);
-        CriteriaHelper.pollUiThread(
-                () -> mManager.getToolbarForTests().isSearching(), "Expected to enter search mode");
+        enterSearch();
 
         // Click "Show in folder".
         View testFolder = mItemsContainer.findViewHolderForAdapterPosition(0).itemView;
-        View more = testFolder.findViewById(R.id.more);
-        Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
-                ((BookmarkFolderRow) testFolder).getTitle());
-        TestThreadUtils.runOnUiThreadBlocking(more::performClick);
+        clickMoreButtonOnFirstItem(TEST_FOLDER_TITLE);
         onView(withText("Show in folder")).perform(click());
 
-        Assert.assertTrue(
-                "Expected bookmark row to be highlighted after clicking \"show in folder\"",
-                checkHighlightOn(testFolder));
+        // Assert that the view pulses.
+        Assert.assertTrue("Expected bookmark row to pulse after clicking \"show in folder\"!",
+                checkHighlightPulse(testFolder));
 
         // Enter search mode again.
-        searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id);
-        TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick);
-        CriteriaHelper.pollUiThread(
-                () -> mManager.getToolbarForTests().isSearching(), "Expected to enter search mode");
+        enterSearch();
 
         Assert.assertTrue("Expected bookmark row to not be highlighted "
                         + "after entering search mode",
                 checkHighlightOff(testFolder));
 
         // Click "Show in folder" again.
-        TestThreadUtils.runOnUiThreadBlocking(more::performClick);
+        clickMoreButtonOnFirstItem(TEST_FOLDER_TITLE);
         onView(withText("Show in folder")).perform(click());
-
-        // Check that the highlight is on.
-        Assert.assertTrue("Expected highlight to successfully come back on"
-                        + " after clicking \"show in folder\" a 2nd time",
-                checkHighlightOn(testFolder));
+        Assert.assertTrue(
+                "Expected bookmark row to pulse after clicking \"show in folder\" a 2nd time!",
+                checkHighlightPulse(testFolder));
     }
 
     @Test
@@ -667,22 +669,16 @@
         openBookmarkManager();
 
         // Enter search mode.
-        View searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id);
-        TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick);
-        CriteriaHelper.pollUiThread(
-                () -> mManager.getToolbarForTests().isSearching(), "Expected to enter search mode");
+        enterSearch();
+
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> mManager.onSearchTextChanged(TEST_FOLDER_TITLE));
         RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
 
         // This should be the only (& therefore 0-indexed) item.
-        View testFolderInSearch = mItemsContainer.findViewHolderForAdapterPosition(0).itemView;
-        View more = testFolderInSearch.findViewById(R.id.more);
-        Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
-                ((BookmarkFolderRow) testFolderInSearch).getTitle());
+        clickMoreButtonOnFirstItem(TEST_FOLDER_TITLE);
 
         // Show in folder.
-        TestThreadUtils.runOnUiThreadBlocking(more::performClick);
         onView(withText("Show in folder")).perform(click());
 
         // This should be in the 8th position now.
@@ -691,8 +687,8 @@
                 "Expected list to scroll bookmark item into view", testFolderInList == null);
         Assert.assertEquals("Wrong bookmark item selected.", TEST_FOLDER_TITLE,
                 ((BookmarkFolderRow) testFolderInList.itemView).getTitle());
-        Assert.assertTrue("Expected bookmark item to be highlighted after scrolling to it.",
-                checkHighlightOn(testFolderInList.itemView));
+        Assert.assertTrue("Expected highlight to pulse on after scrolling to the item!",
+                checkHighlightPulse(testFolderInList.itemView));
     }
 
     @Test
@@ -705,30 +701,23 @@
         openBookmarkManager();
 
         // Enter search mode.
-        View searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id);
-        TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick);
-        CriteriaHelper.pollUiThread(
-                () -> mManager.getToolbarForTests().isSearching(), "Expected to enter search mode");
+        enterSearch();
         TestThreadUtils.runOnUiThreadBlocking(() -> mManager.onSearchTextChanged(TEST_URL_A));
         RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
 
         // This should be the only (& therefore 0-indexed) item.
-        View itemAInSearch = mItemsContainer.findViewHolderForAdapterPosition(0).itemView;
-        View more = itemAInSearch.findViewById(R.id.more);
-        Assert.assertEquals("Wrong bookmark item selected.", TEST_TITLE_A,
-                ((BookmarkItemRow) itemAInSearch).getTitle());
+        clickMoreButtonOnFirstItem(TEST_TITLE_A);
 
         // Show in folder.
-        TestThreadUtils.runOnUiThreadBlocking(more::performClick);
         onView(withText("Show in folder")).perform(click());
 
         // Make sure that we're in the right folder (index 1 because of promo).
         View itemA = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
         Assert.assertEquals("Wrong bookmark item selected.", TEST_TITLE_A,
                 ((BookmarkItemRow) itemA).getTitle());
-        Assert.assertTrue(
-                "Expected bookmark item to be highlighted after opening it in new folder.",
-                checkHighlightOn(itemA));
+
+        Assert.assertTrue("Expected highlight to pulse after opening an item in another folder!",
+                checkHighlightPulse(itemA));
 
         // Open mobile bookmarks folder, then go back to the subfolder.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -737,12 +726,12 @@
         });
         RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
 
-        itemA = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
+        View itemASecondView = mItemsContainer.findViewHolderForAdapterPosition(1).itemView;
         Assert.assertEquals("Wrong bookmark item selected.", TEST_TITLE_A,
-                ((BookmarkItemRow) itemA).getTitle());
-        Assert.assertTrue("Expected bookmark item to not be highlighted after "
-                        + "exiting and re-entering folder.",
-                checkHighlightOff(itemA));
+                ((BookmarkItemRow) itemASecondView).getTitle());
+        Assert.assertTrue(
+                "Expected highlight to not be highlighted after exiting and re-entering folder!",
+                checkHighlightOff(itemASecondView));
     }
 
     @Override
@@ -810,6 +799,22 @@
         return (ReorderBookmarkItemsAdapter) getAdapter();
     }
 
+    private void enterSearch() throws Exception {
+        View searchButton = mManager.getToolbarForTests().findViewById(R.id.search_menu_id);
+        TestThreadUtils.runOnUiThreadBlocking(searchButton::performClick);
+        CriteriaHelper.pollUiThread(
+                () -> mManager.getToolbarForTests().isSearching(), "Expected to enter search mode");
+    }
+
+    private void clickMoreButtonOnFirstItem(String expectedBookmarkItemTitle) throws Exception {
+        View firstItem = mItemsContainer.findViewHolderForAdapterPosition(0).itemView;
+        Assert.assertEquals("Wrong bookmark item selected.", expectedBookmarkItemTitle,
+                firstItem instanceof BookmarkItemRow ? ((BookmarkItemRow) firstItem).getTitle()
+                                                     : ((BookmarkFolderRow) firstItem).getTitle());
+        View more = firstItem.findViewById(R.id.more);
+        TestThreadUtils.runOnUiThreadBlocking(more::performClick);
+    }
+
     @Override
     protected BookmarkManager getBookmarkManager() {
         return (BookmarkManager) getReorderAdapter().getDelegateForTesting();
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 53ce108..219ef53 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -76,6 +76,12 @@
 #define IDC_SITE_SETTINGS               34062
 #define IDC_HOSTED_APP_MENU_APP_INFO    34063
 
+#if defined(OS_CHROMEOS)
+// Terminal system app commands
+#define IDC_TERMINAL_SPLIT_HORIZONTAL   34070
+#define IDC_TERMINAL_SPLIT_VERTICAL     34071
+#endif
+
 // Page-related commands
 #define IDC_BOOKMARK_THIS_TAB           35000
 #define IDC_BOOKMARK_ALL_TABS           35001
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index ad7f42a..39b5854 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7015,6 +7015,14 @@
                    desc="Text for the button that installs the Android app. (In sentence case.)">
             Install
           </message>
+          <message name="IDS_APP_TERMINAL_SPLIT_HORIZONTAL"
+                   desc="Text for the menu item to create a horizontal split in terminal app">
+            Split horizontal
+          </message>
+          <message name="IDS_APP_TERMINAL_SPLIT_VERTICAL"
+                   desc="Text for the menu item to create a vertical split in terminal app">
+            Split vertical
+          </message>
         </if>
       </if>
 
@@ -7080,6 +7088,14 @@
                    desc="Text for the button that installs the Android app. (In title case.)">
             Install
           </message>
+          <message name="IDS_APP_TERMINAL_SPLIT_HORIZONTAL"
+                   desc="Text for the menu item to create a horizontal split in terminal app">
+            Split Horizontal
+          </message>
+          <message name="IDS_APP_TERMINAL_SPLIT_VERTICAL"
+                   desc="Text for the menu item to create a vertical split in terminal app">
+            Split Vertical
+          </message>
         </if>
       </if>
 
diff --git a/chrome/app/generated_resources_grd/IDS_APP_TERMINAL_SPLIT_HORIZONTAL.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_TERMINAL_SPLIT_HORIZONTAL.png.sha1
new file mode 100644
index 0000000..137397f8
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_TERMINAL_SPLIT_HORIZONTAL.png.sha1
@@ -0,0 +1 @@
+dc4a293ba7538023f5fdc6d387307b3a0754df04
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_APP_TERMINAL_SPLIT_VERTICAL.png.sha1 b/chrome/app/generated_resources_grd/IDS_APP_TERMINAL_SPLIT_VERTICAL.png.sha1
new file mode 100644
index 0000000..137397f8
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_APP_TERMINAL_SPLIT_VERTICAL.png.sha1
@@ -0,0 +1 @@
+dc4a293ba7538023f5fdc6d387307b3a0754df04
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/README.md b/chrome/app/generated_resources_grd/README.md
new file mode 100644
index 0000000..f013bb5
--- /dev/null
+++ b/chrome/app/generated_resources_grd/README.md
@@ -0,0 +1,5 @@
+This directory of image SHA-1 hashes is used to improve translations of UI
+strings through context images for translators.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5012697..e55278d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2407,6 +2407,7 @@
       "android/download/download_media_parser.h",
       "android/download/download_media_parser_bridge.cc",
       "android/download/download_media_parser_bridge.h",
+      "android/download/download_open_source.h",
       "android/download/download_startup_utils.cc",
       "android/download/download_startup_utils.h",
       "android/download/download_utils.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6b9cd5a0..ceef9c66 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2148,7 +2148,7 @@
      FEATURE_VALUE_TYPE(features::kWebXrHitTest)},
     {"webxr-anchors", flag_descriptions::kWebXrAnchorsName,
      flag_descriptions::kWebXrAnchorsDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kWebXrPlaneDetection)},
+     FEATURE_VALUE_TYPE(features::kWebXrAnchors)},
     {"webxr-plane-detection", flag_descriptions::kWebXrPlaneDetectionName,
      flag_descriptions::kWebXrPlaneDetectionDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kWebXrPlaneDetection)},
@@ -4452,6 +4452,15 @@
      flag_descriptions::kNewOverviewTabletLayoutName,
      flag_descriptions::kNewOverviewTabletLayoutDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kNewOverviewLayout)},
+
+    {"aggregated-ml-app-ranking",
+     flag_descriptions::kAggregatedMlAppRankingName,
+     flag_descriptions::kAggregatedMlAppRankingDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(app_list_features::kEnableAggregatedMlAppRanking)},
+
+    {"scalable-app-list", flag_descriptions::kScalableAppListName,
+     flag_descriptions::kScalableAppListDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(app_list_features::kScalableAppList)},
 #endif  // defined(OS_CHROMEOS)
 
     {"passwords-account-storage",
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index e0d4aab34..767e577 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -90,6 +90,7 @@
     &kAndroidSearchEngineChoiceNotification,
     &kAndroidSiteSettingsUIRefresh,
     &kBackgroundTaskSchedulerForBackgroundSync,
+    &kBookmarksShowInFolder,
     &kCastDeviceFilter,
     &kCloseTabSuggestionsStale,
     &kCCTBackgroundTab,
@@ -278,6 +279,9 @@
     "BackgroundTaskSchedulerForBackgroundSync",
     base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kBookmarksShowInFolder{"BookmarksShowInFolder",
+                                           base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Used in downstream code.
 const base::Feature kCastDeviceFilter{"CastDeviceFilter",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 17d5d62..98988c4 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -25,6 +25,7 @@
 extern const base::Feature kAndroidSiteSettingsUIRefresh;
 extern const base::Feature kBackgroundTaskComponentUpdate;
 extern const base::Feature kBackgroundTaskSchedulerForBackgroundSync;
+extern const base::Feature kBookmarksShowInFolder;
 extern const base::Feature kCloseTabSuggestionsStale;
 extern const base::Feature kCastDeviceFilter;
 extern const base::Feature kCCTBackgroundTab;
diff --git a/chrome/browser/android/download/available_offline_content_provider.cc b/chrome/browser/android/download/available_offline_content_provider.cc
index 9c0ad8a..795682f 100644
--- a/chrome/browser/android/download/available_offline_content_provider.cc
+++ b/chrome/browser/android/download/available_offline_content_provider.cc
@@ -13,7 +13,8 @@
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "chrome/browser/android/download/download_manager_service.h"
+#include "chrome/browser/android/download/download_open_source.h"
+#include "chrome/browser/android/download/download_utils.h"
 #include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
@@ -247,8 +248,9 @@
 
 void AvailableOfflineContentProvider::LaunchDownloadsPage(
     bool open_prefetched_articles_tab) {
-  DownloadManagerService::GetInstance()->ShowDownloadManager(
-      open_prefetched_articles_tab);
+  DownloadUtils::ShowDownloadManager(
+      open_prefetched_articles_tab,
+      DownloadOpenSource::kDinoPageOfflineContent);
 }
 
 void AvailableOfflineContentProvider::ListVisibilityChanged(bool is_visible) {
diff --git a/chrome/browser/android/download/download_manager_service.cc b/chrome/browser/android/download/download_manager_service.cc
index e7939ce7..fea54a9 100644
--- a/chrome/browser/android/download/download_manager_service.cc
+++ b/chrome/browser/android/download/download_manager_service.cc
@@ -241,12 +241,6 @@
   }
 }
 
-void DownloadManagerService::ShowDownloadManager(bool show_prefetched_content) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_DownloadManagerService_showDownloadManager(
-      env, java_ref_, static_cast<jboolean>(show_prefetched_content));
-}
-
 void DownloadManagerService::OpenDownload(download::DownloadItem* download,
                                           int source) {
   if (java_ref_.is_null())
diff --git a/chrome/browser/android/download/download_manager_service.h b/chrome/browser/android/download/download_manager_service.h
index 4b70b810..8af5bce 100644
--- a/chrome/browser/android/download/download_manager_service.h
+++ b/chrome/browser/android/download/download_manager_service.h
@@ -57,10 +57,6 @@
   // Called when full browser process starts.
   void OnFullBrowserStarted(JNIEnv* env, jobject obj);
 
-  // Called to show the download manager, with a choice to focus on prefetched
-  // content instead of regular downloads.
-  void ShowDownloadManager(bool show_prefetched_content);
-
   // Called to handle subsequent steps, after a download was determined as a OMA
   // download type.
   void HandleOMADownload(download::DownloadItem* download,
diff --git a/chrome/browser/android/download/download_open_source.h b/chrome/browser/android/download/download_open_source.h
new file mode 100644
index 0000000..ff4b48b
--- /dev/null
+++ b/chrome/browser/android/download/download_open_source.h
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_DOWNLOAD_DOWNLOAD_OPEN_SOURCE_H_
+#define CHROME_BROWSER_ANDROID_DOWNLOAD_DOWNLOAD_OPEN_SOURCE_H_
+
+// Tracks where the users interact with download files on Android. Used in
+// histogram. See AndroidDownloadOpenSource in enums.xml. The values used by
+// this enum will be persisted to server logs and should not be deleted, changed
+// or reused.
+// GENERATED_JAVA_ENUM_PACKAGE: (
+//   org.chromium.chrome.browser.download)
+enum class DownloadOpenSource {
+  // The action source is unknown.
+  kUnknown = 0,
+  // Android DownloadManager.
+  kAndroidDownloadManager = 1,
+  // Download home page.
+  kDownloadHome = 2,
+  // Android download notification.
+  kNotification = 3,
+  // New tab page.
+  kNewTabPage = 4,
+  // Download info bar.
+  kInfoBar = 5,
+  // Download snack bar.
+  kSnackBar = 6,
+  // Download is auto opened after completion.
+  kAutoOpen = 7,
+  // Download progress info bar.
+  kDownloadProgressInfoBar = 8,
+  // Main menu.
+  kMenu = 9,
+  // Offline content on Dino page
+  kDinoPageOfflineContent = 10,
+  // Offline indicator.
+  kOfflineIndicator = 11,
+  // Android notification for offline content.
+  kOfflineContentNotification = 12,
+  kMaxValue = kOfflineContentNotification
+};
+
+#endif  // CHROME_BROWSER_ANDROID_DOWNLOAD_DOWNLOAD_OPEN_SOURCE_H_
diff --git a/chrome/browser/android/download/download_utils.cc b/chrome/browser/android/download/download_utils.cc
index 27ce7c4..930fa245 100644
--- a/chrome/browser/android/download/download_utils.cc
+++ b/chrome/browser/android/download/download_utils.cc
@@ -79,7 +79,7 @@
 
 // static
 void DownloadUtils::OpenDownload(download::DownloadItem* item,
-                                 int open_source) {
+                                 DownloadOpenSource open_source) {
   JNIEnv* env = base::android::AttachCurrentThread();
   content::BrowserContext* browser_context =
       content::DownloadItemUtils::GetBrowserContext(item);
@@ -94,7 +94,8 @@
       ConvertUTF8ToJavaString(env, item->GetMimeType()),
       ConvertUTF8ToJavaString(env, item->GetGuid()), is_off_the_record,
       ConvertUTF8ToJavaString(env, original_url),
-      ConvertUTF8ToJavaString(env, item->GetReferrerUrl().spec()), open_source);
+      ConvertUTF8ToJavaString(env, item->GetReferrerUrl().spec()),
+      static_cast<jint>(open_source));
 }
 
 // static
@@ -122,3 +123,12 @@
   return base::EqualsCaseInsensitiveASCII(mime_type,
                                           kOmaDownloadDescriptorMimeType);
 }
+
+// static
+void DownloadUtils::ShowDownloadManager(bool show_prefetched_content,
+                                        DownloadOpenSource open_source) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_DownloadUtils_showDownloadManager(
+      env, nullptr, nullptr, static_cast<jint>(open_source),
+      static_cast<jboolean>(show_prefetched_content));
+}
diff --git a/chrome/browser/android/download/download_utils.h b/chrome/browser/android/download/download_utils.h
index 35ce279d..20a811a 100644
--- a/chrome/browser/android/download/download_utils.h
+++ b/chrome/browser/android/download/download_utils.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/files/file_path.h"
+#include "chrome/browser/android/download/download_open_source.h"
 
 namespace download {
 class DownloadItem;
@@ -20,12 +21,19 @@
  public:
   static base::FilePath GetUriStringForPath(const base::FilePath& file_path);
   static int GetAutoResumptionSizeLimit();
-  static void OpenDownload(download::DownloadItem* item, int open_source);
+  static void OpenDownload(download::DownloadItem* item,
+                           DownloadOpenSource open_source);
   static std::string RemapGenericMimeType(const std::string& mime_type,
                                           const GURL& url,
                                           const std::string& file_name);
   static bool ShouldAutoOpenDownload(download::DownloadItem* item);
   static bool IsOmaDownloadDescription(const std::string& mime_type);
+
+  // Called to show the download manager, with a choice to focus on prefetched
+  // content instead of regular downloads. |download_open_source| is the source
+  // of the action.
+  static void ShowDownloadManager(bool show_prefetched_content,
+                                  DownloadOpenSource open_source);
 };
 
 #endif  // CHROME_BROWSER_ANDROID_DOWNLOAD_DOWNLOAD_UTILS_H_
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
index e5d8e1a..29f1ca2 100644
--- a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
+++ b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h"
 
+#include <string>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/android/oom_intervention/oom_intervention_config.h"
@@ -14,6 +17,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/oom_intervention/oom_intervention_types.h"
 
 namespace {
@@ -323,8 +327,7 @@
   DCHECK(main_frame);
   content::RenderProcessHost* render_process_host = main_frame->GetProcess();
   DCHECK(render_process_host);
-  content::BindInterface(render_process_host,
-                         mojo::MakeRequest(&intervention_));
+  render_process_host->BindReceiver(intervention_.BindNewPipeAndPassReceiver());
   DCHECK(!receiver_.is_bound());
   blink::mojom::DetectionArgsPtr detection_args =
       config->GetRendererOomDetectionArgs();
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h
index 67129ae..a862a25d 100644
--- a/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h
+++ b/chrome/browser/android/oom_intervention/oom_intervention_tab_helper.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_ANDROID_OOM_INTERVENTION_OOM_INTERVENTION_TAB_HELPER_H_
 #define CHROME_BROWSER_ANDROID_OOM_INTERVENTION_OOM_INTERVENTION_TAB_HELPER_H_
 
+#include <memory>
+
 #include "base/memory/unsafe_shared_memory_region.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
@@ -16,7 +18,7 @@
 #include "components/crash/content/browser/crash_metrics_reporter_android.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/mojom/oom_intervention/oom_intervention.mojom.h"
@@ -94,7 +96,7 @@
   // Not owned. This will be nullptr in incognito mode.
   OomInterventionDecider* decider_;
 
-  blink::mojom::OomInterventionPtr intervention_;
+  mojo::Remote<blink::mojom::OomIntervention> intervention_;
 
   enum class InterventionState {
     // Intervention isn't triggered yet.
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc
index da37ea4..cfdee4bc 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.cc
+++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/apps/app_service/app_icon_factory.h"
 
+#include <map>
 #include <utility>
 #include <vector>
 
@@ -11,6 +12,7 @@
 #include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/no_destructor.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/threading/scoped_blocking_call.h"
@@ -19,6 +21,7 @@
 #include "chrome/browser/extensions/chrome_app_icon.h"
 #include "chrome/browser/extensions/chrome_app_icon_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/system_connector.h"
 #include "extensions/browser/component_extension_resource_manager.h"
 #include "extensions/browser/extension_system.h"
@@ -41,6 +44,12 @@
 
 namespace {
 
+std::map<std::pair<int, int>, gfx::ImageSkia>& GetResourceIconCache() {
+  static base::NoDestructor<std::map<std::pair<int, int>, gfx::ImageSkia>>
+      cache;
+  return *cache;
+}
+
 std::vector<uint8_t> ReadFileAsCompressedData(const base::FilePath path) {
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
@@ -321,6 +330,7 @@
                            const std::string& extension_id,
                            IconEffects icon_effects,
                            apps::mojom::Publisher::LoadIconCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   constexpr bool is_placeholder_icon = false;
 
   // This is the default icon for AppType::kExtension. Other app types might
@@ -374,6 +384,7 @@
     apps::mojom::Publisher::LoadIconCallback callback,
     base::OnceCallback<void(apps::mojom::Publisher::LoadIconCallback)>
         fallback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   constexpr bool is_placeholder_icon = false;
   switch (icon_compression) {
     case apps::mojom::IconCompression::kUnknown:
@@ -402,6 +413,7 @@
                           bool is_placeholder_icon,
                           IconEffects icon_effects,
                           apps::mojom::Publisher::LoadIconCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // This must be zero, to avoid a potential infinite loop if the
   // RunCallbackWithXxx functions could otherwise call back into
   // LoadIconFromResource.
@@ -414,6 +426,8 @@
 
       case apps::mojom::IconCompression::kUncompressed:
       case apps::mojom::IconCompression::kCompressed: {
+        // For compressed icons with no |icon_effects|, serve the
+        // already-compressed bytes.
         if (icon_compression == apps::mojom::IconCompression::kCompressed &&
             icon_effects == IconEffects::kNone) {
           base::StringPiece data =
@@ -426,18 +440,35 @@
           return;
         }
 
-        // 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.
-        gfx::ImageSkia* unscaled =
-            ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-                resource_id);
-        RunCallbackWithImageSkia(
-            size_hint_in_dip, default_icon_resource, is_placeholder_icon,
-            icon_effects, icon_compression, std::move(callback),
-            gfx::ImageSkiaOperations::CreateResizedImage(
-                *unscaled, skia::ImageOperations::RESIZE_BEST,
-                gfx::Size(size_hint_in_dip, size_hint_in_dip)));
+        // 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.
+
+        // Get the ImageSkia for the resource. The ui::ResourceBundle shared
+        // instance already caches ImageSkia's, but caches the unscaled
+        // versions. The |cache| here caches scaled versions, keyed by the pair
+        // (resource_id, size_hint_in_dip).
+        gfx::ImageSkia scaled;
+        std::map<std::pair<int, int>, gfx::ImageSkia>& cache =
+            GetResourceIconCache();
+        const auto cache_key = std::make_pair(resource_id, size_hint_in_dip);
+        const auto cache_iter = cache.find(cache_key);
+        if (cache_iter != cache.end()) {
+          scaled = cache_iter->second;
+        } else {
+          gfx::ImageSkia* unscaled =
+              ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+                  resource_id);
+          scaled = gfx::ImageSkiaOperations::CreateResizedImage(
+              *unscaled, skia::ImageOperations::RESIZE_BEST,
+              gfx::Size(size_hint_in_dip, size_hint_in_dip));
+          cache.insert(std::make_pair(cache_key, scaled));
+        }
+
+        // Apply icon effects, re-encode if necessary and run the callback.
+        RunCallbackWithImageSkia(size_hint_in_dip, default_icon_resource,
+                                 is_placeholder_icon, icon_effects,
+                                 icon_compression, std::move(callback), scaled);
         return;
       }
     }
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index 16fe2370..d345c53 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -18,12 +18,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_dialog.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
-#include "chrome/browser/ui/app_list/arc/arc_app_icon_descriptor.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/grit/component_extension_resources.h"
 #include "components/arc/app_permissions/arc_app_permissions_bridge.h"
 #include "components/arc/arc_service_manager.h"
-#include "components/arc/mojom/app.mojom.h"
 #include "components/arc/mojom/app_permissions.mojom.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "content/public/browser/system_connector.h"
@@ -41,6 +39,7 @@
 namespace {
 
 void OnArcAppIconCompletelyLoaded(
+    apps::mojom::IconCompression icon_compression,
     int32_t size_hint_in_dip,
     apps::IconEffects icon_effects,
     apps::mojom::Publisher::LoadIconCallback callback,
@@ -51,88 +50,31 @@
   }
 
   apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
-  iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
-  iv->uncompressed = icon->image_skia();
+  iv->icon_compression = icon_compression;
   iv->is_placeholder_icon = false;
 
-  if (icon_effects != apps::IconEffects::kNone) {
-    apps::ApplyIconEffects(icon_effects, size_hint_in_dip, &iv->uncompressed);
-  }
-
-  std::move(callback).Run(std::move(iv));
-}
-
-// ArcApps::LoadIcon (via ArcApps::LoadIconFromVM) runs a series of callbacks,
-// defined here in back-to-front order so that e.g. the compiler knows
-// LoadIcon1's signature when compiling LoadIcon0 (which binds LoadIcon1).
-//
-//  - LoadIcon0 is called back when the AppConnectionHolder is connected.
-//  - LoadIcon1 is called back when the compressed (PNG) image is loaded.
-
-void LoadIcon1(apps::mojom::IconCompression icon_compression,
-               apps::mojom::Publisher::LoadIconCallback callback,
-               const std::vector<uint8_t>& icon_png_data) {
-  switch (icon_compression) {
-    case apps::mojom::IconCompression::kUnknown:
+  if (icon_compression == apps::mojom::IconCompression::kUncompressed) {
+    iv->uncompressed = icon->image_skia();
+    if (icon_effects != apps::IconEffects::kNone) {
+      apps::ApplyIconEffects(icon_effects, size_hint_in_dip, &iv->uncompressed);
+    }
+  } else {
+    auto& compressed_images = icon->compressed_images();
+    auto iter =
+        compressed_images.find(apps_util::GetPrimaryDisplayUIScaleFactor());
+    if (iter == compressed_images.end()) {
       std::move(callback).Run(apps::mojom::IconValue::New());
-      break;
-
-    case apps::mojom::IconCompression::kUncompressed:
-      LOG(ERROR) << "unexpected ArcApps icon_compression";
-      std::move(callback).Run(apps::mojom::IconValue::New());
-      break;
-
-    case apps::mojom::IconCompression::kCompressed:
-      apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
-      iv->icon_compression = apps::mojom::IconCompression::kCompressed;
-      iv->compressed = icon_png_data;
-      iv->is_placeholder_icon = false;
-      std::move(callback).Run(std::move(iv));
-      break;
-  }
-}
-
-void LoadIcon0(apps::mojom::IconCompression icon_compression,
-               int size_hint_in_px,
-               std::string package_name,
-               std::string activity,
-               std::string icon_resource_id,
-               apps::mojom::Publisher::LoadIconCallback callback,
-               apps::ArcApps::AppConnectionHolder* app_connection_holder) {
-  // TODO(crbug.com/826982): consider that, per khmel@, "Regardless the number
-  // of request for the same icon scale it should be only one and only one
-  // request to ARC++ container to extract the real data. This logic is
-  // isolated inside ArcAppListPrefs and I don't think that anybody else should
-  // call mojom RequestAppIcon".
-  //
-  // Note that this TODO will be obsolete when the LoadIcon0 code is deleted,
-  // which depends on ArcAppIcon being able to provide *compressed* icons
-  // (PNG-encoded bytes, not RGBA pixels).
-  if (app_connection_holder) {
-    if (icon_resource_id.empty()) {
-      auto* app_instance =
-          ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder, RequestAppIcon);
-      if (app_instance) {
-        app_instance->RequestAppIcon(
-            package_name, activity, size_hint_in_px,
-            base::BindOnce(&LoadIcon1, icon_compression, std::move(callback)));
-        return;
-      }
-
-    } else {
-      auto* app_instance = ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder,
-                                                       RequestShortcutIcon);
-      if (app_instance) {
-        app_instance->RequestShortcutIcon(
-            icon_resource_id, size_hint_in_px,
-            base::BindOnce(&LoadIcon1, icon_compression, std::move(callback)));
-        return;
-      }
+      return;
+    }
+    const std::string& data = iter->second;
+    iv->compressed = std::vector<uint8_t>(data.begin(), data.end());
+    if (icon_effects != apps::IconEffects::kNone) {
+      // TODO(crbug.com/988321): decompress the image, apply icon effects then
+      // re-compress.
     }
   }
 
-  // On failure, we still run the callback, with the zero IconValue.
-  std::move(callback).Run(apps::mojom::IconValue::New());
+  std::move(callback).Run(std::move(iv));
 }
 
 void UpdateAppPermissions(
@@ -188,7 +130,6 @@
     return;
   }
   prefs->AddObserver(this);
-  prefs->app_connection_holder()->AddObserver(this);
 
   apps::mojom::PublisherPtr publisher;
   binding_.Bind(mojo::MakeRequest(&publisher));
@@ -196,14 +137,7 @@
                                  apps::mojom::AppType::kArc);
 }
 
-ArcApps::~ArcApps() {
-  // Clear out any pending icon calls to avoid a CHECK for Mojo callbacks that
-  // have not been run at App Service destruction.
-  for (auto& pending : pending_load_icon_calls_) {
-    std::move(pending).Run(nullptr);
-  }
-  pending_load_icon_calls_.clear();
-}
+ArcApps::~ArcApps() = default;
 
 void ArcApps::Shutdown() {
   // Disconnect the observee-observer connections that we made during the
@@ -227,12 +161,6 @@
   // notified so we can re-connect this object as an observer.
   ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_);
   if (prefs) {
-    auto* holder = prefs->app_connection_holder();
-    // The null check is for unit tests. On production, |holder| is always
-    // non-null.
-    if (holder) {
-      holder->RemoveObserver(this);
-    }
     prefs->RemoveObserver(this);
     arc_icon_once_loader_.StopObserving(prefs);
   }
@@ -388,20 +316,6 @@
                        display::Screen::GetScreen()->GetPrimaryDisplay().id());
 }
 
-void ArcApps::OnConnectionReady() {
-  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_);
-  if (!prefs) {
-    return;
-  }
-  AppConnectionHolder* app_connection_holder = prefs->app_connection_holder();
-  if (app_connection_holder && app_connection_holder->IsConnected()) {
-    for (auto& pending : pending_load_icon_calls_) {
-      std::move(pending).Run(app_connection_holder);
-    }
-    pending_load_icon_calls_.clear();
-  }
-}
-
 void ArcApps::OnAppRegistered(const std::string& app_id,
                               const ArcAppListPrefs::AppInfo& app_info) {
   ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_);
@@ -513,47 +427,10 @@
     return;
   }
 
-  // TODO(crbug.com/826982): drop the "kUncompressed only" condition, and
-  // always use the arc_icon_once_loader_, once the ArcAppIcon class supports
-  // providing *compressed* icons (i.e. PNG-encoded bytes, not RGBA pixels).
-  //
-  // Once that happens, we can delete the rest of this function, the LoadIcon0
-  // code, the pending_load_icon_calls_ mechanism and any mention in this file
-  // (or its .h) of arc::ConnectionHolder or arc::ConnectionObserver.
-  if (icon_compression == apps::mojom::IconCompression::kUncompressed) {
-    arc_icon_once_loader_.LoadIcon(
-        app_id, size_hint_in_dip,
-        base::BindOnce(&OnArcAppIconCompletelyLoaded, size_hint_in_dip,
-                       icon_effects, std::move(callback)));
-    return;
-  }
-
-  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_);
-  if (prefs) {
-    std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
-    if (app_info) {
-      constexpr bool quantize_to_supported_scale_factor = true;
-      base::OnceCallback<void(apps::ArcApps::AppConnectionHolder*)> pending =
-          base::BindOnce(
-              &LoadIcon0, icon_compression,
-              apps_util::ConvertDipToPx(size_hint_in_dip,
-                                        quantize_to_supported_scale_factor),
-              app_info->package_name, app_info->activity,
-              app_info->icon_resource_id, std::move(callback));
-
-      AppConnectionHolder* app_connection_holder =
-          prefs->app_connection_holder();
-      if (app_connection_holder && app_connection_holder->IsConnected()) {
-        std::move(pending).Run(app_connection_holder);
-      } else {
-        pending_load_icon_calls_.push_back(std::move(pending));
-      }
-      return;
-    }
-  }
-
-  // On failure, we still run the callback, with the zero IconValue.
-  std::move(callback).Run(apps::mojom::IconValue::New());
+  arc_icon_once_loader_.LoadIcon(
+      app_id, size_hint_in_dip, icon_compression,
+      base::BindOnce(&OnArcAppIconCompletelyLoaded, icon_compression,
+                     size_hint_in_dip, icon_effects, std::move(callback)));
 }
 
 void ArcApps::LoadPlayStoreIcon(apps::mojom::IconCompression icon_compression,
diff --git a/chrome/browser/apps/app_service/arc_apps.h b/chrome/browser/apps/app_service/arc_apps.h
index 4d2f17a..88f5c24 100644
--- a/chrome/browser/apps/app_service/arc_apps.h
+++ b/chrome/browser/apps/app_service/arc_apps.h
@@ -17,7 +17,6 @@
 #include "chrome/browser/apps/app_service/icon_key_util.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/services/app_service/public/mojom/app_service.mojom.h"
-#include "components/arc/session/connection_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
@@ -33,12 +32,8 @@
 // See chrome/services/app_service/README.md.
 class ArcApps : public KeyedService,
                 public apps::mojom::Publisher,
-                public arc::ConnectionObserver<arc::mojom::AppInstance>,
                 public ArcAppListPrefs::Observer {
  public:
-  using AppConnectionHolder =
-      arc::ConnectionHolder<arc::mojom::AppInstance, arc::mojom::AppHost>;
-
   static ArcApps* Get(Profile* profile);
 
   static ArcApps* CreateForTesting(Profile* profile,
@@ -72,9 +67,6 @@
   void Uninstall(const std::string& app_id) override;
   void OpenNativeSettings(const std::string& app_id) override;
 
-  // arc::ConnectionObserver<arc::mojom::AppInstance> overrides.
-  void OnConnectionReady() override;
-
   // ArcAppListPrefs::Observer overrides.
   void OnAppRegistered(const std::string& app_id,
                        const ArcAppListPrefs::AppInfo& app_info) override;
@@ -118,9 +110,6 @@
   Profile* profile_;
   ArcIconOnceLoader arc_icon_once_loader_;
 
-  std::vector<base::OnceCallback<void(AppConnectionHolder*)>>
-      pending_load_icon_calls_;
-
   apps_util::IncrementingIconKeyFactory icon_key_factory_;
 
   base::WeakPtrFactory<ArcApps> weak_ptr_factory_{this};
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 b578048..08dc475 100644
--- a/chrome/browser/apps/app_service/arc_icon_once_loader.cc
+++ b/chrome/browser/apps/app_service/arc_icon_once_loader.cc
@@ -2,20 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <utility>
-
 #include "chrome/browser/apps/app_service/arc_icon_once_loader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
 
 namespace apps {
 
-// A part of an ArcIconOnceLoader, for a specific size_in_dip. This two-level
-// structure (an ArcIconOnceLoader contains multiple SizeSpecificLoader
-// instances) is needed because each ArcAppIcon is for a specific size_in_dip.
+// A part of an ArcIconOnceLoader, for a specific size_in_dip and
+// icon_compression. 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.
 class ArcIconOnceLoader::SizeSpecificLoader : public ArcAppIcon::Observer {
  public:
-  SizeSpecificLoader(Profile* profile, int32_t size_in_dip);
+  SizeSpecificLoader(Profile* profile,
+                     int32_t size_in_dip,
+                     apps::mojom::IconCompression icon_compression);
   ~SizeSpecificLoader() override;
 
   void LoadIcon(const std::string& app_id,
@@ -29,8 +30,10 @@
  private:
   Profile* const profile_;
   const int32_t size_in_dip_;
+  const apps::mojom::IconCompression icon_compression_;
 
-  // Maps App IDs to their icon loaders (for a specific size in DIPs).
+  // Maps App IDs to their icon loaders (for a specific size_in_dip and
+  // icon_compression).
   std::map<std::string, std::unique_ptr<ArcAppIcon>> icons_;
 
   // Maps App IDs to callbacks to run when an icon is completely loaded.
@@ -39,9 +42,13 @@
   DISALLOW_COPY_AND_ASSIGN(SizeSpecificLoader);
 };
 
-ArcIconOnceLoader::SizeSpecificLoader::SizeSpecificLoader(Profile* profile,
-                                                          int32_t size_in_dip)
-    : profile_(profile), size_in_dip_(size_in_dip) {}
+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) {}
 
 ArcIconOnceLoader::SizeSpecificLoader::~SizeSpecificLoader() {
   for (auto& kv_pair : callbacks_) {
@@ -64,12 +71,14 @@
   if (iter != icons_.end()) {
     return;
   }
+  bool compressed =
+      icon_compression_ == apps::mojom::IconCompression::kCompressed;
   iter = icons_
              .insert(std::make_pair(
-                 app_id, std::make_unique<ArcAppIcon>(profile_, app_id,
-                                                      size_in_dip_, this)))
+                 app_id, std::make_unique<ArcAppIcon>(
+                             profile_, app_id, size_in_dip_, this, compressed)))
              .first;
-  iter->second->image_skia().EnsureRepsForSupportedScales();
+  iter->second->LoadSupportedScaleFactors();
 }
 
 void ArcIconOnceLoader::SizeSpecificLoader::Remove(const std::string& app_id) {
@@ -137,13 +146,15 @@
 void ArcIconOnceLoader::LoadIcon(
     const std::string& app_id,
     int32_t size_in_dip,
+    apps::mojom::IconCompression icon_compression,
     base::OnceCallback<void(ArcAppIcon*)> callback) {
-  auto iter = size_specific_loaders_.find(size_in_dip);
+  auto key = std::make_pair(size_in_dip, icon_compression);
+  auto iter = size_specific_loaders_.find(key);
   if (iter == size_specific_loaders_.end()) {
     iter = size_specific_loaders_
                .insert(std::make_pair(
-                   size_in_dip,
-                   std::make_unique<SizeSpecificLoader>(profile_, size_in_dip)))
+                   key, std::make_unique<SizeSpecificLoader>(
+                            profile_, size_in_dip, icon_compression)))
                .first;
   }
   iter->second->LoadIcon(app_id, std::move(callback));
@@ -158,9 +169,14 @@
 void ArcIconOnceLoader::OnAppIconUpdated(
     const std::string& app_id,
     const ArcAppIconDescriptor& descriptor) {
-  auto iter = size_specific_loaders_.find(descriptor.dip_size);
-  if (iter != size_specific_loaders_.end()) {
-    iter->second->Reload(app_id, descriptor.scale_factor);
+  for (int i = 0; i < 2; i++) {
+    auto icon_compression = i ? apps::mojom::IconCompression::kCompressed
+                              : apps::mojom::IconCompression::kUncompressed;
+    auto iter = size_specific_loaders_.find(
+        std::make_pair(descriptor.dip_size, icon_compression));
+    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 4c14148..486ce9d 100644
--- a/chrome/browser/apps/app_service/arc_icon_once_loader.h
+++ b/chrome/browser/apps/app_service/arc_icon_once_loader.h
@@ -8,11 +8,13 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <utility>
 
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_descriptor.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
+#include "chrome/services/app_service/public/mojom/types.mojom.h"
 
 class ArcAppIcon;
 class Profile;
@@ -41,6 +43,7 @@
   // loaded.
   void LoadIcon(const std::string& app_id,
                 int32_t size_in_dip,
+                apps::mojom::IconCompression icon_compression,
                 base::OnceCallback<void(ArcAppIcon*)> callback);
 
   // ArcAppListPrefs::Observer overrides.
@@ -51,8 +54,11 @@
  private:
   class SizeSpecificLoader;
 
+  using SizeAndCompression = std::pair<int32_t, apps::mojom::IconCompression>;
+
   Profile* const profile_;
-  std::map<int32_t, std::unique_ptr<SizeSpecificLoader>> size_specific_loaders_;
+  std::map<SizeAndCompression, std::unique_ptr<SizeSpecificLoader>>
+      size_specific_loaders_;
 
   DISALLOW_COPY_AND_ASSIGN(ArcIconOnceLoader);
 };
diff --git a/chrome/browser/availability/availability_prober.cc b/chrome/browser/availability/availability_prober.cc
index a5ee1c79..98a6d99 100644
--- a/chrome/browser/availability/availability_prober.cc
+++ b/chrome/browser/availability/availability_prober.cc
@@ -313,6 +313,17 @@
   }
 }
 
+// static
+void AvailabilityProber::ClearData(PrefService* pref_service) {
+  for (int i = 0;
+       i <= static_cast<int>(AvailabilityProber::ClientName::kMaxValue); i++) {
+    std::string key = PrefKeyForName(
+        NameForClient(static_cast<AvailabilityProber::ClientName>(i)));
+    DictionaryPrefUpdate update(pref_service, key);
+    update.Get()->Clear();
+  }
+}
+
 void AvailabilityProber::AddSelfAsNetworkConnectionObserver(
     network::NetworkConnectionTracker* network_connection_tracker) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chrome/browser/availability/availability_prober.h b/chrome/browser/availability/availability_prober.h
index a5c3b2d..ac85b7a 100644
--- a/chrome/browser/availability/availability_prober.h
+++ b/chrome/browser/availability/availability_prober.h
@@ -152,6 +152,9 @@
   // Registers the prefs used in this class.
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
+  // Clears the prefs used in this class.
+  static void ClearData(PrefService* pref_service);
+
   // Sends a probe now if the prober is currently inactive. If the probe is
   // active (i.e.: there are probes in flight), this is a no-op. If
   // |send_only_in_foreground| is set, the probe will only be sent when the app
@@ -269,6 +272,8 @@
   // Caches past probe results in a mapping of one tuple to another:
   //   (network_id, url_) -> (last_probe_status, last_modification_time).
   // No more than |max_cache_entries_| will be kept in this dictionary.
+  // |cached_probe_results_| may differ from what is on disk in the event
+  // browsing history is cleared during the limetime of |this|.
   std::unique_ptr<base::DictionaryValue> cached_probe_results_;
 
   // The tick clock used within this class.
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 28d8291..df11622 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/android/feed/feed_host_service_factory.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/autofill/strike_database_factory.h"
+#include "chrome/browser/availability/availability_prober.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browsing_data/browsing_data_helper.h"
@@ -585,6 +586,8 @@
     if (optimization_guide_keyed_service)
       optimization_guide_keyed_service->ClearData();
 
+    AvailabilityProber::ClearData(prefs);
+
 #if defined(OS_ANDROID)
     OomInterventionDecider* oom_intervention_decider =
         OomInterventionDecider::GetForBrowserContext(profile_);
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 22994b8..3154273 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -601,6 +601,10 @@
     "arc/print/arc_print_service.h",
     "arc/print_spooler/arc_print_spooler_bridge.cc",
     "arc/print_spooler/arc_print_spooler_bridge.h",
+    "arc/print_spooler/arc_print_spooler_util.cc",
+    "arc/print_spooler/arc_print_spooler_util.h",
+    "arc/print_spooler/print_session_impl.cc",
+    "arc/print_spooler/print_session_impl.h",
     "arc/process/arc_process.cc",
     "arc/process/arc_process.h",
     "arc/process/arc_process_service.cc",
diff --git a/chrome/browser/chromeos/OWNERS b/chrome/browser/chromeos/OWNERS
index 41860dd6..d6280b0 100644
--- a/chrome/browser/chromeos/OWNERS
+++ b/chrome/browser/chromeos/OWNERS
@@ -12,3 +12,4 @@
 per-file *active_directory*=rsorokin@chromium.org
 per-file *active_directory*=ljusten@chromium.org
 per-file tpm_firmware_update*=mnissler@chromium.org
+# COMPONENT: UI>Shell
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
index 60c3734d..64ab502a 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
@@ -441,6 +441,11 @@
     return;
 
   auto* manager = chromeos::input_method::InputMethodManager::Get();
+  if (!manager || !manager->GetActiveIMEState()) {
+    LOG(WARNING) << "InputMethodManager is not ready yet";
+    return;
+  }
+
   auto new_active_ime_ids =
       manager->GetActiveIMEState()->GetActiveInputMethodIds();
 
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
index d304cc9..bee8b7e 100644
--- a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
@@ -62,7 +62,7 @@
 constexpr char kSetPageZoomAction[] =
     "org.chromium.arc.intent_helper.SET_PAGE_ZOOM";
 
-constexpr char kArcProxyBypassListDelimeter[] = ",";
+constexpr char kArcProxyBypassListDelimiter[] = ",";
 
 bool GetHttpProxyServer(const ProxyConfigDictionary* proxy_config_dict,
                         std::string* host,
@@ -560,7 +560,7 @@
             bypass_list, net::ProxyBypassRules::kBypassListDelimeter,
             base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
         bypass_list =
-            base::JoinString(bypassed_hosts, kArcProxyBypassListDelimeter);
+            base::JoinString(bypassed_hosts, kArcProxyBypassListDelimiter);
         extras.SetString("bypassList", bypass_list);
       }
       break;
diff --git a/chrome/browser/chromeos/arc/print_spooler/OWNERS b/chrome/browser/chromeos/arc/print_spooler/OWNERS
new file mode 100644
index 0000000..f6f4b52
--- /dev/null
+++ b/chrome/browser/chromeos/arc/print_spooler/OWNERS
@@ -0,0 +1 @@
+file://components/arc/print_spooler/OWNERS
diff --git a/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.cc b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.cc
index a00b2fc..b36454e 100644
--- a/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.cc
+++ b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.cc
@@ -4,12 +4,27 @@
 
 #include "chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h"
 
+#include <utility>
+
+#include "ash/public/cpp/arc_custom_tab.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/singleton.h"
-#include "chrome/browser/ui/browser.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_util.h"
+#include "chrome/browser/chromeos/arc/print_spooler/print_session_impl.h"
+#include "chrome/browser/profiles/profile.h"
 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/filename_util.h"
+#include "ui/aura/window.h"
+#include "url/gurl.h"
 
 namespace arc {
 namespace {
@@ -43,8 +58,8 @@
 
 ArcPrintSpoolerBridge::ArcPrintSpoolerBridge(content::BrowserContext* context,
                                              ArcBridgeService* bridge_service)
-    : profile_(Profile::FromBrowserContext(context)),
-      arc_bridge_service_(bridge_service) {
+    : arc_bridge_service_(bridge_service),
+      profile_(Profile::FromBrowserContext(context)) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   arc_bridge_service_->print_spooler()->SetHost(this);
 }
@@ -54,4 +69,49 @@
   arc_bridge_service_->print_spooler()->SetHost(nullptr);
 }
 
+void ArcPrintSpoolerBridge::StartPrintInCustomTab(
+    mojo::ScopedHandle scoped_handle,
+    int32_t task_id,
+    int32_t surface_id,
+    int32_t top_margin,
+    mojom::PrintRendererDelegatePtr delegate,
+    StartPrintInCustomTabCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::ThreadPool(), base::MayBlock()},
+      base::BindOnce(&SavePrintDocument, std::move(scoped_handle)),
+      base::BindOnce(&ArcPrintSpoolerBridge::OnPrintDocumentSaved,
+                     weak_ptr_factory_.GetWeakPtr(), task_id, surface_id,
+                     top_margin, std::move(delegate), std::move(callback)));
+}
+
+void ArcPrintSpoolerBridge::OnPrintDocumentSaved(
+    int32_t task_id,
+    int32_t surface_id,
+    int32_t top_margin,
+    mojom::PrintRendererDelegatePtr delegate,
+    StartPrintInCustomTabCallback callback,
+    base::FilePath file_path) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (file_path.empty()) {
+    std::move(callback).Run(nullptr);
+    return;
+  }
+
+  GURL url = net::FilePathToFileURL(base::MakeAbsoluteFilePath(file_path));
+
+  aura::Window* arc_window = GetArcWindow(task_id);
+  if (!arc_window) {
+    LOG(ERROR) << "No ARC window with the specified task ID " << task_id;
+    std::move(callback).Run(nullptr);
+    return;
+  }
+
+  auto custom_tab =
+      ash::ArcCustomTab::Create(arc_window, surface_id, top_margin);
+  auto web_contents = CreateArcCustomTabWebContents(profile_, url);
+  std::move(callback).Run(PrintSessionImpl::Create(
+      std::move(web_contents), std::move(custom_tab), std::move(delegate)));
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h
index 2d98f3f..3d4edbc 100644
--- a/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h
+++ b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_bridge.h
@@ -7,9 +7,11 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/profiles/profile.h"
 #include "components/arc/mojom/print_spooler.mojom.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+class Profile;
 
 namespace content {
 class BrowserContext;
@@ -22,9 +24,8 @@
 // This class handles print related IPC from the ARC container and allows print
 // jobs to be displayed and managed in Chrome print preview instead of the
 // Android print UI.
-class ArcPrintSpoolerBridge
-    : public KeyedService,
-      public mojom::PrintSpoolerHost {
+class ArcPrintSpoolerBridge : public KeyedService,
+                              public mojom::PrintSpoolerHost {
  public:
   // Returns singleton instance for the given BrowserContext,
   // or nullptr if the browser |context| is not allowed to use ARC.
@@ -35,14 +36,26 @@
                         ArcBridgeService* bridge_service);
   ~ArcPrintSpoolerBridge() override;
 
-  // mojom::PrintSpoolerHost overrides:
-  // TODO(jschettler): Add overrides.
+  // mojom::PrintSpoolerHost:
+  void StartPrintInCustomTab(mojo::ScopedHandle scoped_handle,
+                             int32_t task_id,
+                             int32_t surface_id,
+                             int32_t top_margin,
+                             mojom::PrintRendererDelegatePtr delegate,
+                             StartPrintInCustomTabCallback callback) override;
+
+  void OnPrintDocumentSaved(int32_t task_id,
+                            int32_t surface_id,
+                            int32_t top_margin,
+                            mojom::PrintRendererDelegatePtr delegate,
+                            StartPrintInCustomTabCallback callback,
+                            base::FilePath file_path);
 
  private:
-  Profile* const profile_;
-
   ArcBridgeService* const arc_bridge_service_;  // Owned by ArcServiceManager.
 
+  Profile* const profile_;
+
   base::WeakPtrFactory<ArcPrintSpoolerBridge> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ArcPrintSpoolerBridge);
diff --git a/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_util.cc b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_util.cc
new file mode 100644
index 0000000..515e232
--- /dev/null
+++ b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_util.cc
@@ -0,0 +1,59 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_util.h"
+
+#include <utility>
+
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/platform_file.h"
+#include "base/files/scoped_file.h"
+#include "mojo/public/c/system/types.h"
+
+namespace arc {
+
+base::FilePath SavePrintDocument(mojo::ScopedHandle scoped_handle) {
+  base::PlatformFile platform_file = base::kInvalidPlatformFile;
+  if (mojo::UnwrapPlatformFile(std::move(scoped_handle), &platform_file) !=
+      MOJO_RESULT_OK) {
+    PLOG(ERROR) << "UnwrapPlatformFile failed.";
+    return base::FilePath();
+  }
+
+  base::File src_file(platform_file);
+  if (!src_file.IsValid()) {
+    PLOG(ERROR) << "Source file is invalid.";
+    return base::FilePath();
+  }
+
+  // TODO(jschettler): Determine a more secure location to save the print
+  // document.
+  base::FilePath temp_path;
+  if (!base::CreateTemporaryFile(&temp_path)) {
+    PLOG(ERROR) << "Failed to create file.";
+    return base::FilePath();
+  }
+
+  base::File temp(temp_path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+  char buf[4096];
+  int bytes;
+  while ((bytes = src_file.ReadAtCurrentPos(buf, sizeof(buf))) > 0) {
+    int written = temp.WriteAtCurrentPos(buf, bytes);
+    if (written != bytes) {
+      PLOG(ERROR) << "Error while saving PDF to disk.";
+      return base::FilePath();
+    }
+  }
+
+  if (bytes < 0) {
+    PLOG(ERROR) << "Error reading PDF.";
+    return base::FilePath();
+  }
+
+  return temp_path;
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_util.h b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_util.h
new file mode 100644
index 0000000..7672809
--- /dev/null
+++ b/chrome/browser/chromeos/arc/print_spooler/arc_print_spooler_util.h
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Utilities for ARC Print Spooler.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_ARC_PRINT_SPOOLER_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_ARC_PRINT_SPOOLER_UTIL_H_
+
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+namespace arc {
+
+// Uses the provided scoped handle to save a print document from ARC and returns
+// the document's file path.
+base::FilePath SavePrintDocument(mojo::ScopedHandle scoped_handle);
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_ARC_PRINT_SPOOLER_UTIL_H_
diff --git a/chrome/browser/chromeos/arc/print_spooler/print_session_impl.cc b/chrome/browser/chromeos/arc/print_spooler/print_session_impl.cc
new file mode 100644
index 0000000..07eadad6
--- /dev/null
+++ b/chrome/browser/chromeos/arc/print_spooler/print_session_impl.cc
@@ -0,0 +1,98 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/arc/print_spooler/print_session_impl.h"
+
+#include <utility>
+
+#include "ash/public/cpp/arc_custom_tab.h"
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "chrome/browser/printing/print_view_manager_common.h"
+#include "components/arc/print_spooler/arc_print_renderer.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "net/base/filename_util.h"
+#include "ui/aura/window.h"
+
+// static
+arc::mojom::PrintSessionPtr PrintSessionImpl::Create(
+    std::unique_ptr<content::WebContents> web_contents,
+    std::unique_ptr<ash::ArcCustomTab> custom_tab,
+    arc::mojom::PrintRendererDelegatePtr delegate) {
+  if (!custom_tab || !delegate)
+    return nullptr;
+
+  // This object will be deleted when the mojo connection is closed.
+  arc::mojom::PrintSessionPtr ptr;
+  new PrintSessionImpl(std::move(web_contents), std::move(custom_tab),
+                       std::move(delegate), mojo::MakeRequest(&ptr));
+  return ptr;
+}
+
+PrintSessionImpl::PrintSessionImpl(
+    std::unique_ptr<content::WebContents> web_contents,
+    std::unique_ptr<ash::ArcCustomTab> custom_tab,
+    arc::mojom::PrintRendererDelegatePtr delegate,
+    arc::mojom::PrintSessionRequest request)
+    : ArcCustomTabModalDialogHost(std::move(custom_tab),
+                                  std::move(web_contents)),
+      binding_(this, std::move(request)),
+      weak_ptr_factory_(this) {
+  binding_.set_connection_error_handler(
+      base::BindOnce(&PrintSessionImpl::Close, weak_ptr_factory_.GetWeakPtr()));
+  aura::Window* window = web_contents_->GetNativeView();
+  custom_tab_->Attach(window);
+  window->Show();
+
+  // TODO(jschettler): Handle this correctly once crbug.com/636642 is
+  // resolved. Until then, give the PDF plugin time to load.
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&PrintSessionImpl::StartPrintAfterDelay,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(delegate)),
+      base::TimeDelta::FromSeconds(1));
+}
+
+PrintSessionImpl::~PrintSessionImpl() {
+  // Delete the saved print document now that it's no longer needed.
+  base::FilePath file_path;
+  if (!net::FileURLToFilePath(web_contents_->GetVisibleURL(), &file_path)) {
+    LOG(ERROR) << "Failed to obtain file path from URL.";
+    return;
+  }
+
+  if (!base::DeleteFile(file_path, false))
+    LOG(ERROR) << "Failed to delete print document.";
+}
+
+void PrintSessionImpl::Close() {
+  delete this;
+}
+
+void PrintSessionImpl::StartPrintAfterDelay(
+    arc::mojom::PrintRendererDelegatePtr delegate) {
+  // Hand the PrintRendererDelegate interface pointer to a PrintRenderer
+  // observing the guest WebContents. This ensures the PrintRendererDelegate is
+  // associated with the RenderFrameHost corresponding to the RenderFrame
+  // being printed.
+  content::WebContents* web_contents_to_use =
+      printing::GetWebContentsToUse(web_contents_.get());
+  if (!web_contents_to_use) {
+    LOG(ERROR) << "Failed to obtain WebContents to use.";
+    Close();
+  }
+
+  ArcPrintRenderer::CreateForWebContents(web_contents_to_use);
+  ArcPrintRenderer::FromWebContents(web_contents_to_use)
+      ->SetDelegate(std::move(delegate));
+
+  // Start printing after the ArcPrintRenderer has been created to ensure it's
+  // available to the PrintRenderFrameHelper.
+  printing::StartPrint(web_contents_.get(), false, false);
+}
diff --git a/chrome/browser/chromeos/arc/print_spooler/print_session_impl.h b/chrome/browser/chromeos/arc/print_spooler/print_session_impl.h
new file mode 100644
index 0000000..a33b34c
--- /dev/null
+++ b/chrome/browser/chromeos/arc/print_spooler/print_session_impl.h
@@ -0,0 +1,62 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_PRINT_SESSION_IMPL_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_PRINT_SESSION_IMPL_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/ash/arc_custom_tab_modal_dialog_host.h"
+#include "components/arc/mojom/print_spooler.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace ash {
+class ArcCustomTab;
+}  // namespace ash
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+// Implementation of PrintSession interface. Allows ARC to manage and interact
+// with the ARC Custom Tab used for printing.
+class PrintSessionImpl : public arc::mojom::PrintSession,
+                         public ArcCustomTabModalDialogHost {
+ public:
+  static arc::mojom::PrintSessionPtr Create(
+      std::unique_ptr<content::WebContents> web_contents,
+      std::unique_ptr<ash::ArcCustomTab> custom_tab,
+      arc::mojom::PrintRendererDelegatePtr delegate);
+
+ private:
+  PrintSessionImpl(std::unique_ptr<content::WebContents> web_contents,
+                   std::unique_ptr<ash::ArcCustomTab> custom_tab,
+                   arc::mojom::PrintRendererDelegatePtr delegate,
+                   arc::mojom::PrintSessionRequest request);
+  ~PrintSessionImpl() override;
+
+  void Bind(arc::mojom::PrintSessionPtr* ptr);
+
+  // Used to close the ARC Custom Tab used for printing. If the remote end
+  // closes the connection, the ARC Custom Tab and print preview will be closed.
+  // If printing has already started, this will not cancel any active print job.
+  void Close();
+
+  // Opens Chrome print preview and hands the PrintRendererDelegate interface
+  // pointer to the PrintRenderer.
+  void StartPrintAfterDelay(arc::mojom::PrintRendererDelegatePtr delegate);
+
+  // Used to bind the PrintSession interface implementation to a message pipe.
+  mojo::Binding<arc::mojom::PrintSession> binding_;
+
+  // Note: This should remain the last member so it'll be destroyed and
+  // invalidate its weak pointers before any other members are destroyed.
+  base::WeakPtrFactory<PrintSessionImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrintSessionImpl);
+};
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_PRINT_SPOOLER_PRINT_SESSION_IMPL_H_
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
index 16e9aade..d21de789 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -23,6 +23,7 @@
 #include "components/account_id/account_id.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "content/public/test/browser_task_environment.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "storage/browser/fileapi/external_mount_points.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -169,8 +170,9 @@
     scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
         std::move(user_manager));
 
-    device::mojom::UsbDeviceManagerPtr fake_usb_manager_ptr_;
-    fake_usb_manager_.AddBinding(mojo::MakeRequest(&fake_usb_manager_ptr_));
+    mojo::Remote<device::mojom::UsbDeviceManager> fake_usb_manager;
+    fake_usb_manager_.AddReceiver(
+        fake_usb_manager.BindNewPipeAndPassReceiver());
   }
 
   void TearDown() override {
diff --git a/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc b/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc
index 22251a3..53ffc69 100644
--- a/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc
+++ b/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc
@@ -104,7 +104,8 @@
   ASSERT_TRUE(RunExtensionTest("input_method/basic")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(ExtensionInputMethodApiTest, Typing) {
+// TODO(https://crbug.com/997888): Flaky on multiple platforms.
+IN_PROC_BROWSER_TEST_F(ExtensionInputMethodApiTest, DISABLED_Typing) {
   // Enable the test IME from the test extension.
   std::vector<std::string> extension_ime_ids = {
       "_ext_ime_ilanclmaeigfpnmdlgelmhkpkegdioiptest"};
diff --git a/chrome/browser/chromeos/printing/usb_printer_detector.cc b/chrome/browser/chromeos/printing/usb_printer_detector.cc
index 5c3a75d4..e8df481 100644
--- a/chrome/browser/chromeos/printing/usb_printer_detector.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_detector.cc
@@ -32,6 +32,8 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/system_connector.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/constants.mojom.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_manager_client.mojom.h"
@@ -59,9 +61,9 @@
                                public device::mojom::UsbDeviceManagerClient {
  public:
   explicit UsbPrinterDetectorImpl(
-      device::mojom::UsbDeviceManagerPtrInfo device_manager_info)
-      : device_manager_(std::move(device_manager_info)) {
-    device_manager_.set_connection_error_handler(
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager)
+      : device_manager_(std::move(device_manager)) {
+    device_manager_.set_disconnect_handler(
         base::BindOnce(&UsbPrinterDetectorImpl::OnDeviceManagerConnectionError,
                        weak_factory_.GetWeakPtr()));
 
@@ -180,7 +182,7 @@
 
   OnPrintersFoundCallback on_printers_found_callback_;
 
-  device::mojom::UsbDeviceManagerPtr device_manager_;
+  mojo::Remote<device::mojom::UsbDeviceManager> device_manager_;
   mojo::AssociatedBinding<device::mojom::UsbDeviceManagerClient>
       client_binding_{this};
   base::WeakPtrFactory<UsbPrinterDetectorImpl> weak_factory_{this};
@@ -191,15 +193,16 @@
 // static
 std::unique_ptr<UsbPrinterDetector> UsbPrinterDetector::Create() {
   // Bind to the DeviceService for USB device manager.
-  device::mojom::UsbDeviceManagerPtrInfo usb_manager_info;
-  content::GetSystemConnector()->BindInterface(
-      device::mojom::kServiceName, mojo::MakeRequest(&usb_manager_info));
-  return std::make_unique<UsbPrinterDetectorImpl>(std::move(usb_manager_info));
+  mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager;
+  content::GetSystemConnector()->Connect(
+      device::mojom::kServiceName,
+      usb_manager.InitWithNewPipeAndPassReceiver());
+  return std::make_unique<UsbPrinterDetectorImpl>(std::move(usb_manager));
 }
 
 std::unique_ptr<UsbPrinterDetector> UsbPrinterDetector::CreateForTesting(
-    device::mojom::UsbDeviceManagerPtrInfo usb_manager_info) {
-  return std::make_unique<UsbPrinterDetectorImpl>(std::move(usb_manager_info));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager) {
+  return std::make_unique<UsbPrinterDetectorImpl>(std::move(usb_manager));
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/usb_printer_detector.h b/chrome/browser/chromeos/printing/usb_printer_detector.h
index 501c78652..9d26b58c 100644
--- a/chrome/browser/chromeos/printing/usb_printer_detector.h
+++ b/chrome/browser/chromeos/printing/usb_printer_detector.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/chromeos/printing/printer_detector.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 
 namespace chromeos {
@@ -21,7 +22,7 @@
   static std::unique_ptr<UsbPrinterDetector> Create();
 
   static std::unique_ptr<UsbPrinterDetector> CreateForTesting(
-      device::mojom::UsbDeviceManagerPtrInfo usb_manager_info);
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager);
 
   ~UsbPrinterDetector() override = default;
 
diff --git a/chrome/browser/chromeos/printing/usb_printer_detector_unittest.cc b/chrome/browser/chromeos/printing/usb_printer_detector_unittest.cc
index 0212200..f1abd44 100644
--- a/chrome/browser/chromeos/printing/usb_printer_detector_unittest.cc
+++ b/chrome/browser/chromeos/printing/usb_printer_detector_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/cpp/test/fake_usb_device_info.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
@@ -68,11 +69,10 @@
   };
 
   UsbPrinterDetectorTest() {
-    device::mojom::UsbDeviceManagerPtrInfo manager_ptr_info;
-    usb_manager_.AddBinding(mojo::MakeRequest(&manager_ptr_info));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> manager;
+    usb_manager_.AddReceiver(manager.InitWithNewPipeAndPassReceiver());
 
-    detector_ =
-        UsbPrinterDetector::CreateForTesting(std::move(manager_ptr_info));
+    detector_ = UsbPrinterDetector::CreateForTesting(std::move(manager));
     detector_->RegisterPrintersFoundCallback(
         base::BindRepeating(&FakePrinterDetectorClient::OnPrintersFound,
                             base::Unretained(&detector_client_)));
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.cc b/chrome/browser/chromeos/usb/cros_usb_detector.cc
index aaf0c470..d1052d78 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector.cc
+++ b/chrome/browser/chromeos/usb/cros_usb_detector.cc
@@ -264,9 +264,9 @@
 }
 
 void CrosUsbDetector::SetDeviceManagerForTesting(
-    device::mojom::UsbDeviceManagerPtr device_manager) {
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager) {
   DCHECK(!device_manager_) << "device_manager_ was already initialized";
-  device_manager_ = std::move(device_manager);
+  device_manager_.Bind(std::move(device_manager));
 }
 
 void CrosUsbDetector::AddUsbDeviceObserver(CrosUsbDeviceObserver* observer) {
@@ -300,11 +300,12 @@
 void CrosUsbDetector::ConnectToDeviceManager() {
   // Tests may set a fake manager.
   if (!device_manager_) {
-    content::GetSystemConnector()->BindInterface(
-        device::mojom::kServiceName, mojo::MakeRequest(&device_manager_));
+    content::GetSystemConnector()->Connect(
+        device::mojom::kServiceName,
+        device_manager_.BindNewPipeAndPassReceiver());
   }
   DCHECK(device_manager_);
-  device_manager_.set_connection_error_handler(
+  device_manager_.set_disconnect_handler(
       base::BindOnce(&CrosUsbDetector::OnDeviceManagerConnectionError,
                      weak_ptr_factory_.GetWeakPtr()));
 
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.h b/chrome/browser/chromeos/usb/cros_usb_detector.h
index fd3a36f..692eea7f 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector.h
+++ b/chrome/browser/chromeos/usb/cros_usb_detector.h
@@ -16,6 +16,8 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/usb_enumeration_options.mojom.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 #include "services/device/public/mojom/usb_manager_client.mojom.h"
@@ -89,7 +91,7 @@
   ~CrosUsbDetector() override;
 
   void SetDeviceManagerForTesting(
-      device::mojom::UsbDeviceManagerPtr device_manager);
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager);
 
   // Connect to the device manager to be notified of connection/removal.
   // Used during browser startup, after connection errors and to setup a fake
@@ -178,7 +180,7 @@
   // Returns true when a device should show a notification when attached.
   bool ShouldShowNotification(const device::mojom::UsbDeviceInfo& device_info);
 
-  device::mojom::UsbDeviceManagerPtr device_manager_;
+  mojo::Remote<device::mojom::UsbDeviceManager> device_manager_;
   mojo::AssociatedBinding<device::mojom::UsbDeviceManagerClient>
       client_binding_;
 
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc b/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
index 6dae9ee..77fbe26 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
+++ b/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
@@ -146,10 +146,11 @@
         nullptr /* profile */);
 
     // Set a fake USB device manager before ConnectToDeviceManager().
-    device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-    device_manager_.AddBinding(mojo::MakeRequest(&device_manager_ptr));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
+    device_manager_.AddReceiver(
+        device_manager.InitWithNewPipeAndPassReceiver());
     chromeos::CrosUsbDetector::Get()->SetDeviceManagerForTesting(
-        std::move(device_manager_ptr));
+        std::move(device_manager));
     // Create a default VM instance which is running.
     crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
         crostini::kCrostiniDefaultVmName);
diff --git a/chrome/browser/devtools/device/android_device_manager.cc b/chrome/browser/devtools/device/android_device_manager.cc
index 6a906fd..a5920540 100644
--- a/chrome/browser/devtools/device/android_device_manager.cc
+++ b/chrome/browser/devtools/device/android_device_manager.cc
@@ -574,7 +574,7 @@
 }
 
 void AndroidDeviceManager::set_usb_device_manager_for_test(
-    device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager) {
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_usb_manager) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   handler_thread_->message_loop()->PostTask(
       FROM_HERE,
diff --git a/chrome/browser/devtools/device/android_device_manager.h b/chrome/browser/devtools/device/android_device_manager.h
index b97c7e6..942ac4b 100644
--- a/chrome/browser/devtools/device/android_device_manager.h
+++ b/chrome/browser/devtools/device/android_device_manager.h
@@ -18,6 +18,7 @@
 #include "base/single_thread_task_runner.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -204,7 +205,7 @@
   void CountDevices(const base::Callback<void(int)>& callback);
 
   void set_usb_device_manager_for_test(
-      device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager);
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_usb_manager);
 
   static std::string GetBrowserName(const std::string& socket,
                                     const std::string& package);
diff --git a/chrome/browser/devtools/device/devtools_android_bridge.cc b/chrome/browser/devtools/device/devtools_android_bridge.cc
index 2aeaad3..ec89514 100644
--- a/chrome/browser/devtools/device/devtools_android_bridge.cc
+++ b/chrome/browser/devtools/device/devtools_android_bridge.cc
@@ -400,6 +400,6 @@
 }
 
 void DevToolsAndroidBridge::set_usb_device_manager_for_test(
-    device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager) {
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_usb_manager) {
   device_manager_->set_usb_device_manager_for_test(std::move(fake_usb_manager));
 }
diff --git a/chrome/browser/devtools/device/devtools_android_bridge.h b/chrome/browser/devtools/device/devtools_android_bridge.h
index 6dede6d..b2d2e49 100644
--- a/chrome/browser/devtools/device/devtools_android_bridge.h
+++ b/chrome/browser/devtools/device/devtools_android_bridge.h
@@ -23,6 +23,7 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/devtools_agent_host.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace base {
@@ -136,7 +137,7 @@
       base::Callback<void(scoped_refptr<TCPDeviceProvider>)>;
   void set_tcp_provider_callback_for_test(TCPProviderCallback callback);
   void set_usb_device_manager_for_test(
-      device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager);
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_usb_manager);
 
   void Shutdown() override;
 
diff --git a/chrome/browser/devtools/device/usb/android_usb_browsertest.cc b/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
index 36040c0..31396b5 100644
--- a/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
+++ b/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
@@ -27,6 +27,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_utils.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/cpp/test/fake_usb_device.h"
 #include "services/device/public/cpp/test/fake_usb_device_info.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
@@ -510,9 +511,9 @@
     // Set a fake USB device manager for AndroidUsbDevice.
     usb_manager_ = CreateFakeUsbManager();
     DCHECK(usb_manager_);
-    device::mojom::UsbDeviceManagerPtrInfo manager_ptr_info;
-    usb_manager_->AddBinding(mojo::MakeRequest(&manager_ptr_info));
-    adb_bridge_->set_usb_device_manager_for_test(std::move(manager_ptr_info));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> manager;
+    usb_manager_->AddReceiver(manager.InitWithNewPipeAndPassReceiver());
+    adb_bridge_->set_usb_device_manager_for_test(std::move(manager));
   }
 
   void ScheduleDeviceCountRequest(const base::Closure& request) {
diff --git a/chrome/browser/devtools/device/usb/usb_device_manager_helper.cc b/chrome/browser/devtools/device/usb/usb_device_manager_helper.cc
index b5f1b12..46a13a2 100644
--- a/chrome/browser/devtools/device/usb/usb_device_manager_helper.cc
+++ b/chrome/browser/devtools/device/usb/usb_device_manager_helper.cc
@@ -15,6 +15,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/system_connector.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "services/device/public/mojom/constants.mojom.h"
 #include "services/device/public/mojom/usb_enumeration_options.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -104,11 +105,11 @@
 }
 
 void BindDeviceServiceOnUIThread(
-    device::mojom::UsbDeviceManagerRequest request) {
+    mojo::PendingReceiver<device::mojom::UsbDeviceManager> receiver) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   // Bind to the DeviceService for USB device manager.
-  content::GetSystemConnector()->BindInterface(device::mojom::kServiceName,
-                                               std::move(request));
+  content::GetSystemConnector()->Connect(device::mojom::kServiceName,
+                                         std::move(receiver));
 }
 
 }  // namespace
@@ -147,7 +148,7 @@
 
 // static
 void UsbDeviceManagerHelper::SetUsbManagerForTesting(
-    device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager) {
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_usb_manager) {
   GetInstance()->SetUsbManagerForTestingInternal(std::move(fake_usb_manager));
 }
 
@@ -186,23 +187,21 @@
     return;
 
   // Just for testing.
-  if (testing_device_manager_info_) {
-    device_manager_.Bind(std::move(testing_device_manager_info_));
-    device_manager_.set_connection_error_handler(
+  if (testing_device_manager_) {
+    device_manager_.Bind(std::move(testing_device_manager_));
+    device_manager_.set_disconnect_handler(
         base::BindOnce(&UsbDeviceManagerHelper::OnDeviceManagerConnectionError,
                        weak_factory_.GetWeakPtr()));
     return;
   }
 
-  device::mojom::UsbDeviceManagerRequest request =
-      mojo::MakeRequest(&device_manager_);
-  device_manager_.set_connection_error_handler(
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(&BindDeviceServiceOnUIThread,
+                                device_manager_.BindNewPipeAndPassReceiver()));
+
+  device_manager_.set_disconnect_handler(
       base::BindOnce(&UsbDeviceManagerHelper::OnDeviceManagerConnectionError,
                      weak_factory_.GetWeakPtr()));
-
-  base::PostTask(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&BindDeviceServiceOnUIThread, std::move(request)));
 }
 
 void UsbDeviceManagerHelper::CountDevicesInternal(
@@ -219,10 +218,10 @@
 }
 
 void UsbDeviceManagerHelper::SetUsbManagerForTestingInternal(
-    device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager) {
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_usb_manager) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(fake_usb_manager);
-  testing_device_manager_info_ = std::move(fake_usb_manager);
+  testing_device_manager_ = std::move(fake_usb_manager);
 }
 
 void UsbDeviceManagerHelper::OnDeviceManagerConnectionError() {
diff --git a/chrome/browser/devtools/device/usb/usb_device_manager_helper.h b/chrome/browser/devtools/device/usb/usb_device_manager_helper.h
index 5d7c7d4..d021ea07 100644
--- a/chrome/browser/devtools/device/usb/usb_device_manager_helper.h
+++ b/chrome/browser/devtools/device/usb/usb_device_manager_helper.h
@@ -9,6 +9,8 @@
 #include <vector>
 
 #include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 
@@ -49,7 +51,7 @@
   static UsbDeviceManagerHelper* GetInstance();
   static void CountDevices(base::OnceCallback<void(int)> callback);
   static void SetUsbManagerForTesting(
-      device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager);
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_usb_manager);
 
   // Please do not create UsbDeviceManagerHelper instance from this constructor
   // directly, use static method GetInstance() instead.
@@ -64,13 +66,13 @@
  private:
   void CountDevicesInternal(base::OnceCallback<void(int)> callback);
   void SetUsbManagerForTestingInternal(
-      device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager);
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_usb_manager);
   void EnsureUsbDeviceManagerConnection();
   void OnDeviceManagerConnectionError();
 
-  device::mojom::UsbDeviceManagerPtr device_manager_;
+  mojo::Remote<device::mojom::UsbDeviceManager> device_manager_;
   // Just for test.
-  device::mojom::UsbDeviceManagerPtrInfo testing_device_manager_info_;
+  mojo::PendingRemote<device::mojom::UsbDeviceManager> testing_device_manager_;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index f767c32..cff2867 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -85,6 +85,7 @@
 #include "chrome/browser/android/download/download_controller.h"
 #include "chrome/browser/android/download/download_location_dialog_bridge_impl.h"
 #include "chrome/browser/android/download/download_manager_service.h"
+#include "chrome/browser/android/download/download_open_source.h"
 #include "chrome/browser/android/download/download_utils.h"
 #include "chrome/browser/android/feature_utilities.h"
 #include "chrome/browser/infobars/infobar_service.h"
@@ -684,7 +685,7 @@
                                          false /* show_download_in_folder */);
 
 #if defined(OS_ANDROID)
-  DownloadUtils::OpenDownload(download, 0 /* download source */);
+  DownloadUtils::OpenDownload(download, DownloadOpenSource::kUnknown);
   return;
 #endif
 
diff --git a/chrome/browser/download/download_crx_util.cc b/chrome/browser/download/download_crx_util.cc
index 56e11d0..912b7f1 100644
--- a/chrome/browser/download/download_crx_util.cc
+++ b/chrome/browser/download/download_crx_util.cc
@@ -65,11 +65,6 @@
 }
 
 bool OffStoreInstallAllowedByPrefs(Profile* profile, const DownloadItem& item) {
-  // TODO(aa): RefererURL is cleared in some cases, for example when going
-  // between secure and non-secure URLs. It would be better if DownloadItem
-  // tracked the initiating page explicitly.
-  LOG(ERROR) << "OffStoreInstallAllowedByPrefs************"
-             << g_allow_offstore_install_for_testing;
   return g_allow_offstore_install_for_testing ||
          extensions::ExtensionManagementFactory::GetForBrowserContext(profile)
              ->IsOffstoreInstallAllowed(item.GetURL(), item.GetReferrerUrl());
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index cc062bbe..000374b 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -758,6 +758,7 @@
     "//chrome/common",
     "//chrome/common/extensions/api",
     "//components/safe_browsing:csd_proto",
+    "//components/safe_browsing:webprotect_proto",
     "//components/safe_browsing/db:util",
     "//components/signin/core/browser",
     "//content/public/browser",
diff --git a/chrome/browser/extensions/api/device_permissions_manager_unittest.cc b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
index d51463b1..095b9e97 100644
--- a/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
+++ b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc
@@ -17,6 +17,7 @@
 #include "extensions/browser/api/usb/usb_device_manager.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/common/extension.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/cpp/hid/fake_hid_manager.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/device/public/mojom/hid.mojom.h"
@@ -53,10 +54,10 @@
         "}"));
 
     // Set fake device manager for extensions::UsbDeviceManager.
-    device::mojom::UsbDeviceManagerPtr usb_manager_ptr;
-    fake_usb_manager_.AddBinding(mojo::MakeRequest(&usb_manager_ptr));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager;
+    fake_usb_manager_.AddReceiver(usb_manager.InitWithNewPipeAndPassReceiver());
     UsbDeviceManager::Get(env_->profile())
-        ->SetDeviceManagerForTesting(std::move(usb_manager_ptr));
+        ->SetDeviceManagerForTesting(std::move(usb_manager));
     base::RunLoop().RunUntilIdle();
 
     device0_ = fake_usb_manager_.CreateAndAddDevice(0, 0, "Test Manufacturer",
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index 209af21e..c7b38283 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -20,6 +20,7 @@
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
 #include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h"
+#include "components/safe_browsing/proto/webprotect.pb.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/browser_context.h"
 #include "extensions/browser/event_router.h"
@@ -46,6 +47,8 @@
 const char SafeBrowsingPrivateEventRouter::kKeyNetErrorCode[] = "netErrorCode";
 const char SafeBrowsingPrivateEventRouter::kKeyClickedThrough[] =
     "clickedThrough";
+const char SafeBrowsingPrivateEventRouter::kKeyTriggeredRules[] =
+    "triggeredRules";
 
 const char SafeBrowsingPrivateEventRouter::kKeyPasswordReuseEvent[] =
     "passwordReuseEvent";
@@ -55,6 +58,10 @@
     "dangerousDownloadEvent";
 const char SafeBrowsingPrivateEventRouter::kKeyInterstitialEvent[] =
     "interstitialEvent";
+const char SafeBrowsingPrivateEventRouter::kKeySensitiveDataEvent[] =
+    "sensitiveDataEvent";
+const char SafeBrowsingPrivateEventRouter::kKeyLargeUnscannedFileEvent[] =
+    "largeUnscannedFileEvent";
 
 SafeBrowsingPrivateEventRouter::SafeBrowsingPrivateEventRouter(
     content::BrowserContext* context)
@@ -258,7 +265,7 @@
     const std::string& file_name,
     const std::string& download_digest_sha256) {
   if (client_) {
-    // Convert |params| to a real-time event dictionary and report it.
+    // Create a real-time event dictionary from the arguments and report it.
     base::Value event(base::Value::Type::DICTIONARY);
     event.SetStringKey(kKeyUrl, url.spec());
     event.SetStringKey(kKeyFileName, file_name);
@@ -268,6 +275,43 @@
   }
 }
 
+void SafeBrowsingPrivateEventRouter::OnSensitiveDataEvent(
+    const safe_browsing::DlpDeepScanningVerdict& verdict,
+    const GURL& url,
+    const std::string& file_name,
+    const std::string& download_digest_sha256) {
+  if (client_) {
+    // Create a real-time event dictionary from the arguments and report it.
+    base::Value event(base::Value::Type::DICTIONARY);
+    event.SetStringKey(kKeyUrl, url.spec());
+    event.SetStringKey(kKeyFileName, file_name);
+    event.SetStringKey(kKeyDownloadDigestSha256, download_digest_sha256);
+    event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
+
+    base::ListValue triggered_rules;
+    for (auto rule : verdict.triggered_rules()) {
+      triggered_rules.AppendString(rule.rule_name());
+    }
+    event.SetKey(kKeyTriggeredRules, std::move(triggered_rules));
+    ReportRealtimeEvent(kKeySensitiveDataEvent, std::move(event));
+  }
+}
+
+void SafeBrowsingPrivateEventRouter::OnLargeUnscannedFileEvent(
+    const GURL& url,
+    const std::string& file_name,
+    const std::string& download_digest_sha256) {
+  if (client_) {
+    // Create a real-time event dictionary from the arguments and report it.
+    base::Value event(base::Value::Type::DICTIONARY);
+    event.SetStringKey(kKeyUrl, url.spec());
+    event.SetStringKey(kKeyFileName, file_name);
+    event.SetStringKey(kKeyDownloadDigestSha256, download_digest_sha256);
+    event.SetStringKey(kKeyProfileUserName, GetProfileUserName());
+    ReportRealtimeEvent(kKeyLargeUnscannedFileEvent, std::move(event));
+  }
+}
+
 void SafeBrowsingPrivateEventRouter::SetCloudPolicyClientForTesting(
     std::unique_ptr<policy::CloudPolicyClient> client) {
   DCHECK_EQ(nullptr, client_.get());
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
index 5172a29..8d0b6f1 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
@@ -31,6 +31,10 @@
 class CloudPolicyClient;
 }
 
+namespace safe_browsing {
+class DlpDeepScanningVerdict;
+}
+
 namespace extensions {
 
 // An event router that observes Safe Browsing events and notifies listeners.
@@ -52,11 +56,14 @@
   static const char kKeyReason[];
   static const char kKeyNetErrorCode[];
   static const char kKeyClickedThrough[];
+  static const char kKeyTriggeredRules[];
 
   static const char kKeyPasswordReuseEvent[];
   static const char kKeyPasswordChangedEvent[];
   static const char kKeyDangerousDownloadEvent[];
   static const char kKeyInterstitialEvent[];
+  static const char kKeySensitiveDataEvent[];
+  static const char kKeyLargeUnscannedFileEvent[];
 
   explicit SafeBrowsingPrivateEventRouter(content::BrowserContext* context);
 
@@ -91,6 +98,18 @@
                                      const std::string& file_name,
                                      const std::string& download_digest_sha256);
 
+  // Notifies listeners that scanning for sensitive data detected a violation.
+  void OnSensitiveDataEvent(
+      const safe_browsing::DlpDeepScanningVerdict& verdict,
+      const GURL& url,
+      const std::string& file_name,
+      const std::string& download_digest_sha256);
+
+  // Notifies listeners that deep scanning failed, since the file was too large.
+  void OnLargeUnscannedFileEvent(const GURL& url,
+                                 const std::string& file_name,
+                                 const std::string& download_digest_sha256);
+
   void SetCloudPolicyClientForTesting(
       std::unique_ptr<policy::CloudPolicyClient> client);
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index f96e9056..d51ab2a7 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -34,6 +34,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "aggregated-ml-app-ranking",
+    "owners": [ "pdyson", "jiameng" ],
+    "expiry_milestone": 81
+  },
+  {
     "name": "allow-insecure-localhost",
     "owners": [ "security-dev" ],
     "expiry_milestone": 82
@@ -2012,7 +2017,7 @@
   {
     "name": "extension-content-verification",
     "owners": [ "//extensions/OWNERS" ],
-    "expiry_milestone": 76
+    "expiry_milestone": 86
   },
   {
     "name": "extensions-on-chrome-urls",
@@ -2891,6 +2896,11 @@
     "expiry_milestone": 83
   },
   {
+    "name": "scalable-app-list",
+    "owners": [ "tbarzic" ],
+    "expiry_milestone": 82
+  },
+  {
     "name": "scheduler-configuration",
     "owners": [ "kerrnel", "mnissler" ],
     "expiry_milestone": 77
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index fbd93bf9..ccf813e 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1658,6 +1658,13 @@
     "Enables an experiment to switch menu labels that use 'Save as...' to "
     "'Download'.";
 
+const char kScalableAppListName[] =
+    "App list UI configuration dependant on display size";
+const char kScalableAppListDescription[] =
+    "Adapts app list item sizing and spacing for smaller screen sizes, "
+    "instead of using single app list configuration, that is optionally "
+    "scaled down, for all screens.";
+
 const char kScrollableTabStripName[] = "Scrollable TabStrip";
 const char kScrollableTabStripDescription[] =
     "Allows users to access tabs by scrolling when they no longer fit in the "
@@ -3032,6 +3039,10 @@
     "Enable hardware-accelerated mjpeg decode for captured frame where "
     "available.";
 
+const char kAggregatedMlAppRankingName[] = "Rank suggested apps with ML.";
+const char kAggregatedMlAppRankingDescription[] =
+    "Use the aggregated ML model to rank the suggested apps.";
+
 const char kAppServiceAshName[] = "App Service Ash";
 const char kAppServiceAshDescription[] =
     "Use the App Service to provide data to the Ash UI, such as the app list.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 7be86da..0d4a4fe 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -995,6 +995,9 @@
 extern const char kSaveasMenuLabelExperimentName[];
 extern const char kSaveasMenuLabelExperimentDescription[];
 
+extern const char kScalableAppListName[];
+extern const char kScalableAppListDescription[];
+
 extern const char kScrollableTabStripName[];
 extern const char kScrollableTabStripDescription[];
 
@@ -1805,6 +1808,9 @@
 extern const char kAcceleratedMjpegDecodeName[];
 extern const char kAcceleratedMjpegDecodeDescription[];
 
+extern const char kAggregatedMlAppRankingName[];
+extern const char kAggregatedMlAppRankingDescription[];
+
 extern const char kAppServiceAshName[];
 extern const char kAppServiceAshDescription[];
 
diff --git a/chrome/browser/metrics/perf/perf_output.cc b/chrome/browser/metrics/perf/perf_output.cc
index 92a694e..901c27c 100644
--- a/chrome/browser/metrics/perf/perf_output.cc
+++ b/chrome/browser/metrics/perf/perf_output.cc
@@ -21,9 +21,11 @@
       pending_stop_(false) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  perf_data_pipe_reader_ = std::make_unique<chromeos::PipeReader>(
-      base::CreateTaskRunner({base::ThreadPool(), base::MayBlock(),
-                              base::TaskPriority::USER_VISIBLE}));
+  perf_data_pipe_reader_ =
+      std::make_unique<chromeos::PipeReader>(base::CreateTaskRunner(
+          {base::ThreadPool(), base::MayBlock(),
+           base::TaskPriority::USER_VISIBLE,
+           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
 
   base::ScopedFD pipe_write_end =
       perf_data_pipe_reader_->StartIO(base::BindOnce(
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.cc b/chrome/browser/navigation_predictor/navigation_predictor.cc
index 550b253..cd94d91 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor.cc
@@ -461,7 +461,7 @@
   page_link_builder.SetNumberOfAnchors_URLIncremented(
       GetBucketMinForPageMetrics(number_of_anchors_url_incremented_));
   page_link_builder.SetTotalClickableSpace(
-      GetBucketMinForPageMetrics(total_clickable_space_));
+      GetBucketMinForPageMetrics(static_cast<int>(total_clickable_space_)));
   page_link_builder.SetMedianLinkLocation(
       GetLinearBucketForLinkLocation(median_link_location_));
   page_link_builder.SetViewport_Height(
@@ -805,7 +805,7 @@
         static_cast<int>(metric->is_url_incremented_by_one);
 
     link_locations.push_back(metric->ratio_distance_top_to_visible_top);
-    total_clickable_space_ += metric->ratio_visible_area;
+    total_clickable_space_ += metric->ratio_visible_area * 100.0;
   }
 
   sort(link_locations.begin(), link_locations.end());
@@ -963,14 +963,15 @@
   // TODO(chelu): https://crbug.com/850624/. Experiment with other heuristic
   // algorithms for computing the anchor elements score.
   double score =
-      ratio_area_scale_ * metrics.ratio_area +
-      is_in_iframe_scale_ * metrics.is_in_iframe +
-      contains_image_scale_ * metrics.contains_image + host_score +
-      is_url_incremented_scale_ * metrics.is_url_incremented_by_one +
-      source_engagement_score_scale_ * document_engagement_score +
-      target_engagement_score_scale_ * target_engagement_score +
-      area_rank_scale_ * area_rank_score +
-      ratio_distance_root_top_scale_ * metrics.ratio_distance_root_top;
+      (ratio_area_scale_ * (metrics.ratio_area * 100.0)) +
+      (metrics.is_in_iframe ? is_in_iframe_scale_ : 0.0) +
+      (metrics.contains_image ? contains_image_scale_ : 0.0) + host_score +
+      (metrics.is_url_incremented_by_one ? is_url_incremented_scale_ : 0.0) +
+      (source_engagement_score_scale_ * document_engagement_score) +
+      (target_engagement_score_scale_ * target_engagement_score) +
+      (area_rank_scale_ * area_rank_score) +
+      (ratio_distance_root_top_scale_ *
+       (metrics.ratio_distance_root_top * 100.0));
 
   if (normalize_navigation_scores_) {
     score = score / sum_link_scales_ * 100.0;
@@ -985,15 +986,15 @@
     return 0;
   } else {
     DCHECK(!viewport_size_.IsEmpty());
-    return link_total_scale_ * number_of_anchors_ +
-           iframe_link_total_scale_ * number_of_anchors_in_iframe_ +
-           increment_link_total_scale_ * number_of_anchors_url_incremented_ +
-           same_origin_link_total_scale_ * number_of_anchors_same_host_ +
-           image_link_total_scale_ * number_of_anchors_contains_image_ +
-           clickable_space_scale_ * total_clickable_space_ +
-           median_link_location_scale_ * median_link_location_ +
-           viewport_width_scale_ * viewport_size_.width() +
-           viewport_height_scale_ * viewport_size_.height();
+    return (link_total_scale_ * number_of_anchors_) +
+           (iframe_link_total_scale_ * number_of_anchors_in_iframe_) +
+           (increment_link_total_scale_ * number_of_anchors_url_incremented_) +
+           (same_origin_link_total_scale_ * number_of_anchors_same_host_) +
+           (image_link_total_scale_ * number_of_anchors_contains_image_) +
+           (clickable_space_scale_ * total_clickable_space_) +
+           (median_link_location_scale_ * median_link_location_) +
+           (viewport_width_scale_ * viewport_size_.width()) +
+           (viewport_height_scale_ * viewport_size_.height());
   }
 }
 
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.h b/chrome/browser/navigation_predictor/navigation_predictor.h
index 9a6ef74..f41e67b 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.h
+++ b/chrome/browser/navigation_predictor/navigation_predictor.h
@@ -239,10 +239,11 @@
 
   // Viewport-related metrics for anchor elements: the viewport size,
   // the median distance down the viewport of all the links, and the
-  // total clickable space for first viewport links.
+  // total clickable space for first viewport links. |total_clickable_space_| is
+  // a percent (between 0 and 100).
   gfx::Size viewport_size_;
   int median_link_location_ = 0;
-  int total_clickable_space_ = 0;
+  float total_clickable_space_ = 0;
 
   // Anchor-specific scaling factors used to compute navigation scores.
   const int ratio_area_scale_;
diff --git a/chrome/browser/net/dns_util.cc b/chrome/browser/net/dns_util.cc
index af1998f..42261d6 100644
--- a/chrome/browser/net/dns_util.cc
+++ b/chrome/browser/net/dns_util.cc
@@ -10,6 +10,10 @@
 #include "net/third_party/uri_template/uri_template.h"
 #include "url/gurl.h"
 
+#if defined(OS_WIN)
+#include "base/enterprise_util.h"
+#endif
+
 namespace chrome_browser_net {
 
 bool IsValidDohTemplate(const std::string& server_template,
@@ -43,8 +47,12 @@
 
 bool ShouldDisableDohForManaged() {
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
-  return g_browser_process->browser_policy_connector()
-      ->HasMachineLevelPolicies();
+  if (g_browser_process->browser_policy_connector()->HasMachineLevelPolicies())
+    return true;
+#endif
+#if defined(OS_WIN)
+  if (base::IsMachineExternallyManaged())
+    return true;
 #endif
   return false;
 }
diff --git a/chrome/browser/net/dns_util.h b/chrome/browser/net/dns_util.h
index 8e2cc99d..7935013 100644
--- a/chrome/browser/net/dns_util.h
+++ b/chrome/browser/net/dns_util.h
@@ -16,11 +16,11 @@
 bool IsValidDohTemplate(const std::string& server_template,
                         std::string* server_method);
 
-// Returns true if any machine level policies. ChromeOS devices are already
-// handled by the default_for_enterprise_users field on the DoH policy. We don't
-// attempt enterprise detection on Android at this time. This special logic is
-// to prevent enterprises from having DoH enabled by default and is necessary
-// because default_for_enterprise_users only applies to ChromeOS.
+// Returns true if there are any active machine level policies or if the machine
+// is domain joined. This special logic is used to disable DoH by default for
+// Desktop platforms (the enterprise policy field default_for_enterprise_users
+// only applies to ChromeOS). We don't attempt enterprise detection on Android
+// at this time.
 bool ShouldDisableDohForManaged();
 
 const char kDnsOverHttpsModeOff[] = "off";
diff --git a/chrome/browser/net/system_network_context_manager_browsertest.cc b/chrome/browser/net/system_network_context_manager_browsertest.cc
index 3d06290..f277e6f7 100644
--- a/chrome/browser/net/system_network_context_manager_browsertest.cc
+++ b/chrome/browser/net/system_network_context_manager_browsertest.cc
@@ -28,6 +28,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 
+#if defined(OS_WIN)
+#include "base/win/win_util.h"
+#endif
+
 namespace {
 
 void GetStubResolverConfig(
@@ -46,6 +50,10 @@
 // various DoH modes and DoH template strings and makes sure the settings are
 // respected.
 void RunStubResolverConfigTests(bool async_dns_feature_enabled) {
+  // Mark as not enterprise managed.
+#if defined(OS_WIN)
+  base::win::ScopedDomainStateForTesting scoped_domain(false);
+#endif
   // Check initial state.
   bool insecure_stub_resolver_enabled = !async_dns_feature_enabled;
   net::DnsConfig::SecureDnsMode secure_dns_mode;
@@ -284,7 +292,7 @@
 
 INSTANTIATE_TEST_SUITE_P(,
                          SystemNetworkContextManagerStubResolverBrowsertest,
-                         ::testing::Values(false, true));
+                         ::testing::Bool());
 
 class SystemNetworkContextManagerFreezeQUICUaBrowsertest
     : public SystemNetworkContextManagerBrowsertest,
@@ -326,4 +334,27 @@
 
 INSTANTIATE_TEST_SUITE_P(,
                          SystemNetworkContextManagerFreezeQUICUaBrowsertest,
-                         ::testing::Values(true, false));
+                         ::testing::Bool());
+
+class SystemNetworkContextManagerWPADQuickCheckBrowsertest
+    : public SystemNetworkContextManagerBrowsertest,
+      public testing::WithParamInterface<bool> {
+ public:
+  SystemNetworkContextManagerWPADQuickCheckBrowsertest() = default;
+  ~SystemNetworkContextManagerWPADQuickCheckBrowsertest() override = default;
+};
+
+IN_PROC_BROWSER_TEST_P(SystemNetworkContextManagerWPADQuickCheckBrowsertest,
+                       WPADQuickCheckPref) {
+  PrefService* local_state = g_browser_process->local_state();
+  local_state->SetBoolean(prefs::kQuickCheckEnabled, GetParam());
+
+  network::mojom::NetworkContextParamsPtr network_context_params =
+      g_browser_process->system_network_context_manager()
+          ->CreateDefaultNetworkContextParams();
+  EXPECT_EQ(GetParam(), network_context_params->pac_quick_check_enabled);
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         SystemNetworkContextManagerWPADQuickCheckBrowsertest,
+                         ::testing::Bool());
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
index e581915..b33883d2 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
+++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
@@ -11,8 +11,10 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/guid.h"
 #include "base/memory/weak_ptr.h"
+#include "base/stl_util.h"
 #include "base/task/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/notifications/scheduler/internal/icon_store.h"
@@ -35,19 +37,33 @@
   return lhs->create_time <= rhs->create_time;
 }
 
-void ValidateNotificationParams(const NotificationParams& params) {
-  // Validate time window.
-  DCHECK(params.schedule_params.deliver_time_start.has_value() &&
-         params.schedule_params.deliver_time_end.has_value())
-      << "Currently only support deliver in a time window,";
-  DCHECK(params.schedule_params.deliver_time_start.value() <=
-         params.schedule_params.deliver_time_end.value());
-
-  // Validate ihnr buttons option.
-  if (params.enable_ihnr_buttons) {
-    DCHECK(params.notification_data.buttons.empty())
-        << "Can't have custom buttons when have helpful/unhelpful buttons.";
+// Vailidates notification parameters. Returns false if the parameters are
+// invalid.
+bool ValidateNotificationParams(const NotificationParams& params) {
+  // Validate time window. Currently we only support deliver notification
+  // according to a time window.
+  if (!params.schedule_params.deliver_time_start.has_value() ||
+      !params.schedule_params.deliver_time_end.has_value() ||
+      params.schedule_params.deliver_time_start.value() >
+          params.schedule_params.deliver_time_end.value()) {
+    return false;
   }
+
+  // Validate ihnr buttons option. Custom buttons will be overwritten.
+  if (params.enable_ihnr_buttons && !params.notification_data.buttons.empty()) {
+    return false;
+  }
+  return true;
+}
+
+// Vailidates notification entry. Returns false if the entry should be deleted.
+bool ValidateNotificationEntry(const NotificationEntry& entry) {
+  // Check the deliver time window.
+  return (entry.schedule_params.deliver_time_start.has_value() &&
+          entry.schedule_params.deliver_time_end.has_value() &&
+          entry.schedule_params.deliver_time_end > base::Time::Now() &&
+          entry.schedule_params.deliver_time_end >=
+              entry.schedule_params.deliver_time_start);
 }
 
 class ScheduledNotificationManagerImpl : public ScheduledNotificationManager {
@@ -66,16 +82,16 @@
         config_(config) {}
 
  private:
+  // NotificationManager implementation.
   void Init(Delegate* delegate, InitCallback callback) override {
     DCHECK(!delegate_);
     delegate_ = delegate;
 
-    notification_store_->InitAndLoad(base::BindOnce(
-        &ScheduledNotificationManagerImpl::OnNotificationStoreInitialized,
+    icon_store_->Init(base::BindOnce(
+        &ScheduledNotificationManagerImpl::OnIconStoreInitialized,
         weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
-  // NotificationManager implementation.
   void ScheduleNotification(
       std::unique_ptr<NotificationParams> notification_params) override {
     DCHECK(notification_params);
@@ -89,7 +105,10 @@
       return;
     }
 
-    ValidateNotificationParams(*notification_params);
+    bool valid = ValidateNotificationParams(*notification_params);
+    DCHECK(valid) << "Invalid notification parameters.";
+    if (!valid)
+      return;
 
     if (notification_params->enable_ihnr_buttons) {
       CreateInhrButtonsPair(&notification_params->notification_data.buttons);
@@ -108,15 +127,16 @@
   }
 
   void DisplayNotification(const std::string& guid) override {
-    std::unique_ptr<NotificationEntry> entry;
+    NotificationEntry* entry = nullptr;
     for (auto it = notifications_.begin(); it != notifications_.end(); it++) {
       if (it->second.count(guid)) {
-        entry = std::move(it->second[guid]);
+        entry = it->second[guid].get();
         break;
       }
     }
 
-    DCHECK(entry);
+    if (!entry)
+      return;
 
     std::vector<std::string> keys;
     for (const auto& pair : entry->icons_uuid) {
@@ -125,7 +145,8 @@
     icon_store_->LoadIcons(
         std::move(keys),
         base::BindOnce(&ScheduledNotificationManagerImpl::OnIconsLoaded,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(entry)));
+                       weak_ptr_factory_.GetWeakPtr(), entry->type,
+                       entry->guid));
   }
 
   void GetAllNotifications(Notifications* notifications) const override {
@@ -160,30 +181,25 @@
   void DeleteNotifications(SchedulerClientType type) override {
     if (!notifications_.count(type))
       return;
+
     auto it = notifications_[type].begin();
     while (it != notifications_[type].end()) {
       const auto& entry = *it->second;
       ++it;
-      notification_store_->Delete(
-          entry.guid,
-          base::BindOnce(
-              &ScheduledNotificationManagerImpl::OnNotificationDeleted,
-              weak_ptr_factory_.GetWeakPtr()));
+      DeleteNotificationInDb(entry);
     }
     notifications_.erase(type);
   }
 
-  // Sync with registered clients. Delete entrties in |notifications_| if
-  // their clients are deprecated.
-  void SyncRegisteredClients() {
-    auto it = notifications_.begin();
-    while (it != notifications_.end()) {
-      auto type = it->first;
-      it++;
-      if (!clients_.count(type)) {
-        DeleteNotifications(type);
-      }
+  void OnIconStoreInitialized(InitCallback callback, bool success) {
+    if (!success) {
+      std::move(callback).Run(false);
+      return;
     }
+
+    notification_store_->InitAndLoad(base::BindOnce(
+        &ScheduledNotificationManagerImpl::OnNotificationStoreInitialized,
+        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
   void OnNotificationStoreInitialized(
@@ -197,30 +213,43 @@
       return;
     }
 
+    FilterNotificationEntries(std::move(entries));
+    std::move(callback).Run(true);
+  }
+
+  // Filters and loads notification into memory.
+  void FilterNotificationEntries(
+      CollectionStore<NotificationEntry>::Entries entries) {
     for (auto it = entries.begin(); it != entries.end(); it++) {
       auto* entry = it->get();
       // Prune expired notifications. Also delete them in db.
       bool expired = entry->create_time + config_.notification_expiration <=
                      base::Time::Now();
-      if (expired) {
-        notification_store_->Delete(
-            entry->guid,
-            base::BindOnce(
-                &ScheduledNotificationManagerImpl::OnNotificationDeleted,
-                weak_ptr_factory_.GetWeakPtr()));
-      } else if (clients_.count(entry->type)) {
+      bool valid = ValidateNotificationEntry(*entry);
+      bool deprecated_client = !base::Contains(clients_, entry->type);
+      if (expired || deprecated_client || !valid) {
+        DeleteNotificationInDb(*entry);
+      } else {
         notifications_[entry->type].emplace(entry->guid, std::move(*it));
       }
     }
-    SyncRegisteredClients();
-
-    icon_store_->Init(base::BindOnce(
-        &ScheduledNotificationManagerImpl::OnIconStoreInitialized,
-        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
-  void OnIconStoreInitialized(InitCallback callback, bool success) {
-    std::move(callback).Run(success);
+  // Deletes a notification entry and its associated icon resources from
+  // database.
+  void DeleteNotificationInDb(const NotificationEntry& entry) {
+    // Deletes icon first.
+    std::vector<std::string> icons_to_delete;
+    for (const auto& icon_id : entry.icons_uuid) {
+      icons_to_delete.emplace_back(icon_id.second);
+    }
+    icon_store_->DeleteIcons(std::move(icons_to_delete), base::DoNothing());
+
+    // Deletes notification entry.
+    notification_store_->Delete(
+        entry.guid,
+        base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationDeleted,
+                       weak_ptr_factory_.GetWeakPtr()));
   }
 
   void OnNotificationAdded(SchedulerClientType type,
@@ -267,24 +296,42 @@
 
   void OnIconDeleted(bool success) { NOTIMPLEMENTED(); }
 
-  void OnIconsLoaded(std::unique_ptr<NotificationEntry> entry,
+  void OnIconsLoaded(SchedulerClientType client_type,
+                     const std::string& guid,
                      bool success,
                      IconStore::LoadedIconsMap loaded_icons_map) {
     // TODO(hesen): delete notification entry if icons failed to load.
     if (!success)
       return;
+
+    // Delete icons from database.
+    std::vector<std::string> icons_to_delete;
+    for (const auto& loaded_icon : loaded_icons_map) {
+      icons_to_delete.emplace_back(loaded_icon.first);
+    }
+    icon_store_->DeleteIcons(std::move(icons_to_delete), base::DoNothing());
+
+    // Can't find the entry.
+    if (!FindNotificationEntry(client_type, guid)) {
+      return;
+    }
+
+    // Glue the icon data to entry.
+    std::unique_ptr<NotificationEntry> entry =
+        std::move(notifications_[client_type][guid]);
+    DCHECK(entry);
     for (const auto& pair : entry->icons_uuid) {
       auto icon_bundle = IconBundle(std::move(loaded_icons_map[pair.second]));
       entry->notification_data.icons.emplace(pair.first,
                                              std::move(icon_bundle));
     }
-    auto type = entry->type;
-    auto guid = entry->guid;
-    notifications_[type].erase(guid);
-    if (notifications_[type].empty())
-      notifications_.erase(type);
+
+    // Before moving out the entry, delete it from container and disk.
+    notifications_[entry->type].erase(entry->guid);
+    if (notifications_[entry->type].empty())
+      notifications_.erase(entry->type);
     notification_store_->Delete(
-        guid,
+        entry->guid,
         base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationDeleted,
                        weak_ptr_factory_.GetWeakPtr()));
 
@@ -329,6 +376,7 @@
       this};
   DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManagerImpl);
 };
+
 }  // namespace
 
 // static
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
index 7a86cc6..bc1f41b 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
@@ -34,7 +34,12 @@
 const char kLargeIconUuid[] = "test_large_icon_uuid";
 
 NotificationEntry CreateNotificationEntry(SchedulerClientType type) {
-  return NotificationEntry(type, base::GenerateGUID());
+  NotificationEntry entry(type, base::GenerateGUID());
+  entry.schedule_params.deliver_time_start =
+      base::Time::Now() + base::TimeDelta::FromDays(1);
+  entry.schedule_params.deliver_time_end =
+      base::Time::Now() + base::TimeDelta::FromDays(2);
+  return entry;
 }
 
 IconStore::IconTypeBundleMap CreateIcons() {
@@ -111,9 +116,7 @@
     config_.notification_expiration = base::TimeDelta::FromDays(1);
     manager_ = ScheduledNotificationManager::Create(
         std::move(notification_store), std::move(icon_store),
-        {SchedulerClientType::kTest1, SchedulerClientType::kTest2,
-         SchedulerClientType::kTest3},
-        config_);
+        {SchedulerClientType::kTest1, SchedulerClientType::kTest2}, config_);
   }
 
  protected:
@@ -122,6 +125,7 @@
   MockIconStore* icon_store() { return icon_store_; }
   MockDelegate* delegate() { return delegate_.get(); }
   const SchedulerConfig& config() const { return config_; }
+
   // Initializes the manager with predefined data in the store.
   void InitWithData(std::vector<NotificationEntry> data) {
     Entries entries;
@@ -165,11 +169,42 @@
   DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManagerTest);
 };
 
-// Verify that error is received when initialization failed.
-TEST_F(ScheduledNotificationManagerTest, InitFailed) {
+// Verify that error is received when notification database failed to
+// initialize.
+TEST_F(ScheduledNotificationManagerTest, NotificationDbInitFailed) {
   EXPECT_CALL(*notification_store(), InitAndLoad(_))
-      .WillOnce(Invoke([](base::OnceCallback<void(bool, Entries)> cb) {
-        std::move(cb).Run(false, Entries());
+      .WillOnce(Invoke([](base::OnceCallback<void(bool, Entries)> callback) {
+        std::move(callback).Run(false, Entries());
+      }));
+
+  EXPECT_CALL(*icon_store(), Init(_))
+      .WillOnce(Invoke([](base::OnceCallback<void(bool)> callback) {
+        std::move(callback).Run(true);
+      }));
+
+  base::RunLoop loop;
+  manager()->Init(delegate(),
+                  base::BindOnce(
+                      [](base::RepeatingClosure closure, bool success) {
+                        // Expected to receive error.
+                        EXPECT_FALSE(success);
+                        std::move(closure).Run();
+                      },
+                      loop.QuitClosure()));
+  loop.Run();
+}
+
+// Verify that error is received when icon database failed to initialize.
+TEST_F(ScheduledNotificationManagerTest, IconDbInitFailed) {
+  ON_CALL(*notification_store(), InitAndLoad(_))
+      .WillByDefault(
+          Invoke([](base::OnceCallback<void(bool, Entries)> callback) {
+            std::move(callback).Run(true, Entries());
+          }));
+
+  EXPECT_CALL(*icon_store(), Init(_))
+      .WillOnce(Invoke([](base::OnceCallback<void(bool)> callback) {
+        std::move(callback).Run(false);
       }));
 
   base::RunLoop loop;
@@ -293,6 +328,7 @@
         std::move(callback).Run(true, {});
       }));
   EXPECT_CALL(*notification_store(), Delete(kGuid, _));
+  EXPECT_CALL(*icon_store(), DeleteIcons(_, _));
   EXPECT_CALL(*delegate(), DisplayNotification(NotificationEntryIs(entry)));
   manager()->DisplayNotification(kGuid);
 
@@ -349,23 +385,21 @@
 TEST_F(ScheduledNotificationManagerTest, DeleteNotifications) {
   // Type1: entry0
   // Type2: entry1, entry2
-  // Type3: entry3
   auto entry0 = CreateNotificationEntry(SchedulerClientType::kTest1);
   auto entry1 = CreateNotificationEntry(SchedulerClientType::kTest2);
   auto entry2 = CreateNotificationEntry(SchedulerClientType::kTest2);
-  auto entry3 = CreateNotificationEntry(SchedulerClientType::kTest3);
-  InitWithData(
-      std::vector<NotificationEntry>({entry0, entry1, entry2, entry3}));
+  InitWithData(std::vector<NotificationEntry>({entry0, entry1, entry2}));
   ScheduledNotificationManager::Notifications notifications;
   manager()->GetAllNotifications(&notifications);
-  EXPECT_EQ(notifications.size(), 3u);
+  EXPECT_EQ(notifications.size(), 2u);
 
   EXPECT_CALL(*notification_store(), Delete(_, _))
       .Times(2)
       .RetiresOnSaturation();
+  EXPECT_CALL(*icon_store(), DeleteIcons(_, _)).Times(2).RetiresOnSaturation();
   manager()->DeleteNotifications(SchedulerClientType::kTest2);
   manager()->GetAllNotifications(&notifications);
-  EXPECT_EQ(notifications.size(), 2u);
+  EXPECT_EQ(notifications.size(), 1u);
 
   // Ensure deleting non-existing key will not crash, and store will not call
   // Delete.
@@ -374,48 +408,50 @@
       .RetiresOnSaturation();
   manager()->DeleteNotifications(SchedulerClientType::kTest2);
   manager()->GetAllNotifications(&notifications);
-  EXPECT_EQ(notifications.size(), 2u);
-
-  EXPECT_CALL(*notification_store(), Delete(_, _)).RetiresOnSaturation();
-  manager()->DeleteNotifications(SchedulerClientType::kTest1);
-  manager()->GetAllNotifications(&notifications);
   EXPECT_EQ(notifications.size(), 1u);
 
   EXPECT_CALL(*notification_store(), Delete(_, _)).RetiresOnSaturation();
+  EXPECT_CALL(*icon_store(), DeleteIcons(_, _)).RetiresOnSaturation();
+  manager()->DeleteNotifications(SchedulerClientType::kTest1);
+  manager()->GetAllNotifications(&notifications);
+  EXPECT_EQ(notifications.size(), 0u);
+
+  // Unregistered client.
+  EXPECT_CALL(*notification_store(), Delete(_, _)).Times(0);
   manager()->DeleteNotifications(SchedulerClientType::kTest3);
   manager()->GetAllNotifications(&notifications);
   EXPECT_EQ(notifications.size(), 0u);
 }
 
-TEST_F(ScheduledNotificationManagerTest, PruneExpiredNotifications) {
+// Verifies that unused notifications will be deleted during initialization.
+TEST_F(ScheduledNotificationManagerTest, PruneNotifications) {
   // Type1: entry0
-  // Type2: entry1, entry2(expired), entry3
-  // Type3: entry4(expired), entry5(expired)
+  // Type2: entry1(invalid), entry2(expired), entry3
+  // Type3: entry4(unregistered client)
   auto now = base::Time::Now();
   auto entry0 = CreateNotificationEntry(SchedulerClientType::kTest1);
   entry0.create_time = now - base::TimeDelta::FromHours(12);
   auto entry1 = CreateNotificationEntry(SchedulerClientType::kTest2);
   entry1.create_time = now - base::TimeDelta::FromHours(14);
+  entry1.schedule_params.deliver_time_start =
+      base::Time::Now() - base::TimeDelta::FromDays(2);
+  entry1.schedule_params.deliver_time_end =
+      base::Time::Now() - base::TimeDelta::FromDays(1);
   auto entry2 = CreateNotificationEntry(SchedulerClientType::kTest2);
   entry2.create_time = now - base::TimeDelta::FromHours(24);
   auto entry3 = CreateNotificationEntry(SchedulerClientType::kTest2);
   entry3.create_time = now - base::TimeDelta::FromHours(23);
   auto entry4 = CreateNotificationEntry(SchedulerClientType::kTest3);
-  entry4.create_time = now - base::TimeDelta::FromHours(25);
-  auto entry5 = CreateNotificationEntry(SchedulerClientType::kTest3);
-  entry5.create_time =
-      now - base::TimeDelta::FromDays(1) - base::TimeDelta::FromMicroseconds(1);
 
-  EXPECT_CALL(*notification_store(), Delete(_, _))
-      .Times(3)
-      .RetiresOnSaturation();
-  InitWithData(std::vector<NotificationEntry>(
-      {entry0, entry1, entry2, entry3, entry4, entry5}));
+  EXPECT_CALL(*notification_store(), Delete(_, _)).Times(3);
+  EXPECT_CALL(*icon_store(), DeleteIcons(_, _)).Times(3);
+  InitWithData(
+      std::vector<NotificationEntry>({entry0, entry1, entry2, entry3, entry4}));
   ScheduledNotificationManager::Notifications notifications;
   manager()->GetAllNotifications(&notifications);
   EXPECT_EQ(notifications.size(), 2u);
   EXPECT_EQ(notifications[SchedulerClientType::kTest1].size(), 1u);
-  EXPECT_EQ(notifications[SchedulerClientType::kTest2].size(), 2u);
+  EXPECT_EQ(notifications[SchedulerClientType::kTest2].size(), 1u);
 }
 
 // Test to schedule a notification with two icons in notification data.
@@ -519,6 +555,8 @@
         std::move(callback).Run(true, std::move(result));
       }));
   EXPECT_CALL(*notification_store(), Delete(kGuid, _));
+  EXPECT_CALL(*icon_store(), DeleteIcons(_, _));
+
   auto expected_entry(entry);
   expected_entry.notification_data.icons = icons;
   EXPECT_CALL(*delegate(),
diff --git a/chrome/browser/notifications/win/notification_launch_id.cc b/chrome/browser/notifications/win/notification_launch_id.cc
index db27a59a..7abc864 100644
--- a/chrome/browser/notifications/win/notification_launch_id.cc
+++ b/chrome/browser/notifications/win/notification_launch_id.cc
@@ -132,7 +132,7 @@
   origin_url_ = GURL(tokens[4]);
 
   notification_id_.clear();
-  // Notification IDs is the rest of the string (delimeters not stripped off).
+  // Notification IDs is the rest of the string (delimiters not stripped off).
   const size_t kMinVectorSize = 5;
   for (size_t i = kMinVectorSize; i < tokens.size(); ++i) {
     if (i > kMinVectorSize)
@@ -145,7 +145,7 @@
 }
 
 std::string NotificationLaunchId::Serialize() const {
-  // The pipe was chosen as delimeter because it is invalid for directory paths
+  // The pipe was chosen as delimiter because it is invalid for directory paths
   // and unsafe for origins -- and should therefore be encoded (as per
   // http://www.ietf.org/rfc/rfc1738.txt).
   std::string prefix;
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index 4d080ee2..f03d488 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -115,6 +115,40 @@
       ->GetOrCreateOptimizationGuideNavigationData(navigation_handle);
 }
 
+// Returns the page hint for the navigation, if applicable. It will use the
+// cached page hint stored in |navigation_handle| if we have already done the
+// computation to find the page hint in a previous request to the hints manager.
+// Otherwise, we will loop through the page hints in |loaded_hint| to find the
+// one that matches and store it for subsequent calls for the navigation.
+const optimization_guide::proto::PageHint* GetPageHintForNavigation(
+    content::NavigationHandle* navigation_handle,
+    const optimization_guide::proto::Hint* loaded_hint) {
+  OptimizationGuideNavigationData* navigation_data =
+      GetNavigationDataForNavigationHandle(navigation_handle);
+
+  // If we already know we had a page hint for the navigation, then just return
+  // that.
+  if (navigation_data && navigation_data->has_page_hint_value()) {
+    return navigation_data->page_hint();
+  }
+
+  // We do not yet know the answer, so find the applicable page hint.
+  const optimization_guide::proto::PageHint* matched_page_hint =
+      optimization_guide::FindPageHintForURL(navigation_handle->GetURL(),
+                                             loaded_hint);
+
+  if (navigation_data) {
+    // Store the page hint for the next time this is called, so we do not have
+    // to loop over all page hints within a hint.
+    navigation_data->set_page_hint(
+        matched_page_hint
+            ? std::make_unique<optimization_guide::proto::PageHint>(
+                  *matched_page_hint)
+            : nullptr);
+  }
+  return matched_page_hint;
+}
+
 }  // namespace
 
 OptimizationGuideHintsManager::OptimizationGuideHintsManager(
@@ -619,7 +653,7 @@
       hint_cache_->GetHintIfLoaded(host);
   bool has_hint_in_cache = hint_cache_->HasHint(host);
   const optimization_guide::proto::PageHint* matched_page_hint =
-      loaded_hint ? optimization_guide::FindPageHintForURL(url, loaded_hint)
+      loaded_hint ? GetPageHintForNavigation(navigation_handle, loaded_hint)
                   : nullptr;
 
   // Populate navigation data with hint information.
@@ -627,7 +661,6 @@
       GetNavigationDataForNavigationHandle(navigation_handle);
   if (navigation_data) {
     navigation_data->set_has_hint_after_commit(has_hint_in_cache);
-    navigation_data->set_has_page_hint(matched_page_hint);
 
     if (loaded_hint)
       navigation_data->set_serialized_hint_version_string(
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
index 160f1b1..e702b615 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -639,7 +639,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
-  EXPECT_EQ(base::nullopt, navigation_data->has_page_hint());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest, LoadHintForNavigationWithHint) {
@@ -663,7 +663,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
-  EXPECT_EQ(base::nullopt, navigation_data->has_page_hint());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest, LoadHintForNavigationNoHint) {
@@ -687,7 +687,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
   EXPECT_FALSE(navigation_data->has_hint_before_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
-  EXPECT_EQ(base::nullopt, navigation_data->has_page_hint());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest, LoadHintForNavigationNoHost) {
@@ -710,7 +710,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
-  EXPECT_EQ(base::nullopt, navigation_data->has_page_hint());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1115,7 +1115,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
-  EXPECT_EQ(base::nullopt, navigation_data->has_page_hint());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1159,7 +1159,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
-  EXPECT_FALSE(navigation_data->has_page_hint().value());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1206,7 +1206,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
-  EXPECT_FALSE(navigation_data->has_page_hint().value());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1253,7 +1253,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
-  EXPECT_FALSE(navigation_data->has_page_hint().value());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest, CanApplyOptimizationNoECTEstimate) {
@@ -1300,7 +1300,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
-  EXPECT_FALSE(navigation_data->has_page_hint().value());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1348,7 +1348,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
-  EXPECT_FALSE(navigation_data->has_page_hint().value());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1392,7 +1392,7 @@
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
-  EXPECT_TRUE(navigation_data->has_page_hint().value());
+  ASSERT_TRUE(navigation_data->page_hint());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1431,7 +1431,7 @@
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
-  EXPECT_TRUE(navigation_data->has_page_hint().value());
+  ASSERT_TRUE(navigation_data->page_hint());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1471,7 +1471,7 @@
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
-  EXPECT_TRUE(navigation_data->has_page_hint().value());
+  ASSERT_TRUE(navigation_data->page_hint());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1507,7 +1507,7 @@
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
-  EXPECT_EQ(base::nullopt, navigation_data->has_page_hint());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1546,7 +1546,50 @@
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
-  EXPECT_TRUE(navigation_data->has_page_hint().value());
+  ASSERT_TRUE(navigation_data->page_hint());
+}
+
+TEST_F(OptimizationGuideHintsManagerTest,
+       CanApplyOptimizationUsesCachedPageHintFromNavigationData) {
+  InitializeWithDefaultConfig("1.0.0.0");
+
+  // Set ECT estimate so hint is activated.
+  hints_manager()->OnEffectiveConnectionTypeChanged(
+      net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
+  std::unique_ptr<content::MockNavigationHandle> navigation_handle =
+      CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
+          url_with_hints());
+  base::RunLoop run_loop;
+  hints_manager()->LoadHintForNavigation(navigation_handle.get(),
+                                         run_loop.QuitClosure());
+  run_loop.Run();
+
+  // Purposely set the page hint to be null to show that we override the page
+  // hint information from the navigation handle.
+  OptimizationGuideNavigationData* navigation_data =
+      GetOptimizationGuideNavigationData(navigation_handle.get());
+  navigation_data->set_page_hint(nullptr);
+
+  optimization_guide::OptimizationTargetDecision optimization_target_decision;
+  optimization_guide::OptimizationTypeDecision optimization_type_decision;
+  optimization_guide::OptimizationMetadata optimization_metadata;
+  hints_manager()->CanApplyOptimization(
+      navigation_handle.get(),
+      optimization_guide::OptimizationTarget::kPainfulPageLoad,
+      optimization_guide::proto::DEFER_ALL_SCRIPT,
+      &optimization_target_decision, &optimization_type_decision,
+      /*optimization_metadata=*/nullptr);
+
+  // Make sure decisions are logged correctly.
+  EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
+            optimization_target_decision);
+  EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNoMatchingPageHint,
+            optimization_type_decision);
+  // Make sure navigation data is populated correctly.
+  EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
+  EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
+  EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
+  EXPECT_EQ(nullptr, navigation_data->page_hint());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1585,7 +1628,7 @@
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
-  EXPECT_FALSE(navigation_data->has_page_hint().value());
+  EXPECT_EQ(nullptr, navigation_data->page_hint());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1621,7 +1664,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
-  EXPECT_FALSE(navigation_data->has_page_hint().value());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1656,7 +1699,7 @@
   EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
-  EXPECT_FALSE(navigation_data->has_page_hint().value());
+  EXPECT_FALSE(navigation_data->has_page_hint_value());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1719,7 +1762,7 @@
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
-  EXPECT_TRUE(navigation_data->has_page_hint().value());
+  ASSERT_TRUE(navigation_data->page_hint());
 }
 
 TEST_F(OptimizationGuideHintsManagerTest,
@@ -1783,5 +1826,5 @@
   EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
   EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
   EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
-  EXPECT_TRUE(navigation_data->has_page_hint().value());
+  ASSERT_TRUE(navigation_data->page_hint());
 }
diff --git a/chrome/browser/optimization_guide/optimization_guide_navigation_data.cc b/chrome/browser/optimization_guide/optimization_guide_navigation_data.cc
index 3c8d52f..75f8603 100644
--- a/chrome/browser/optimization_guide/optimization_guide_navigation_data.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_navigation_data.cc
@@ -40,7 +40,18 @@
 OptimizationGuideNavigationData::~OptimizationGuideNavigationData() = default;
 
 OptimizationGuideNavigationData::OptimizationGuideNavigationData(
-    const OptimizationGuideNavigationData& other) = default;
+    const OptimizationGuideNavigationData& other)
+    : navigation_id_(other.navigation_id_),
+      serialized_hint_version_string_(other.serialized_hint_version_string_),
+      optimization_type_decisions_(other.optimization_type_decisions_),
+      optimization_target_decisions_(other.optimization_target_decisions_),
+      has_hint_before_commit_(other.has_hint_before_commit_),
+      has_hint_after_commit_(other.has_hint_after_commit_) {
+  if (other.has_page_hint_value()) {
+    page_hint_ = std::make_unique<optimization_guide::proto::PageHint>(
+        *other.page_hint());
+  }
+}
 
 void OptimizationGuideNavigationData::RecordMetrics(bool has_committed) const {
   RecordHintCacheMatch(has_committed);
@@ -71,7 +82,7 @@
                         had_hint_loaded);
   if (had_hint_loaded) {
     UMA_HISTOGRAM_BOOLEAN("OptimizationGuide.HintCache.PageMatch.AtCommit",
-                          has_page_hint_.has_value() && has_page_hint_.value());
+                          has_page_hint_value() && page_hint());
   }
 }
 
diff --git a/chrome/browser/optimization_guide/optimization_guide_navigation_data.h b/chrome/browser/optimization_guide/optimization_guide_navigation_data.h
index f91c42d..d43fb03 100644
--- a/chrome/browser/optimization_guide/optimization_guide_navigation_data.h
+++ b/chrome/browser/optimization_guide/optimization_guide_navigation_data.h
@@ -6,8 +6,10 @@
 #define CHROME_BROWSER_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_NAVIGATION_DATA_H_
 
 #include <stdint.h>
+#include <memory>
 #include <string>
 #include <unordered_map>
+#include <utility>
 
 #include "base/optional.h"
 #include "components/optimization_guide/optimization_guide_decider.h"
@@ -74,9 +76,15 @@
     has_hint_after_commit_ = has_hint_after_commit;
   }
 
-  // Whether the hint cache had a page hint for the navigation.
-  base::Optional<bool> has_page_hint() const { return has_page_hint_; }
-  void set_has_page_hint(bool has_page_hint) { has_page_hint_ = has_page_hint; }
+  // The page hint applicable for the navigation.
+  bool has_page_hint_value() const { return !!page_hint_; }
+  const optimization_guide::proto::PageHint* page_hint() const {
+    return page_hint_.value().get();
+  }
+  void set_page_hint(
+      std::unique_ptr<optimization_guide::proto::PageHint> page_hint) {
+    page_hint_ = std::move(page_hint);
+  }
 
  private:
   // Records hint cache histograms based on data currently held in |this|.
@@ -114,8 +122,9 @@
   // Whether the hint cache had a hint for the navigation after commit.
   base::Optional<bool> has_hint_after_commit_ = base::nullopt;
 
-  // Whether there was a page hint for the navigation.
-  base::Optional<bool> has_page_hint_ = base::nullopt;
+  // The page hint for the navigation.
+  base::Optional<std::unique_ptr<optimization_guide::proto::PageHint>>
+      page_hint_ = base::nullopt;
 
   DISALLOW_ASSIGN(OptimizationGuideNavigationData);
 };
diff --git a/chrome/browser/optimization_guide/optimization_guide_navigation_data_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_navigation_data_unittest.cc
index 2e10a25d..837b1b5 100644
--- a/chrome/browser/optimization_guide/optimization_guide_navigation_data_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_navigation_data_unittest.cc
@@ -125,7 +125,7 @@
   data.set_has_hint_before_commit(true);
   data.set_has_hint_after_commit(true);
   data.set_serialized_hint_version_string("abc");
-  data.set_has_page_hint(true);
+  data.set_page_hint(std::make_unique<optimization_guide::proto::PageHint>());
   data.RecordMetrics(/*has_committed=*/true);
 
   histogram_tester.ExpectUniqueSample(
@@ -166,7 +166,7 @@
   data.set_has_hint_before_commit(true);
   data.set_has_hint_after_commit(true);
   data.set_serialized_hint_version_string("abc");
-  data.set_has_page_hint(false);
+  data.set_page_hint(nullptr);
   data.RecordMetrics(/*has_committed=*/true);
 
   histogram_tester.ExpectUniqueSample(
@@ -478,7 +478,7 @@
                 optimization_guide::OptimizationTarget::kPainfulPageLoad));
   EXPECT_EQ(base::nullopt, data->has_hint_before_commit());
   EXPECT_EQ(base::nullopt, data->has_hint_after_commit());
-  EXPECT_EQ(base::nullopt, data->has_page_hint());
+  EXPECT_FALSE(data->has_page_hint_value());
 
   data->set_serialized_hint_version_string("123abc");
   data->SetDecisionForOptimizationType(
@@ -490,7 +490,10 @@
   data->set_serialized_hint_version_string("123abc");
   data->set_has_hint_before_commit(true);
   data->set_has_hint_after_commit(true);
-  data->set_has_page_hint(false);
+  optimization_guide::proto::PageHint page_hint;
+  page_hint.set_page_pattern("pagepattern");
+  data->set_page_hint(
+      std::make_unique<optimization_guide::proto::PageHint>(page_hint));
 
   OptimizationGuideNavigationData data_copy(*data);
   EXPECT_EQ(3, data_copy.navigation_id());
@@ -503,5 +506,6 @@
   EXPECT_TRUE(data_copy.has_hint_before_commit().value());
   EXPECT_TRUE(data_copy.has_hint_after_commit().value());
   EXPECT_EQ("123abc", *(data_copy.serialized_hint_version_string()));
-  EXPECT_FALSE(data_copy.has_page_hint().value());
+  EXPECT_TRUE(data_copy.has_page_hint_value());
+  EXPECT_EQ("pagepattern", data_copy.page_hint()->page_pattern());
 }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index ab9047e..101e45a 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1092,20 +1092,23 @@
     browser_switcher::prefs::kDelay,
     base::Value::Type::INTEGER },
   { key::kUnsafeEventsReportingEnabled,
-    policy_prefs::kUnsafeEventsReportingEnabled,
+    prefs::kUnsafeEventsReportingEnabled,
     base::Value::Type::BOOLEAN },
   { key::kDelayDeliveryUntilVerdict,
-    policy_prefs::kDelayDeliveryUntilVerdict,
+    prefs::kDelayDeliveryUntilVerdict,
     base::Value::Type::INTEGER },
   { key::kBlockLargeFileTransfer,
-    policy_prefs::kBlockLargeFileTransfer,
+    prefs::kBlockLargeFileTransfer,
     base::Value::Type::INTEGER },
   { key::kAllowPasswordProtectedFiles,
-    policy_prefs::kAllowPasswordProtectedFiles,
+    prefs::kAllowPasswordProtectedFiles,
     base::Value::Type::INTEGER },
   { key::kCheckContentCompliance,
-    policy_prefs::kCheckContentCompliance,
+    prefs::kCheckContentCompliance,
     base::Value::Type::INTEGER },
+  { key::kDomainsToCheckComplianceOfDownloadedContent,
+    prefs::kDomainsToCheckComplianceOfDownloadedContent,
+    base::Value::Type::LIST },
 #endif
 #if defined(OS_WIN)
   { key::kBrowserSwitcherUseIeSitelist,
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index e8b8745..755d4698 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -40,6 +40,7 @@
 #include "content/public/common/referrer.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/web_contents_tester.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
@@ -491,10 +492,11 @@
 
   // Connect with the FakeUsbDeviceManager.
   device::FakeUsbDeviceManager device_manager;
-  device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-  device_manager.AddBinding(mojo::MakeRequest(&device_manager_ptr));
+  mojo::PendingRemote<device::mojom::UsbDeviceManager> pending_device_manager;
+  device_manager.AddReceiver(
+      pending_device_manager.InitWithNewPipeAndPassReceiver());
   UsbChooserContextFactory::GetForProfile(profile())
-      ->SetDeviceManagerForTesting(std::move(device_manager_ptr));
+      ->SetDeviceManagerForTesting(std::move(pending_device_manager));
 
   UsbTabHelper* usb_tab_helper =
       UsbTabHelper::GetOrCreateForWebContents(web_contents_);
diff --git a/chrome/browser/resources/print_preview/data/measurement_system.js b/chrome/browser/resources/print_preview/data/measurement_system.js
index 06ffb6f..c57e8a9 100644
--- a/chrome/browser/resources/print_preview/data/measurement_system.js
+++ b/chrome/browser/resources/print_preview/data/measurement_system.js
@@ -27,23 +27,23 @@
      * Measurement system of the print preview. Used to parse and serialize
      * point measurements into the system's local units (e.g. millimeters,
      * inches).
-     * @param {string} thousandsDelimeter Delimeter between thousands digits.
-     * @param {string} decimalDelimeter Delimeter between integers and decimals.
+     * @param {string} thousandsDelimiter Delimiter between thousands digits.
+     * @param {string} decimalDelimiter Delimiter between integers and decimals.
      * @param {!print_preview.MeasurementSystemUnitType} unitType Measurement
      *     unit type of the system.
      */
-    constructor(thousandsDelimeter, decimalDelimeter, unitType) {
+    constructor(thousandsDelimiter, decimalDelimiter, unitType) {
       /**
-       * The thousands delimeter to use when displaying numbers.
+       * The thousands delimiter to use when displaying numbers.
        * @private {string}
        */
-      this.thousandsDelimeter_ = thousandsDelimeter || ',';
+      this.thousandsDelimiter_ = thousandsDelimiter || ',';
 
       /**
-       * The decimal delimeter to use when displaying numbers.
+       * The decimal delimiter to use when displaying numbers.
        * @private {string}
        */
-      this.decimalDelimeter_ = decimalDelimeter || '.';
+      this.decimalDelimiter_ = decimalDelimiter || '.';
 
       assert(measurementSystemPrefs.has(unitType));
       /**
@@ -59,19 +59,19 @@
     }
 
     /**
-     * @return {string} The thousands delimeter character of the measurement
+     * @return {string} The thousands delimiter character of the measurement
      *     system.
      */
-    get thousandsDelimeter() {
-      return this.thousandsDelimeter_;
+    get thousandsDelimiter() {
+      return this.thousandsDelimiter_;
     }
 
     /**
-     * @return {string} The decimal delimeter character of the measurement
+     * @return {string} The decimal delimiter character of the measurement
      *     system.
      */
-    get decimalDelimeter() {
-      return this.decimalDelimeter_;
+    get decimalDelimiter() {
+      return this.decimalDelimiter_;
     }
 
     /**
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index fe78ee05..ddf522a 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -33,8 +33,8 @@
    *   isInKioskAutoPrintMode: boolean,
    *   isInAppKioskMode: boolean,
    *   uiLocale: string,
-   *   thousandsDelimeter: string,
-   *   decimalDelimeter: string,
+   *   thousandsDelimiter: string,
+   *   decimalDelimiter: string,
    *   unitType: !print_preview.MeasurementSystemUnitType,
    *   previewModifiable: boolean,
    *   documentTitle: string,
diff --git a/chrome/browser/resources/print_preview/ui/app.js b/chrome/browser/resources/print_preview/ui/app.js
index afdf54fe..8f30d4e 100644
--- a/chrome/browser/resources/print_preview/ui/app.js
+++ b/chrome/browser/resources/print_preview/ui/app.js
@@ -272,7 +272,7 @@
       this.$.model.setPolicySettings(
           settings.headerFooter, settings.isHeaderFooterManaged);
       this.measurementSystem_ = new print_preview.MeasurementSystem(
-          settings.thousandsDelimeter, settings.decimalDelimeter,
+          settings.thousandsDelimiter, settings.decimalDelimiter,
           settings.unitType);
       this.setSetting('selectionOnly', settings.shouldPrintSelectionOnly);
       this.$.sidebar.init(
diff --git a/chrome/browser/resources/print_preview/ui/margin_control.js b/chrome/browser/resources/print_preview/ui/margin_control.js
index 7fdb799..9a030e0 100644
--- a/chrome/browser/resources/print_preview/ui/margin_control.js
+++ b/chrome/browser/resources/print_preview/ui/margin_control.js
@@ -186,8 +186,8 @@
       return null;
     }
     assert(this.measurementSystem);
-    const decimal = this.measurementSystem.decimalDelimeter;
-    const thousands = this.measurementSystem.thousandsDelimeter;
+    const decimal = this.measurementSystem.decimalDelimiter;
+    const thousands = this.measurementSystem.thousandsDelimiter;
     const whole = `(?:0|[1-9]\\d*|[1-9]\\d{0,2}(?:[${thousands}]\\d{3})*)`;
     const fractional = `(?:[${decimal}]\\d*)`;
     const validationRegex =
@@ -214,7 +214,7 @@
     value = this.measurementSystem.roundValue(value);
     // Convert the dot symbol to the decimal delimiter for the locale.
     return value.toString().replace(
-        '.', this.measurementSystem.decimalDelimeter);
+        '.', this.measurementSystem.decimalDelimiter);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
index 07e1c06c..3b7d3b42 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
@@ -116,8 +116,6 @@
                link to settings.-->
             <a href="chrome://settings" target="_blank"
                 tabindex="-1" role="presentation">
-              <iron-icon id="openInNewBrowserSettingsIcon"
-                  icon="cr:open-in-new"></iron-icon>
             </a>
           </div>
           <cr-icon-button class="icon-clear"
diff --git a/chrome/browser/resources/settings/printing_page/BUILD.gn b/chrome/browser/resources/settings/printing_page/BUILD.gn
index 0f0acd7..e761995 100644
--- a/chrome/browser/resources/settings/printing_page/BUILD.gn
+++ b/chrome/browser/resources/settings/printing_page/BUILD.gn
@@ -103,6 +103,7 @@
       ":cups_nearby_printers",
       ":cups_printers_browser_proxy",
       ":cups_saved_printers",
+      "..:route",
       "//ui/webui/resources/cr_components/chromeos/network:mojo_interface_provider",
       "//ui/webui/resources/cr_elements/chromeos/network:cr_network_listener_behavior",
       "//ui/webui/resources/cr_elements/cr_toast:cr_toast",
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers.html b/chrome/browser/resources/settings/printing_page/cups_printers.html
index 346cb019..1ccaf14 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers.html
+++ b/chrome/browser/resources/settings/printing_page/cups_printers.html
@@ -19,6 +19,7 @@
 <link rel="import" href="cups_printers_list.html">
 <link rel="import" href="cups_saved_printers.html">
 <link rel="import" href="cups_nearby_printers.html">
+<link rel="import" href="../route.html">
 
 <dom-module id="settings-cups-printers">
   <template>
@@ -121,16 +122,21 @@
         </div>
       </template>
 
-      <div class="settings-box first">
-        <div class="start">
-          <span>$i18n{savedPrintersTitle}</span>
+      <template is="dom-if"
+          if="[[doesAccountHaveSavedPrinters_(savedPrinters_)]]"
+          id="savedPrintersSection" restamp>
+        <div class="settings-box first">
+          <div class="start">
+            <span>$i18n{savedPrintersTitle}</span>
+          </div>
         </div>
-      </div>
 
-      <settings-cups-saved-printers id="savedPrinters"
-          active-printer="{{activePrinter}}"
-          search-term="[[searchTerm]]">
-      </settings-cups-saved-printers>
+        <settings-cups-saved-printers id="savedPrinters"
+            active-printer="{{activePrinter}}"
+            saved-printers="[[savedPrinters_]]"
+            search-term="[[searchTerm]]">
+        </settings-cups-saved-printers>
+      </template>
 
       <div class="padded first" id="nearbyPrinters">
         <div>$i18n{nearbyPrintersListTitle}</div>
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers.js b/chrome/browser/resources/settings/printing_page/cups_printers.js
index fd3fcd83d..2110935 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers.js
+++ b/chrome/browser/resources/settings/printing_page/cups_printers.js
@@ -14,6 +14,7 @@
 
   behaviors: [
       CrNetworkListenerBehavior,
+      settings.RouteObserverBehavior,
       WebUIListenerBehavior,
   ],
 
@@ -42,6 +43,15 @@
       reflectToAttribute: true,
     },
 
+    /**
+     * @type {!Array<!PrinterListEntry>}
+     * @private
+     */
+    savedPrinters_: {
+      type: Array,
+      value: () => [],
+    },
+
     /** @private */
     showCupsEditPrinterDialog_: Boolean,
 
@@ -93,9 +103,6 @@
     if (this.enableUpdatedUi_) {
       return;
     }
-
-    this.addWebUIListener(
-        'on-printers-changed', this.printersChanged_.bind(this));
   },
 
   /** @override */
@@ -103,6 +110,21 @@
     this.updateCupsPrintersList_();
   },
 
+
+  /**
+   * settings.RouteObserverBehavior
+   * @param {!settings.Route} route
+   * @protected
+   */
+  currentRouteChanged: function(route) {
+    if (route != settings.routes.CUPS_PRINTERS) {
+      cr.removeWebUIListener('on-printers-changed');
+      return;
+    }
+    cr.addWebUIListener(
+        'on-printers-changed', this.onPrintersChanged_.bind(this));
+  },
+
   /**
    * CrosNetworkConfigObserver impl
    * @param {!Array<chromeos.networkConfig.mojom.NetworkStateProperties>}
@@ -130,21 +152,13 @@
     const printerName = event.detail.printerName;
     switch (event.detail.resultCode) {
       case PrinterSetupResult.SUCCESS:
-        if (this.enableUpdatedUi_) {
-          this.$$('#savedPrinters').updateSavedPrintersList();
-        } else {
-          this.updateCupsPrintersList_();
-        }
+        this.updateCupsPrintersList_();
         this.addPrinterResultText_ =
             loadTimeData.getStringF('printerAddedSuccessfulMessage',
                                     printerName);
         break;
       case PrinterSetupResult.EDIT_SUCCESS:
-        if (this.enableUpdatedUi_) {
-          this.$$('#savedPrinters').updateSavedPrintersList();
-        } else {
-          this.updateCupsPrintersList_();
-        }
+        this.updateCupsPrintersList_();
         this.addPrinterResultText_ =
             loadTimeData.getStringF('printerEditedSuccessfulMessage',
                                     printerName);
@@ -176,15 +190,22 @@
   updateCupsPrintersList_: function() {
     settings.CupsPrintersBrowserProxyImpl.getInstance()
         .getCupsPrintersList()
-        .then(this.printersChanged_.bind(this));
+        .then(this.onPrintersChanged_.bind(this));
   },
 
   /**
    * @param {!CupsPrintersList} cupsPrintersList
    * @private
    */
-  printersChanged_: function(cupsPrintersList) {
-    this.printers = cupsPrintersList.printerList;
+  onPrintersChanged_: function(cupsPrintersList) {
+    if (this.enableUpdatedUi_) {
+      this.savedPrinters_ = cupsPrintersList.printerList.map(
+          printer => /** @type {!PrinterListEntry} */({
+              printerInfo: printer,
+              printerType: PrinterType.SAVED}));
+    } else {
+      this.printers = cupsPrintersList.printerList;
+    }
   },
 
   /** @private */
@@ -235,5 +256,13 @@
   addPrinterButtonActive_: function(
       connectedToNetwork, userNativePrintersAllowed) {
     return connectedToNetwork && userNativePrintersAllowed;
+  },
+
+  /**
+   * @return {boolean} Whether |savedPrinters_| is empty.
+   * @private
+   */
+  doesAccountHaveSavedPrinters_: function() {
+    return !!this.savedPrinters_.length;
   }
 });
diff --git a/chrome/browser/resources/settings/printing_page/cups_saved_printers.html b/chrome/browser/resources/settings/printing_page/cups_saved_printers.html
index b3439925..1bb09c08 100644
--- a/chrome/browser/resources/settings/printing_page/cups_saved_printers.html
+++ b/chrome/browser/resources/settings/printing_page/cups_saved_printers.html
@@ -19,7 +19,7 @@
     </cr-action-menu>
 
     <style include="settings-shared"></style>
-    <settings-cups-printers-entry-list printers="[[savedPrinters_]]"
+    <settings-cups-printers-entry-list printers="[[savedPrinters]]"
         search-term="[[searchTerm]]">
     </settings-cups-printers-entry-list>
   </template>
diff --git a/chrome/browser/resources/settings/printing_page/cups_saved_printers.js b/chrome/browser/resources/settings/printing_page/cups_saved_printers.js
index a44b197..85ef694 100644
--- a/chrome/browser/resources/settings/printing_page/cups_saved_printers.js
+++ b/chrome/browser/resources/settings/printing_page/cups_saved_printers.js
@@ -14,17 +14,13 @@
   ],
 
   properties: {
-    /**
-     * @type {!Array<!PrinterListEntry>}
-     * @private
-     */
-    savedPrinters_: {
+    /** @type {!Array<!PrinterListEntry>} */
+    savedPrinters: {
       type: Array,
-      value: () => [],
     },
 
     /**
-     * Search term for filtering |savedPrinters_|.
+     * Search term for filtering |savedPrinters|.
      * @type {string}
      */
     searchTerm: {
@@ -60,35 +56,6 @@
     this.browserProxy_ = settings.CupsPrintersBrowserProxyImpl.getInstance();
   },
 
-  /** @override */
-  ready: function() {
-    this.addWebUIListener(
-        'on-printers-changed', this.printersChanged_.bind(this));
-    this.updateSavedPrintersList();
-  },
-
-  /** Public function to update the printer list. */
-  updateSavedPrintersList: function() {
-    settings.CupsPrintersBrowserProxyImpl.getInstance()
-        .getCupsPrintersList()
-        .then(this.printersChanged_.bind(this));
-  },
-
-  /**
-   * @param {!CupsPrintersList} cupsPrintersList
-   * @private
-   */
-  printersChanged_: function(cupsPrintersList) {
-    if (!cupsPrintersList) {
-      return;
-    }
-
-    this.savedPrinters_ = cupsPrintersList.printerList.map(
-        printer => /** @type {!PrinterListEntry} */({
-            printerInfo: printer,
-            printerType: PrinterType.SAVED}));
-  },
-
   /**
    * @param {!CustomEvent<{target: !HTMLElement, item: !PrinterListEntry}>} e
    * @private
@@ -96,10 +63,10 @@
   onOpenActionMenu_: function(e) {
     const item = /** @type {!PrinterListEntry} */(e.detail.item);
     this.activePrinterListEntryIndex_ =
-        this.savedPrinters_.findIndex(
+        this.savedPrinters.findIndex(
             printer => printer.printerInfo == item.printerInfo);
     this.activePrinter =
-        this.get(['savedPrinters_', this.activePrinterListEntryIndex_])
+        this.get(['savedPrinters', this.activePrinterListEntryIndex_])
         .printerInfo;
 
     const target = /** @type {!HTMLElement} */ (e.detail.target);
@@ -115,7 +82,6 @@
 
   /** @private */
   onRemoveTap_: function() {
-    this.splice('savedPrinters_', this.activePrinterListEntryIndex_, 1);
     this.browserProxy_.removeCupsPrinter(
         this.activePrinter.printerId, this.activePrinter.printerName);
     this.activePrinter = null;
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index 5fceb4a6..cc8319c 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -24,6 +24,9 @@
 #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
 #include "chrome/common/safe_browsing/download_type_util.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
+#include "components/policy/core/common/policy_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/common/utils.h"
 #include "components/safe_browsing/features.h"
 #include "components/safe_browsing/proto/csd.pb.h"
@@ -43,6 +46,18 @@
     const std::string& download_digest_sha256,
     BinaryUploadService::Result result,
     DeepScanningClientResponse response) {
+  if (result == BinaryUploadService::Result::FILE_TOO_LARGE) {
+    extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile)
+        ->OnLargeUnscannedFileEvent(url, file_name, download_digest_sha256);
+  }
+
+  if (result != BinaryUploadService::Result::SUCCESS)
+    return;
+
+  if (!g_browser_process->local_state()->GetBoolean(
+          prefs::kUnsafeEventsReportingEnabled))
+    return;
+
   if (response.malware_scan_verdict().verdict() ==
           MalwareDeepScanningVerdict::UWS ||
       response.malware_scan_verdict().verdict() ==
@@ -50,9 +65,18 @@
     extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile)
         ->OnDangerousDeepScanningResult(url, file_name, download_digest_sha256);
   }
+
+  if (response.dlp_scan_verdict().status() == DlpDeepScanningVerdict::SUCCESS) {
+    if (!response.dlp_scan_verdict().triggered_rules().empty()) {
+      extensions::SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile)
+          ->OnSensitiveDataEvent(response.dlp_scan_verdict(), url, file_name,
+                                 download_digest_sha256);
+    }
+  }
 }
 
 }  // namespace
+
 CheckClientDownloadRequest::CheckClientDownloadRequest(
     download::DownloadItem* item,
     CheckDownloadCallback callback,
@@ -203,34 +227,41 @@
       result, upload_requested, item_, request_data, response_body);
 }
 
-void CheckClientDownloadRequest::MaybeUploadBinary(DownloadCheckResult result,
-                                                   const std::string& token) {
-  if (ShouldUploadBinary(result)) {
-    content::BrowserContext* browser_context = GetBrowserContext();
-    if (browser_context) {
-      Profile* profile = Profile::FromBrowserContext(browser_context);
-      auto request = std::make_unique<DownloadItemRequest>(
-          item_, base::BindOnce(&MaybeReportDownloadDeepScanningVerdict,
-                                profile, item_->GetURL(),
-                                item_->GetTargetFilePath().AsUTF8Unsafe(),
-                                item_->GetHash()));
+void CheckClientDownloadRequest::MaybeUploadBinary(
+    DownloadCheckResultReason reason) {
+  bool upload_for_dlp = ShouldUploadForDlpScan();
+  bool upload_for_malware = ShouldUploadForMalwareScan(reason);
+  if (upload_for_dlp || upload_for_malware) {
+    Profile* profile = Profile::FromBrowserContext(GetBrowserContext());
+    if (!profile)
+      return;
 
+    auto request = std::make_unique<DownloadItemRequest>(
+        item_, base::BindOnce(&MaybeReportDownloadDeepScanningVerdict, profile,
+                              item_->GetURL(),
+                              item_->GetTargetFilePath().AsUTF8Unsafe(),
+                              item_->GetHash()));
+
+    if (upload_for_dlp) {
       DlpDeepScanningClientRequest dlp_request;
       dlp_request.set_content_source(
           DlpDeepScanningClientRequest::FILE_DOWNLOAD);
       request->set_request_dlp_scan(std::move(dlp_request));
+    }
 
+    if (upload_for_malware) {
       MalwareDeepScanningClientRequest malware_request;
       malware_request.set_population(
           MalwareDeepScanningClientRequest::POPULATION_ENTERPRISE);
-      malware_request.set_download_token(token);
+      malware_request.set_download_token(
+          DownloadProtectionService::GetDownloadPingToken(item_));
       request->set_request_malware_scan(std::move(malware_request));
-
-      request->set_dm_token(
-          policy::BrowserDMTokenStorage::Get()->RetrieveDMToken());
-
-      service()->UploadForDeepScanning(profile, std::move(request));
     }
+
+    request->set_dm_token(
+        policy::BrowserDMTokenStorage::Get()->RetrieveDMToken());
+
+    service()->UploadForDeepScanning(profile, std::move(request));
   }
 }
 
@@ -246,21 +277,61 @@
   item_->RemoveObserver(this);
 }
 
-bool CheckClientDownloadRequest::ShouldUploadBinary(
-    DownloadCheckResult result) {
-  if (!base::FeatureList::IsEnabled(kUploadForMalwareCheck))
+bool CheckClientDownloadRequest::ShouldUploadForDlpScan() {
+  int check_content_compliance = g_browser_process->local_state()->GetInteger(
+      prefs::kCheckContentCompliance);
+  if (check_content_compliance !=
+          CheckContentComplianceValues::CHECK_DOWNLOADS &&
+      check_content_compliance !=
+          CheckContentComplianceValues::CHECK_UPLOADS_AND_DOWNLOADS)
     return false;
 
+  // If there's no DM token, the upload will fail, so we can skip uploading now.
   if (policy::BrowserDMTokenStorage::Get()->RetrieveDMToken().empty())
     return false;
 
-  if (result != DownloadCheckResult::SAFE &&
-      result != DownloadCheckResult::UNCOMMON &&
-      result != DownloadCheckResult::UNKNOWN)
+  const base::ListValue* domains = g_browser_process->local_state()->GetList(
+      prefs::kDomainsToCheckComplianceOfDownloadedContent);
+  bool host_in_list =
+      std::any_of(domains->GetList().begin(), domains->GetList().end(),
+                  [this](const base::Value& domain) {
+                    return domain.is_string() &&
+                           domain.GetString() == item_->GetURL().host();
+                  });
+
+  return host_in_list;
+}
+
+bool CheckClientDownloadRequest::ShouldUploadForMalwareScan(
+    DownloadCheckResultReason reason) {
+  // If we know the file is malicious, we don't need to upload it.
+  if (reason != DownloadCheckResultReason::REASON_DOWNLOAD_SAFE &&
+      reason != DownloadCheckResultReason::REASON_DOWNLOAD_UNCOMMON &&
+      reason != DownloadCheckResultReason::REASON_VERDICT_UNKNOWN)
     return false;
 
-  // TODO(drubery): Add the appropriate checks against the enterprise policy
-  // here.
+  // This feature can be used to force uploads.
+  if (base::FeatureList::IsEnabled(kUploadForMalwareCheck))
+    return true;
+
+  content::BrowserContext* browser_context =
+      content::DownloadItemUtils::GetBrowserContext(item_);
+  if (!browser_context)
+    return false;
+
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  if (!profile)
+    return false;
+
+  int send_files_for_malware_check = profile->GetPrefs()->GetInteger(
+      prefs::kSafeBrowsingSendFilesForMalwareCheck);
+  if (send_files_for_malware_check !=
+      SendFilesForMalwareCheckValues::SEND_DOWNLOADS)
+    return false;
+
+  // If there's no DM token, the upload will fail, so we can skip uploading now.
+  if (policy::BrowserDMTokenStorage::Get()->RetrieveDMToken().empty())
+    return false;
 
   return true;
 }
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
index 893bf65d..3aa57f7 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
@@ -53,12 +53,12 @@
                                   bool upload_requested,
                                   const std::string& request_data,
                                   const std::string& response_body) override;
-  void MaybeUploadBinary(DownloadCheckResult result,
-                         const std::string& token) override;
+  void MaybeUploadBinary(DownloadCheckResultReason reason) override;
   void NotifyRequestFinished(DownloadCheckResult result,
                              DownloadCheckResultReason reason) override;
 
-  bool ShouldUploadBinary(DownloadCheckResult result);
+  bool ShouldUploadForDlpScan();
+  bool ShouldUploadForMalwareScan(DownloadCheckResultReason reason);
 
   // The DownloadItem we are checking. Will be NULL if the request has been
   // canceled. Must be accessed only on UI thread.
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
index 5823e01..649f457 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
@@ -198,6 +198,8 @@
   UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats", reason,
                             REASON_MAX);
 
+  MaybeUploadBinary(reason);
+
   std::move(callback_).Run(result);
   NotifyRequestFinished(result, reason);
   service()->RequestFinished(this);
@@ -694,8 +696,6 @@
                                *response_body.get());
   }
 
-  MaybeUploadBinary(result, token);
-
   // We don't need the loader anymore.
   loader_.reset();
   UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration",
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
index ac9798f..2abc42c 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
@@ -122,8 +122,7 @@
                                           const std::string& response_body) = 0;
 
   // Called after receiving, or failing to receive a response from the server.
-  virtual void MaybeUploadBinary(DownloadCheckResult result,
-                                 const std::string& token) = 0;
+  virtual void MaybeUploadBinary(DownloadCheckResultReason reason) = 0;
 
   // Called whenever a request has completed.
   virtual void NotifyRequestFinished(DownloadCheckResult result,
diff --git a/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.cc b/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.cc
index bcf3275a..40833c8 100644
--- a/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.cc
@@ -140,8 +140,7 @@
 }
 
 void CheckNativeFileSystemWriteRequest::MaybeUploadBinary(
-    DownloadCheckResult result,
-    const std::string& token) {}
+    DownloadCheckResultReason reason) {}
 
 void CheckNativeFileSystemWriteRequest::NotifyRequestFinished(
     DownloadCheckResult result,
diff --git a/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.h b/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.h
index a86abe9..3b031a8 100644
--- a/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.h
+++ b/chrome/browser/safe_browsing/download_protection/check_native_file_system_write_request.h
@@ -68,8 +68,7 @@
                                   bool upload_requested,
                                   const std::string& request_data,
                                   const std::string& response_body) override;
-  void MaybeUploadBinary(DownloadCheckResult result,
-                         const std::string& token) override;
+  void MaybeUploadBinary(DownloadCheckResultReason reason) override;
   void NotifyRequestFinished(DownloadCheckResult result,
                              DownloadCheckResultReason reason) override;
 
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
index ce59d84..75072ec 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
@@ -30,6 +30,7 @@
 #include "base/test/bind_test_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
@@ -50,6 +51,7 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
 #include "components/download/public/common/download_danger_type.h"
 #include "components/download/public/common/mock_download_item.h"
 #include "components/history/core/browser/history_service.h"
@@ -214,7 +216,8 @@
 
 class DownloadProtectionServiceTest : public ChromeRenderViewHostTestHarness {
  protected:
-  DownloadProtectionServiceTest() {}
+  DownloadProtectionServiceTest()
+      : testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {}
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
 
@@ -593,7 +596,7 @@
   void CheckClientDownloadReportCorruptArchive(ArchiveType type);
 
  protected:
-  // This will effectivly mask the global Singleton while this is in scope.
+  // This will effectively mask the global Singleton while this is in scope.
   FileTypePoliciesTestOverlay policies_;
 
   scoped_refptr<FakeSafeBrowsingService> sb_service_;
@@ -619,6 +622,7 @@
   std::string hash_;
   base::ScopedTempDir temp_dir_;
   extensions::TestEventRouter* test_event_router_;
+  TestingProfileManager testing_profile_manager_;
 };
 
 void DownloadProtectionServiceTest::CheckClientDownloadReportCorruptArchive(
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index 933d6f9..24c5485 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -882,62 +882,6 @@
   ASSERT_EQ(kDefaultCustomerID, pdm->GetPaymentsCustomerData()->customer_id);
 }
 
-// Tests that we do report age metric on startup.
-IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest,
-                       PRE_UseDateMetricReportedOnStartup) {
-  GetFakeServer()->SetWalletData(
-      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
-                            kDefaultBillingAddressID),
-       CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
-       CreateDefaultSyncPaymentsCustomerData()});
-  ASSERT_TRUE(SetupSync());
-
-  // Make sure the data is present on the client.
-  autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
-  ASSERT_EQ(1uL, pdm->GetCreditCards().size());
-  ASSERT_EQ(1uL, pdm->GetServerProfiles().size());
-  ASSERT_EQ(kDefaultCustomerID, pdm->GetPaymentsCustomerData()->customer_id);
-
-  // Here, we would ideally test that no metrics get recorded during initial
-  // sync. Due to design differences between USS&Directory, we cannot make it
-  // work (the metadata syncable service has no reliable way to tell it is
-  // initial sync).
-}
-
-IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest,
-                       UseDateMetricReportedOnStartup) {
-  // Advance the clock to get a reasonable value.
-  AdvanceAutofillClockByOneDay();
-
-  // Set the same data on the server so that we get an empty update (this is
-  // based on a hash of the data).
-  GetFakeServer()->SetWalletData(
-      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
-                            kDefaultBillingAddressID),
-       CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
-       CreateDefaultSyncPaymentsCustomerData()});
-  ASSERT_TRUE(SetupSync());
-
-  // Make sure the data is still present on the client.
-  autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
-  ASSERT_EQ(1uL, pdm->GetCreditCards().size());
-  ASSERT_EQ(1uL, pdm->GetServerProfiles().size());
-  ASSERT_EQ(kDefaultCustomerID, pdm->GetPaymentsCustomerData()->customer_id);
-
-  // The metric gets recorded.
-  histogram_tester_.ExpectTotalCount("Autofill.WalletUseDateInMinutes.Card", 1);
-  histogram_tester_.ExpectBucketCount(
-      "Autofill.WalletUseDateInMinutes.Card",
-      /*sample=*/base::TimeDelta::FromDays(1).InMinutes(),
-      /*count=*/1);
-  histogram_tester_.ExpectTotalCount("Autofill.WalletUseDateInMinutes.Address",
-                                     1);
-  histogram_tester_.ExpectBucketCount(
-      "Autofill.WalletUseDateInMinutes.Address",
-      /*sample=*/base::TimeDelta::FromDays(1).InMinutes(),
-      /*count=*/1);
-}
-
 // Wallet data should get cleared from the database when the wallet sync type
 // flag is disabled.
 // Test is flaky: https://crbug.com/997786
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index bead205..8dcfdc5 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2749,6 +2749,8 @@
       "views/frame/system_menu_model_delegate.h",
       "views/frame/tab_strip_region_view.cc",
       "views/frame/tab_strip_region_view.h",
+      "views/frame/terminal_system_app_menu_button_chromeos.cc",
+      "views/frame/terminal_system_app_menu_button_chromeos.h",
       "views/frame/toolbar_button_provider.h",
       "views/frame/top_container_view.cc",
       "views/frame/top_container_view.h",
@@ -3768,6 +3770,8 @@
       "extensions/installation_error_infobar_delegate.h",
       "extensions/settings_api_bubble_helpers.cc",
       "extensions/settings_api_bubble_helpers.h",
+      "extensions/terminal_system_app_menu_model_chromeos.cc",
+      "extensions/terminal_system_app_menu_model_chromeos.h",
       "views/extensions/browser_action_drag_data.cc",
       "views/extensions/browser_action_drag_data.h",
       "views/extensions/extension_action_platform_delegate_views.cc",
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon.cc b/chrome/browser/ui/app_list/arc/arc_app_icon.cc
index 8578f1e..541e2dd 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_icon.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_icon.cc
@@ -210,13 +210,13 @@
               << "x" << bitmap.height() << ". Expected " << expected_dim << ".";
       host_->MaybeRequestIcon(descriptor_.scale_factor);
     } else {
-      host_->Update(descriptor_.scale_factor,
-                    skia::ImageOperations::Resize(
-                        bitmap, skia::ImageOperations::RESIZE_BEST,
-                        expected_dim, expected_dim));
+      host_->UpdateUncompressed(descriptor_.scale_factor,
+                                skia::ImageOperations::Resize(
+                                    bitmap, skia::ImageOperations::RESIZE_BEST,
+                                    expected_dim, expected_dim));
     }
   } else {
-    host_->Update(descriptor_.scale_factor, bitmap);
+    host_->UpdateUncompressed(descriptor_.scale_factor, bitmap);
   }
 
   host_->DiscardDecodeRequest(this);
@@ -248,17 +248,22 @@
 ArcAppIcon::ArcAppIcon(content::BrowserContext* context,
                        const std::string& app_id,
                        int resource_size_in_dip,
-                       Observer* observer)
+                       Observer* observer,
+                       bool serve_compressed_icons)
     : context_(context),
       app_id_(app_id),
       mapped_app_id_(GetAppFromAppOrGroupId(context, app_id)),
       resource_size_in_dip_(resource_size_in_dip),
-      observer_(observer) {
+      observer_(observer),
+      serve_compressed_icons_(serve_compressed_icons) {
   CHECK(observer_ != nullptr);
-  auto source = std::make_unique<Source>(weak_ptr_factory_.GetWeakPtr(),
-                                         resource_size_in_dip);
-  gfx::Size resource_size(resource_size_in_dip, resource_size_in_dip);
-  image_skia_ = gfx::ImageSkia(std::move(source), resource_size);
+
+  if (!serve_compressed_icons_) {
+    auto source = std::make_unique<Source>(weak_ptr_factory_.GetWeakPtr(),
+                                           resource_size_in_dip);
+    gfx::Size resource_size(resource_size_in_dip, resource_size_in_dip);
+    image_skia_ = gfx::ImageSkia(std::move(source), resource_size);
+  }
 
   const std::vector<ui::ScaleFactor>& scale_factors =
       ui::GetSupportedScaleFactors();
@@ -268,6 +273,19 @@
 ArcAppIcon::~ArcAppIcon() {
 }
 
+void ArcAppIcon::LoadSupportedScaleFactors() {
+  if (serve_compressed_icons_) {
+    for (auto scale_factor : incomplete_scale_factors_)
+      LoadForScaleFactor(scale_factor);
+  } else {
+    // Calling GetRepresentation indirectly calls LoadForScaleFactor but also
+    // first initializes image_skia_ with the placeholder icons (e.g.
+    // IDR_APP_DEFAULT_ICON), via ArcAppIcon::Source::GetImageForScale.
+    for (auto scale_factor : incomplete_scale_factors_)
+      image_skia_.GetRepresentation(ui::GetScaleForScaleFactor(scale_factor));
+  }
+}
+
 bool ArcAppIcon::EverySupportedScaleFactorIsLoaded() const {
   return incomplete_scale_factors_.empty();
 }
@@ -360,6 +378,12 @@
     MaybeRequestIcon(read_result->scale_factor);
 
   if (!read_result->unsafe_icon_data.empty()) {
+    if (serve_compressed_icons_) {
+      UpdateCompressed(read_result->scale_factor,
+                       std::move(read_result->unsafe_icon_data));
+      return;
+    }
+
     decode_requests_.emplace_back(std::make_unique<DecodeRequest>(
         weak_ptr_factory_.GetWeakPtr(),
         ArcAppIconDescriptor(resource_size_in_dip_, read_result->scale_factor),
@@ -383,7 +407,8 @@
   }
 }
 
-void ArcAppIcon::Update(ui::ScaleFactor scale_factor, const SkBitmap& bitmap) {
+void ArcAppIcon::UpdateUncompressed(ui::ScaleFactor scale_factor,
+                                    const SkBitmap& bitmap) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   gfx::ImageSkiaRep image_rep(bitmap, ui::GetScaleForScaleFactor(scale_factor));
@@ -398,6 +423,14 @@
   observer_->OnIconUpdated(this);
 }
 
+void ArcAppIcon::UpdateCompressed(ui::ScaleFactor scale_factor,
+                                  std::string data) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  compressed_images_[scale_factor] = std::move(data);
+  incomplete_scale_factors_.erase(scale_factor);
+  observer_->OnIconUpdated(this);
+}
+
 void ArcAppIcon::DiscardDecodeRequest(DecodeRequest* request) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon.h b/chrome/browser/ui/app_list/arc/arc_app_icon.h
index f1329ff9..bd1fab0 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_icon.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_icon.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_H_
 #define CHROME_BROWSER_UI_APP_LIST_ARC_ARC_APP_ICON_H_
 
+#include <map>
 #include <memory>
 #include <set>
 #include <string>
@@ -51,15 +52,30 @@
   ArcAppIcon(content::BrowserContext* context,
              const std::string& app_id,
              int resource_size_in_dip,
-             Observer* observer);
+             Observer* observer,
+             bool serve_compressed_icons = false);
   ~ArcAppIcon();
 
+  // Starts loading the icon at every supported scale factor. The |observer_|
+  // will be notified as progress is made. "Supported" is in the same sense as
+  // ui::GetSupportedScaleFactors().
+  void LoadSupportedScaleFactors();
+
   // Whether every supported scale factor was successfully loaded. "Supported"
   // is in the same sense as ui::GetSupportedScaleFactors().
   bool EverySupportedScaleFactorIsLoaded() const;
 
   const std::string& app_id() const { return app_id_; }
-  const gfx::ImageSkia& image_skia() const { return image_skia_; }
+  // Valid if the |serve_compressed_icons_| is false.
+  const gfx::ImageSkia& image_skia() const {
+    DCHECK(!serve_compressed_icons_);
+    return image_skia_;
+  }
+  // Valid if the |serve_compressed_icons_| is true.
+  const std::map<ui::ScaleFactor, std::string>& compressed_images() const {
+    DCHECK(serve_compressed_icons_);
+    return compressed_images_;
+  }
 
   // Disables async safe decoding requests when unit tests are executed. This is
   // done to avoid two problems. Problems come because icons are decoded at a
@@ -105,7 +121,8 @@
       const base::FilePath& path,
       const base::FilePath& default_app_path);
   void OnIconRead(std::unique_ptr<ArcAppIcon::ReadResult> read_result);
-  void Update(ui::ScaleFactor scale_factor, const SkBitmap& bitmap);
+  void UpdateUncompressed(ui::ScaleFactor scale_factor, const SkBitmap& bitmap);
+  void UpdateCompressed(ui::ScaleFactor scale_factor, std::string data);
   void DiscardDecodeRequest(DecodeRequest* request);
 
   content::BrowserContext* const context_;
@@ -115,8 +132,10 @@
   const std::string mapped_app_id_;
   const int resource_size_in_dip_;
   Observer* const observer_;
+  const bool serve_compressed_icons_;
 
   gfx::ImageSkia image_skia_;
+  std::map<ui::ScaleFactor, std::string> compressed_images_;
   std::set<ui::ScaleFactor> incomplete_scale_factors_;
 
   // Contains pending image decode requests.
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
index 7514911..bde46f8 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_icon_loader.cc
@@ -36,7 +36,7 @@
   // |icon_size_in_dip_| differs from this size, re-scale is required.
   std::unique_ptr<ArcAppIcon> icon =
       std::make_unique<ArcAppIcon>(profile(), app_id, icon_size_in_dip(), this);
-  icon->image_skia().EnsureRepsForSupportedScales();
+  icon->LoadSupportedScaleFactors();
   icon_map_[app_id] = std::move(icon);
   UpdateImage(app_id);
 }
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
index d490db0..7daf9f8e 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
@@ -97,7 +97,7 @@
     bool sticky;
     // Whether notifications are enabled for the app.
     bool notifications_enabled;
-    // Whether app is ready.
+    // Whether app is ready. Disabled and removed apps are not ready.
     bool ready;
     // Whether app was suspended by policy. It may have or may not have ready
     // state.
@@ -145,11 +145,27 @@
     virtual void OnAppRegistered(const std::string& app_id,
                                  const AppInfo& app_info) {}
     // Notifies an observer that app states have been changed.
+    //
+    // State includes the the following AppInfo fields:
+    //  - sticky
+    //  - notifications_enabled
+    //  - ready
+    //  - suspended
+    //  - show_in_launcher
+    //  - launchable
+    //
+    // In practice, only ready and suspended change over time.
     virtual void OnAppStatesChanged(const std::string& id,
                                     const AppInfo& app_info) {}
     // Notifies an observer that app was removed.
     virtual void OnAppRemoved(const std::string& id) {}
-    // Notifies an observer that app icon has been installed or updated.
+    // Notifies an observer that app icon has been installed or updated:
+    // 1. When default apps are registered.
+    // 2. When the new icon has been installed:
+    //  - App appears for the first time and we fetch the icon from Android.
+    //  - App icon was invalid or non-readable and we re-fetch it from Android.
+    //  - App was updated and we re-fetch.
+    //  - Framework version changed (e.g. NYC -> PI) and we re-fetch.
     virtual void OnAppIconUpdated(const std::string& id,
                                   const ArcAppIconDescriptor& descriptor) {}
     // Notifies an observer that the name of an app has changed.
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider.cc b/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider.cc
index c14bc14..c4a9aa66 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider.h"
 
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/location.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/strings/stringprintf.h"
@@ -143,16 +144,124 @@
   }
 }
 
+// Returns: true if preprocessor config loaded, false if it could not be loaded.
+bool LoadExamplePreprocessorConfig(
+    assist_ranker::ExamplePreprocessorConfig* preprocessor_config) {
+  DCHECK(preprocessor_config);
+
+  const int resource_id = IDR_TOP_CAT_20190722_EXAMPLE_PREPROCESSOR_CONFIG_PB;
+  const scoped_refptr<base::RefCountedMemory> raw_config =
+      ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
+          resource_id);
+  if (!raw_config || !raw_config->front()) {
+    LOG(ERROR) << "Failed to load TopCatModel example preprocessor config.";
+    return false;
+  }
+
+  if (!preprocessor_config->ParseFromArray(raw_config->front(),
+                                           raw_config->size())) {
+    LOG(ERROR) << "Failed to parse TopCatModel example preprocessor config.";
+    return false;
+  }
+  return true;
+}
+
+// Perform the inference given the |features| and |app_id| of an app.
+// Posts |callback| to |task_runner| to perform the actual inference.
+void DoInference(
+    const std::string& app_id,
+    const std::vector<float>& features,
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    const base::RepeatingCallback<void(std::map<std::string, TensorPtr> inputs,
+                                       const std::vector<std::string> outputs,
+                                       const std::string app_id)> callback) {
+  // Prepare the input tensor.
+  std::map<std::string, TensorPtr> inputs;
+  auto tensor = Tensor::New();
+  tensor->shape = Int64List::New();
+  tensor->shape->value = std::vector<int64_t>({1, features.size()});
+  tensor->data = ValueList::New();
+  tensor->data->set_float_list(FloatList::New());
+  tensor->data->get_float_list()->value =
+      std::vector<double>(std::begin(features), std::end(features));
+  inputs.emplace(std::string("input"), std::move(tensor));
+
+  const std::vector<std::string> outputs({std::string("output")});
+  DCHECK(task_runner);
+  task_runner->PostTask(FROM_HERE, base::BindOnce(callback, std::move(inputs),
+                                                  std::move(outputs), app_id));
+}
+
+// Process the RankerExample to vectorize the feature list for inference.
+// Returns true on success.
+bool RankerExampleToVectorizedFeatures(
+    const assist_ranker::ExamplePreprocessorConfig& preprocessor_config,
+    assist_ranker::RankerExample& example,
+    std::vector<float>* vectorized_features) {
+  int preprocessor_error = assist_ranker::ExamplePreprocessor::Process(
+      preprocessor_config, &example, true);
+  // kNoFeatureIndexFound can occur normally (e.g., when the app URL
+  // isn't known to the model or a rarely seen enum value is used).
+  if (preprocessor_error != assist_ranker::ExamplePreprocessor::kSuccess &&
+      preprocessor_error !=
+          assist_ranker::ExamplePreprocessor::kNoFeatureIndexFound) {
+    // TODO: Log to UMA.
+    return false;
+  }
+
+  const auto& extracted_features =
+      example.features()
+          .at(assist_ranker::ExamplePreprocessor::kVectorizedFeatureDefaultName)
+          .float_list()
+          .float_value();
+  vectorized_features->assign(extracted_features.begin(),
+                              extracted_features.end());
+  return true;
+}
+
+// Does the CPU-intensive part of CreateRankings (preparing the Tensor inputs
+// from |app_features_map|, intended to be called on a low-priority
+// background thread. Invokes |callback| on |task_runner| once for each app in
+// |app_features_map|.
+void CreateRankingsImpl(
+    base::flat_map<std::string, AppLaunchFeatures> app_features_map,
+    int total_hours,
+    int all_clicks_last_hour,
+    int all_clicks_last_24_hours,
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    const base::RepeatingCallback<void(std::map<std::string, TensorPtr> inputs,
+                                       const std::vector<std::string> outputs,
+                                       const std::string app_id)>& callback) {
+  const base::Time now(base::Time::Now());
+  const int hour = HourOfDay(now);
+  const int day = DayOfWeek(now);
+
+  assist_ranker::ExamplePreprocessorConfig preprocessor_config;
+  if (!LoadExamplePreprocessorConfig(&preprocessor_config)) {
+    return;
+  }
+  for (auto& app : app_features_map) {
+    assist_ranker::RankerExample example(
+        CreateRankerExample(app.second,
+                            now.ToDeltaSinceWindowsEpoch().InSeconds() -
+                                app.second.time_of_last_click_sec(),
+                            total_hours, day, hour, all_clicks_last_hour,
+                            all_clicks_last_24_hours));
+    std::vector<float> vectorized_features;
+    if (RankerExampleToVectorizedFeatures(preprocessor_config, example,
+                                          &vectorized_features)) {
+      DoInference(app.first, vectorized_features, task_runner, callback);
+    }
+  }
+}
+
 }  // namespace
 
 MlAppRankProvider::MlAppRankProvider()
     : creation_task_runner_(base::SequencedTaskRunnerHandle::Get()),
       background_task_runner_(base::CreateSequencedTaskRunner(
           {base::ThreadPool(), base::TaskPriority::BEST_EFFORT,
-           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {
-  // Constructor is not required to run on |background_task_runner_|:
-  DETACH_FROM_SEQUENCE(background_sequence_checker_);
-}
+           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
 
 MlAppRankProvider::~MlAppRankProvider() = default;
 
@@ -166,9 +275,11 @@
   // sequence.
   background_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&MlAppRankProvider::CreateRankingsImpl,
-                     base::Unretained(this), app_features_map, total_hours,
-                     all_clicks_last_hour, all_clicks_last_24_hours));
+      base::BindOnce(&CreateRankingsImpl, app_features_map, total_hours,
+                     all_clicks_last_hour, all_clicks_last_24_hours,
+                     creation_task_runner_,
+                     base::BindRepeating(&MlAppRankProvider::RunExecutor,
+                                         weak_factory_.GetWeakPtr())));
 }
 
 std::map<std::string, float> MlAppRankProvider::RetrieveRankings() {
@@ -176,51 +287,6 @@
   return ranking_map_;
 }
 
-void MlAppRankProvider::CreateRankingsImpl(
-    base::flat_map<std::string, AppLaunchFeatures> app_features_map,
-    int total_hours,
-    int all_clicks_last_hour,
-    int all_clicks_last_24_hours) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(background_sequence_checker_);
-  const base::Time now(base::Time::Now());
-  const int hour = HourOfDay(now);
-  const int day = DayOfWeek(now);
-
-  for (auto& app : app_features_map) {
-    assist_ranker::RankerExample example(
-        CreateRankerExample(app.second,
-                            now.ToDeltaSinceWindowsEpoch().InSeconds() -
-                                app.second.time_of_last_click_sec(),
-                            total_hours, day, hour, all_clicks_last_hour,
-                            all_clicks_last_24_hours));
-    std::vector<float> vectorized_features;
-    if (RankerExampleToVectorizedFeatures(example, &vectorized_features)) {
-      DoInference(app.first, vectorized_features);
-    }
-  }
-}
-
-void MlAppRankProvider::DoInference(const std::string& app_id,
-                                    const std::vector<float>& features) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(background_sequence_checker_);
-  // Prepare the input tensor.
-  std::map<std::string, TensorPtr> inputs;
-  auto tensor = Tensor::New();
-  tensor->shape = Int64List::New();
-  tensor->shape->value = std::vector<int64_t>({1, features.size()});
-  tensor->data = ValueList::New();
-  tensor->data->set_float_list(FloatList::New());
-  tensor->data->get_float_list()->value =
-      std::vector<double>(std::begin(features), std::end(features));
-  inputs.emplace(std::string("input"), std::move(tensor));
-
-  const std::vector<std::string> outputs({std::string("output")});
-  creation_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&MlAppRankProvider::RunExecutor,
-                                weak_factory_.GetWeakPtr(), std::move(inputs),
-                                std::move(outputs), app_id));
-}
-
 void MlAppRankProvider::RunExecutor(std::map<std::string, TensorPtr> inputs,
                                     const std::vector<std::string> outputs,
                                     const std::string app_id) {
@@ -268,55 +334,4 @@
   model_.reset();
 }
 
-bool MlAppRankProvider::RankerExampleToVectorizedFeatures(
-    assist_ranker::RankerExample& example,
-    std::vector<float>* vectorized_features) {
-  if (!LoadExamplePreprocessorConfigIfNeeded()) {
-    return false;
-  }
-  DCHECK(preprocessor_config_);
-  int preprocessor_error = assist_ranker::ExamplePreprocessor::Process(
-      *preprocessor_config_, &example, true);
-  // kNoFeatureIndexFound can occur normally (e.g., when the app URL
-  // isn't known to the model or a rarely seen enum value is used).
-  if (preprocessor_error != assist_ranker::ExamplePreprocessor::kSuccess &&
-      preprocessor_error !=
-          assist_ranker::ExamplePreprocessor::kNoFeatureIndexFound) {
-    // TODO: Log to UMA.
-    return false;
-  }
-
-  const auto& extracted_features =
-      example.features()
-          .at(assist_ranker::ExamplePreprocessor::kVectorizedFeatureDefaultName)
-          .float_list()
-          .float_value();
-  vectorized_features->assign(extracted_features.begin(),
-                              extracted_features.end());
-  return true;
-}
-
-bool MlAppRankProvider::LoadExamplePreprocessorConfigIfNeeded() {
-  if (preprocessor_config_) {
-    return true;
-  }
-  const int resource_id = IDR_TOP_CAT_20190722_EXAMPLE_PREPROCESSOR_CONFIG_PB;
-  const scoped_refptr<base::RefCountedMemory> raw_config =
-      ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
-          resource_id);
-  if (!raw_config || !raw_config->front()) {
-    LOG(ERROR) << "Failed to load TopCatModel example preprocessor config.";
-    return false;
-  }
-
-  preprocessor_config_ =
-      std::make_unique<assist_ranker::ExamplePreprocessorConfig>();
-  if (!preprocessor_config_->ParseFromArray(raw_config->front(),
-                                            raw_config->size())) {
-    LOG(ERROR) << "Failed to parse TopCatModel example preprocessor config.";
-    return false;
-  }
-  return true;
-}
-
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider.h b/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider.h
index 7ddcd4a..8b6983d 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider.h
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider.h
@@ -48,24 +48,12 @@
   std::map<std::string, float> RetrieveRankings();
 
  private:
-  // Does the CPU-intensive part of CreateRankings (perparing the Tensor inputs
-  // from |app_features_map|, intended to be called on a low-priority
-  // background thread.
-  void CreateRankingsImpl(
-      base::flat_map<std::string, AppLaunchFeatures> app_features_map,
-      int total_hours,
-      int all_clicks_last_hour,
-      int all_clicks_last_24_hours);
-
-  void DoInference(const std::string& app_id,
-                   const std::vector<float>& features);
-
   // Execute the |executor_| on the creation thread.
   void RunExecutor(
       std::map<std::string, ::chromeos::machine_learning::mojom::TensorPtr>
           inputs,
-      const std::vector<std::string> outputs,
-      const std::string app_id);
+      std::vector<std::string> outputs,
+      std::string app_id);
 
   // Stores the ranking score for an |app_id| in the |ranking_map_|.
   // Executed by the ML Service when an Execute call is complete.
@@ -81,24 +69,10 @@
 
   void OnConnectionError();
 
-  // Process the RankerExample to vectorize the feature list for inference.
-  // Returns true on success.
-  bool RankerExampleToVectorizedFeatures(
-      assist_ranker::RankerExample& example,
-      std::vector<float>* vectorized_features);
-
-  // Loads the preprocessor config if it is not already loaded.
-  // Returns: true if preprocessor config is loaded, false if
-  // it could not be loaded.
-  bool LoadExamplePreprocessorConfigIfNeeded();
-
   // Remotes used to execute functions in the ML service server end.
   mojo::Remote<::chromeos::machine_learning::mojom::Model> model_;
   mojo::Remote<::chromeos::machine_learning::mojom::GraphExecutor> executor_;
 
-  std::unique_ptr<assist_ranker::ExamplePreprocessorConfig>
-      preprocessor_config_;
-
   // Map from app id to ranking score.
   std::map<std::string, float> ranking_map_;
 
@@ -111,9 +85,6 @@
   // Sequence checker for methods that must run on the creation sequence.
   SEQUENCE_CHECKER(creation_sequence_checker_);
 
-  // Sequence checker for background_task_runner_.
-  SEQUENCE_CHECKER(background_sequence_checker_);
-
   base::WeakPtrFactory<MlAppRankProvider> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(MlAppRankProvider);
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider_unittest.cc
index 2187c6b8..61cc1ff0 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/ml_app_rank_provider_unittest.cc
@@ -54,4 +54,31 @@
   EXPECT_NEAR(expected_value, it->second, 0.001);
 }
 
+TEST(MlAppRankProviderTest, ExecutionAfterDestructorTest) {
+  base::test::TaskEnvironment task_environment_;
+
+  chromeos::machine_learning::FakeServiceConnectionImpl fake_service_connection;
+
+  const double expected_value = 1.234;
+  fake_service_connection.SetOutputValue(std::vector<int64_t>{1L},
+                                         std::vector<double>{expected_value});
+
+  chromeos::machine_learning::ServiceConnection::
+      UseFakeServiceConnectionForTesting(&fake_service_connection);
+
+  {
+    MlAppRankProvider ml_app_rank_provider;
+
+    base::flat_map<std::string, AppLaunchFeatures> app_features_map;
+    AppLaunchFeatures features;
+    features.set_app_id(kAppId);
+    features.set_app_type(AppLaunchEvent_AppType_CHROME);
+    app_features_map[kAppId] = features;
+    ml_app_rank_provider.CreateRankings(app_features_map, 3, 1, 7);
+  }
+  // Run the background tasks after ml_app_rank_provider has been destroyed.
+  // If this does not crash it is a success.
+  task_environment_.RunUntilIdle();
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
index 1a1fa962..9eb1523 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
@@ -243,24 +243,25 @@
 
   app_launch_event_logger_ = std::make_unique<app_list::AppLaunchEventLogger>();
 
-  if (app_list_features::IsAppRankerEnabled()) {
-    if (GetFieldTrialParamByFeatureAsBool(app_list_features::kEnableAppRanker,
-                                          "use_recurrence_ranker", true)) {
-      RecurrenceRankerConfigProto config;
-      config.set_min_seconds_between_saves(240u);
-      config.set_condition_limit(1u);
-      config.set_condition_decay(0.5);
-      config.set_target_limit(200);
-      config.set_target_decay(0.8);
-      config.mutable_predictor()->mutable_default_predictor();
+  bool apps_enabled = app_list_features::IsAppRankerEnabled();
+  if (app_list_features::IsAggregatedMlAppRankingEnabled() ||
+      (apps_enabled &&
+       GetFieldTrialParamByFeatureAsBool(app_list_features::kEnableAppRanker,
+                                         "use_topcat_ranker", false))) {
+    using_aggregated_app_inference_ = true;
+    app_launch_event_logger_->CreateRankings();
+  } else if (apps_enabled) {
+    RecurrenceRankerConfigProto config;
+    config.set_min_seconds_between_saves(240u);
+    config.set_condition_limit(1u);
+    config.set_condition_decay(0.5);
+    config.set_target_limit(200);
+    config.set_target_decay(0.8);
+    config.mutable_predictor()->mutable_default_predictor();
 
-      app_ranker_ = std::make_unique<RecurrenceRanker>(
-          "AppRanker", profile_->GetPath().AppendASCII("app_ranker.pb"), config,
-          chromeos::ProfileHelper::IsEphemeralUserProfile(profile_));
-    } else {
-      using_aggregated_app_inference_ = true;
-      app_launch_event_logger_->CreateRankings();
-    }
+    app_ranker_ = std::make_unique<RecurrenceRanker>(
+        "AppRanker", profile_->GetPath().AppendASCII("app_ranker.pb"), config,
+        chromeos::ProfileHelper::IsEphemeralUserProfile(profile_));
   }
 }
 
diff --git a/chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.cc b/chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.cc
new file mode 100644
index 0000000..5b8f17c
--- /dev/null
+++ b/chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.cc
@@ -0,0 +1,79 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.h"
+
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/no_destructor.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "url/gurl.h"
+#include "url/third_party/mozilla/url_parse.h"
+#include "url/url_canon.h"
+
+namespace {
+
+static const base::NoDestructor<base::flat_map<int, std::string>> g_commands({
+    // Opens settings page.
+    {IDC_OPTIONS, "options"},
+    // Split the currently selected pane vertically.
+    {IDC_TERMINAL_SPLIT_VERTICAL, "splitv"},
+    // Split the currently selected pane horizontally.
+    {IDC_TERMINAL_SPLIT_HORIZONTAL, "splith"},
+    // Open the find dialog.
+    {IDC_FIND, "find"},
+});
+
+}  // namespace
+
+TerminalSystemAppMenuModel::TerminalSystemAppMenuModel(
+    ui::AcceleratorProvider* provider,
+    Browser* browser)
+    : AppMenuModel(provider, browser) {}
+
+TerminalSystemAppMenuModel::~TerminalSystemAppMenuModel() {}
+
+void TerminalSystemAppMenuModel::Build() {
+  AddItemWithStringId(IDC_OPTIONS, IDS_OPTIONS);
+  AddItemWithStringId(IDC_TERMINAL_SPLIT_VERTICAL,
+                      IDS_APP_TERMINAL_SPLIT_VERTICAL);
+  AddItemWithStringId(IDC_TERMINAL_SPLIT_HORIZONTAL,
+                      IDS_APP_TERMINAL_SPLIT_HORIZONTAL);
+  AddItemWithStringId(IDC_FIND, IDS_FIND);
+}
+
+bool TerminalSystemAppMenuModel::IsCommandIdEnabled(int command_id) const {
+  return true;
+}
+
+void TerminalSystemAppMenuModel::ExecuteCommand(int command_id,
+                                                int event_flags) {
+  auto it = g_commands->find(command_id);
+  if (it == g_commands->end()) {
+    NOTREACHED() << "Unknown command " << command_id;
+    return;
+  }
+  std::string fragment = it->second;
+  url::Replacements<char> replacements;
+  replacements.SetRef(fragment.c_str(), url::Component(0, fragment.size()));
+  NavigateParams params(
+      browser(),
+      browser()->app_controller()->GetAppLaunchURL().ReplaceComponents(
+          replacements),
+      ui::PAGE_TRANSITION_FROM_API);
+  Navigate(&params);
+}
+
+void TerminalSystemAppMenuModel::LogMenuAction(AppMenuAction action_id) {
+  UMA_HISTOGRAM_ENUMERATION("TerminalSystemAppFrame.WrenchMenu.MenuAction",
+                            action_id, LIMIT_MENU_ACTION);
+}
diff --git a/chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.h b/chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.h
new file mode 100644
index 0000000..97da7a5
--- /dev/null
+++ b/chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.h
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_EXTENSIONS_TERMINAL_SYSTEM_APP_MENU_MODEL_CHROMEOS_H_
+#define CHROME_BROWSER_UI_EXTENSIONS_TERMINAL_SYSTEM_APP_MENU_MODEL_CHROMEOS_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/toolbar/app_menu_model.h"
+
+// Menu model for the Terminal System App menu button.
+class TerminalSystemAppMenuModel : public AppMenuModel {
+ public:
+  TerminalSystemAppMenuModel(ui::AcceleratorProvider* provider,
+                             Browser* browser);
+  ~TerminalSystemAppMenuModel() override;
+
+ private:
+  // AppMenuModel:
+  void Build() override;
+  bool IsCommandIdEnabled(int command_id) const override;
+  void ExecuteCommand(int command_id, int event_flags) override;
+  void LogMenuAction(AppMenuAction action_id) override;
+
+  DISALLOW_COPY_AND_ASSIGN(TerminalSystemAppMenuModel);
+};
+
+#endif  // CHROME_BROWSER_UI_EXTENSIONS_TERMINAL_SYSTEM_APP_MENU_MODEL_CHROMEOS_H_
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk.cc b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
index b77b8a53..80542c2c 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk.cc
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
@@ -346,6 +346,13 @@
   g_type_class_unref(g_type_class_ref(gtk_tree_view_get_type()));
   g_type_class_unref(g_type_class_ref(gtk_window_get_type()));
 
+  // Add the web native theme as an observer to stay in sync with dark mode,
+  // high contrast, and preferred color scheme changes.
+  color_scheme_observer_ =
+      std::make_unique<NativeTheme::ColorSchemeNativeThemeObserver>(
+          NativeTheme::GetInstanceForWeb());
+  AddObserver(color_scheme_observer_.get());
+
   OnThemeChanged(gtk_settings_get_default(), nullptr);
 }
 
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk.h b/chrome/browser/ui/libgtkui/native_theme_gtk.h
index 302fbe8..e5bb437 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk.h
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk.h
@@ -86,6 +86,11 @@
 
   ScopedCssProvider theme_css_override_;
 
+  // Used to notify the web native theme of changes to dark mode, high
+  // contrast, and preferred color scheme.
+  std::unique_ptr<NativeTheme::ColorSchemeNativeThemeObserver>
+      color_scheme_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(NativeThemeGtk);
 };
 
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index 3d3aa2b..efce4e773 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -35,6 +35,7 @@
 #include "content/public/browser/ssl_host_state_delegate.h"
 #include "content/public/browser/ssl_status.h"
 #include "content/public/common/content_switches.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/cert/cert_status_flags.h"
 #include "net/cert/x509_certificate.h"
 #include "net/ssl/ssl_connection_status_flags.h"
@@ -408,10 +409,11 @@
 TEST_F(PageInfoTest, OnChosenObjectDeleted) {
   // Connect the UsbChooserContext with FakeUsbDeviceManager.
   device::FakeUsbDeviceManager usb_device_manager;
-  device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-  usb_device_manager.AddBinding(mojo::MakeRequest(&device_manager_ptr));
+  mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
+  usb_device_manager.AddReceiver(
+      device_manager.InitWithNewPipeAndPassReceiver());
   UsbChooserContext* store = UsbChooserContextFactory::GetForProfile(profile());
-  store->SetDeviceManagerForTesting(std::move(device_manager_ptr));
+  store->SetDeviceManagerForTesting(std::move(device_manager));
 
   auto device_info = usb_device_manager.CreateAndAddDevice(
       0, 0, "Google", "Gizmo", "1234567890");
diff --git a/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc b/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc
index 54c20d378..089f353 100644
--- a/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc
+++ b/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc
@@ -10,11 +10,14 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/render_widget_host_view.h"
 
 class AuraLinuxAccessibilityInProcessBrowserTest : public InProcessBrowserTest {
  protected:
   AuraLinuxAccessibilityInProcessBrowserTest() {}
 
+  void VerifyEmbedRelationships();
+
  private:
   DISALLOW_COPY_AND_ASSIGN(AuraLinuxAccessibilityInProcessBrowserTest);
 };
@@ -48,8 +51,7 @@
   return nullptr;
 }
 
-IN_PROC_BROWSER_TEST_F(AuraLinuxAccessibilityInProcessBrowserTest,
-                       EmbeddedRelationship) {
+void AuraLinuxAccessibilityInProcessBrowserTest::VerifyEmbedRelationships() {
   AtkObject* native_view_accessible =
       static_cast<BrowserView*>(browser()->window())->GetNativeViewAccessible();
   EXPECT_NE(nullptr, native_view_accessible);
@@ -69,8 +71,14 @@
   EXPECT_EQ(1u, targets->len);
 
   AtkObject* target = static_cast<AtkObject*>(g_ptr_array_index(targets, 0));
+
   EXPECT_NE(nullptr, target);
 
+  content::WebContents* active_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_EQ(target, active_web_contents->GetRenderWidgetHostView()
+                        ->GetNativeViewAccessible());
+
   g_object_unref(relations);
 
   relations = atk_object_ref_relation_set(target);
@@ -89,3 +97,23 @@
 
   g_object_unref(relations);
 }
+
+IN_PROC_BROWSER_TEST_F(AuraLinuxAccessibilityInProcessBrowserTest,
+                       EmbeddedRelationship) {
+  GURL url(url::kAboutBlankURL);
+  AddTabAtIndex(0, url, ui::PAGE_TRANSITION_LINK);
+  EXPECT_EQ(2, browser()->tab_strip_model()->count());
+  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
+
+  VerifyEmbedRelationships();
+
+  browser()->tab_strip_model()->ActivateTabAt(1);
+  EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
+
+  VerifyEmbedRelationships();
+
+  browser()->tab_strip_model()->ActivateTabAt(0);
+  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
+
+  VerifyEmbedRelationships();
+}
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_button.cc b/chrome/browser/ui/views/extensions/extensions_menu_button.cc
index f4d10b29..074a158c 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_button.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_button.cc
@@ -51,11 +51,9 @@
                   true,
                   true),
       browser_(browser),
+      parent_(parent),
       controller_(controller),
-      model_(ToolbarActionsModel::Get(browser_->profile())),
-      context_menu_controller_(nullptr, controller_) {
-  set_context_menu_controller(&context_menu_controller_);
-
+      model_(ToolbarActionsModel::Get(browser_->profile())) {
   // Set so the extension button receives enter/exit on children to retain hover
   // status when hovering child views.
   set_notify_enter_exit_on_child(true);
@@ -150,7 +148,7 @@
 }
 
 bool ExtensionsMenuButton::IsMenuRunning() const {
-  return context_menu_controller_.IsMenuRunning();
+  return parent_->IsContextMenuRunning();
 }
 
 void ExtensionsMenuButton::ConfigureSecondaryView() {
@@ -170,7 +168,6 @@
 
   pin_button_ = pin_button.get();
   SetSecondaryButtonHighlightPath(pin_button_);
-  UpdatePinButton();
   container->AddChildView(std::move(pin_button));
 }
 
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_button.h b/chrome/browser/ui/views/extensions/extensions_menu_button.h
index 3e27ee53..f0a7043 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_button.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_button.h
@@ -61,6 +61,9 @@
 
   Browser* const browser_;
 
+  // The container containing this view.
+  ExtensionsMenuItemView* const parent_;
+
   // Responsible for executing the extension's actions.
   ToolbarActionViewController* const controller_;
 
@@ -68,10 +71,6 @@
 
   views::ImageButton* pin_button_ = nullptr;
 
-  // This controller is responsible for showing the context menu for an
-  // extension.
-  ExtensionContextMenuController context_menu_controller_;
-
   DISALLOW_COPY_AND_ASSIGN(ExtensionsMenuButton);
 };
 
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
index e1e31a32..c752760 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
@@ -73,6 +73,8 @@
 
   context_menu_button_ = context_menu_button.get();
   AddChildView(std::move(context_menu_button));
+
+  UpdatePinButton();
 }
 
 ExtensionsMenuItemView::~ExtensionsMenuItemView() = default;
@@ -91,6 +93,10 @@
   primary_action_button_->UpdatePinButton();
 }
 
+bool ExtensionsMenuItemView::IsContextMenuRunning() {
+  return context_menu_controller_->IsMenuRunning();
+}
+
 ExtensionsMenuButton*
 ExtensionsMenuItemView::primary_action_button_for_testing() {
   return primary_action_button_;
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_item_view.h b/chrome/browser/ui/views/extensions/extensions_menu_item_view.h
index 6f59008..b20a913 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_item_view.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_item_view.h
@@ -41,6 +41,8 @@
 
   void UpdatePinButton();
 
+  bool IsContextMenuRunning();
+
   ExtensionsMenuButton* primary_action_button_for_testing();
 
  private:
@@ -50,6 +52,8 @@
 
   views::MenuButton* context_menu_button_ = nullptr;
 
+  // This controller is responsible for showing the context menu for an
+  // extension.
   std::unique_ptr<ExtensionContextMenuController> context_menu_controller_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionsMenuItemView);
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.cc b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
index 0fd9167..5faaa80 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.cc
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
@@ -38,6 +38,10 @@
 #include "ui/views/window/custom_frame_view.h"
 #include "ui/views/window/hit_test_utils.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/ui/views/frame/terminal_system_app_menu_button_chromeos.h"
+#endif
+
 namespace {
 
 bool g_animation_disabled_for_testing = false;
@@ -243,8 +247,19 @@
   views::SetHitTestComponent(browser_actions_container_,
                              static_cast<int>(HTCLIENT));
 
+// TODO(crbug.com/998900): Create AppControllerUi class to contain this logic.
+#if defined(OS_CHROMEOS)
+  if (app_controller->UseTitlebarTerminalSystemAppMenu()) {
+    app_menu_button_ = AddChildView(
+        std::make_unique<TerminalSystemAppMenuButton>(browser_view));
+  } else {
+    app_menu_button_ =
+        AddChildView(std::make_unique<HostedAppMenuButton>(browser_view));
+  }
+#else
   app_menu_button_ =
       AddChildView(std::make_unique<HostedAppMenuButton>(browser_view));
+#endif
 
   UpdateChildrenColor();
   UpdateStatusIconsVisibility();
diff --git a/chrome/browser/ui/views/frame/hosted_app_menu_button.h b/chrome/browser/ui/views/frame/hosted_app_menu_button.h
index a8d79c9a..cbfd912 100644
--- a/chrome/browser/ui/views/frame/hosted_app_menu_button.h
+++ b/chrome/browser/ui/views/frame/hosted_app_menu_button.h
@@ -34,6 +34,9 @@
   // AppMenuButton:
   SkColor GetInkDropBaseColor() const override;
 
+ protected:
+  BrowserView* browser_view() { return browser_view_; }
+
  private:
   void FadeHighlightOff();
 
diff --git a/chrome/browser/ui/views/frame/terminal_system_app_menu_button_chromeos.cc b/chrome/browser/ui/views/frame/terminal_system_app_menu_button_chromeos.cc
new file mode 100644
index 0000000..87cdfe0
--- /dev/null
+++ b/chrome/browser/ui/views/frame/terminal_system_app_menu_button_chromeos.cc
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/frame/terminal_system_app_menu_button_chromeos.h"
+
+#include <memory>
+
+#include "base/metrics/user_metrics.h"
+#include "chrome/browser/ui/extensions/terminal_system_app_menu_model_chromeos.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "ui/views/controls/menu/menu_runner.h"
+
+TerminalSystemAppMenuButton::TerminalSystemAppMenuButton(
+    BrowserView* browser_view)
+    : HostedAppMenuButton(browser_view) {}
+
+TerminalSystemAppMenuButton::~TerminalSystemAppMenuButton() {}
+
+void TerminalSystemAppMenuButton::OnMenuButtonClicked(views::Button* source,
+                                                      const gfx::Point& point,
+                                                      const ui::Event* event) {
+  Browser* browser = browser_view()->browser();
+  RunMenu(std::make_unique<TerminalSystemAppMenuModel>(browser_view(), browser),
+          browser,
+          event && event->IsKeyEvent()
+              ? views::MenuRunner::SHOULD_SHOW_MNEMONICS
+              : views::MenuRunner::NO_FLAGS,
+          /*alert_reopen_tab_items=*/false);
+
+  base::RecordAction(
+      base::UserMetricsAction("TerminalSystemAppMenuButtonButton_Clicked"));
+}
+
+const char* TerminalSystemAppMenuButton::GetClassName() const {
+  return "TerminalSystemAppMenuButton";
+}
diff --git a/chrome/browser/ui/views/frame/terminal_system_app_menu_button_chromeos.h b/chrome/browser/ui/views/frame/terminal_system_app_menu_button_chromeos.h
new file mode 100644
index 0000000..99d7b72
--- /dev/null
+++ b/chrome/browser/ui/views/frame/terminal_system_app_menu_button_chromeos.h
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_TERMINAL_SYSTEM_APP_MENU_BUTTON_CHROMEOS_H_
+#define CHROME_BROWSER_UI_VIEWS_FRAME_TERMINAL_SYSTEM_APP_MENU_BUTTON_CHROMEOS_H_
+
+#include "chrome/browser/ui/views/frame/hosted_app_menu_button.h"
+
+class BrowserView;
+
+class TerminalSystemAppMenuButton : public HostedAppMenuButton {
+ public:
+  explicit TerminalSystemAppMenuButton(BrowserView* browser_view);
+  ~TerminalSystemAppMenuButton() override;
+
+  // views::MenuButtonListener:
+  void OnMenuButtonClicked(views::Button* source,
+                           const gfx::Point& point,
+                           const ui::Event* event) override;
+
+ private:
+  // views::View:
+  const char* GetClassName() const override;
+
+  DISALLOW_COPY_AND_ASSIGN(TerminalSystemAppMenuButton);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_TERMINAL_SYSTEM_APP_MENU_BUTTON_CHROMEOS_H_
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index 86ab0625..0365dba 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -30,6 +30,7 @@
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_web_contents_factory.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
@@ -365,11 +366,11 @@
 
   // Connect the UsbChooserContext with FakeUsbDeviceManager.
   device::FakeUsbDeviceManager usb_device_manager;
-  device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-  usb_device_manager.AddBinding(mojo::MakeRequest(&device_manager_ptr));
+  mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager;
+  usb_device_manager.AddReceiver(usb_manager.InitWithNewPipeAndPassReceiver());
   UsbChooserContext* store =
       UsbChooserContextFactory::GetForProfile(web_contents_helper_.profile());
-  store->SetDeviceManagerForTesting(std::move(device_manager_ptr));
+  store->SetDeviceManagerForTesting(std::move(usb_manager));
 
   auto device_info = usb_device_manager.CreateAndAddDevice(
       0, 0, "Google", "Gizmo", "1234567890");
@@ -470,10 +471,11 @@
 
   // Connect the UsbChooserContext with FakeUsbDeviceManager.
   device::FakeUsbDeviceManager usb_device_manager;
-  device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-  usb_device_manager.AddBinding(mojo::MakeRequest(&device_manager_ptr));
+  mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
+  usb_device_manager.AddReceiver(
+      device_manager.InitWithNewPipeAndPassReceiver());
   UsbChooserContext* store = UsbChooserContextFactory::GetForProfile(profile);
-  store->SetDeviceManagerForTesting(std::move(device_manager_ptr));
+  store->SetDeviceManagerForTesting(std::move(device_manager));
 
   auto device_info = usb_device_manager.CreateAndAddDevice(
       0, 0, "Google", "Gizmo", "1234567890");
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.cc b/chrome/browser/ui/web_applications/app_browser_controller.cc
index db1fe7e..7c3fcb3d 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/app_browser_controller.cc
@@ -117,6 +117,18 @@
   return !IsForSystemWebApp();
 }
 
+#if defined(OS_CHROMEOS)
+bool AppBrowserController::UseTitlebarTerminalSystemAppMenu() const {
+  // Use the Terminal System App Menu for Terminal System App only.
+  // TODO(crbug.com/846546): Generalise this as a SystemWebApp capability.
+  if (IsForSystemWebApp()) {
+    return GetAppIdForSystemWebApp(browser()->profile(),
+                                   SystemAppType::TERMINAL) == GetAppId();
+  }
+  return false;
+}
+#endif
+
 bool AppBrowserController::IsInstalled() const {
   return false;
 }
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.h b/chrome/browser/ui/web_applications/app_browser_controller.h
index 77574ecc..6838bf6 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/app_browser_controller.h
@@ -70,6 +70,11 @@
   // Whether to show content settings in the titlebar toolbar.
   virtual bool HasTitlebarContentSettings() const;
 
+#if defined(OS_CHROMEOS)
+  // Whether to use the Terminal System App menu rather than the default menu.
+  virtual bool UseTitlebarTerminalSystemAppMenu() const;
+#endif
+
   // Returns the app icon for the window to use in the task list.
   virtual gfx::ImageSkia GetWindowAppIcon() const = 0;
 
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
index 13276fa..f1efaf5 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
@@ -35,6 +35,7 @@
 #include "extensions/browser/api/usb/usb_device_manager.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/value_builder.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "printing/pdf_render_settings.h"
 #include "printing/print_job_constants.h"
 #include "printing/pwg_raster_settings.h"
@@ -507,10 +508,10 @@
         std::move(pwg_raster_converter));
 
     // Set fake USB device manager for extensions::UsbDeviceManager.
-    device::mojom::UsbDeviceManagerPtr usb_manager_ptr;
-    fake_usb_manager_.AddBinding(mojo::MakeRequest(&usb_manager_ptr));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager;
+    fake_usb_manager_.AddReceiver(usb_manager.InitWithNewPipeAndPassReceiver());
     extensions::UsbDeviceManager::Get(env_.profile())
-        ->SetDeviceManagerForTesting(std::move(usb_manager_ptr));
+        ->SetDeviceManagerForTesting(std::move(usb_manager));
     base::RunLoop().RunUntilIdle();
   }
 
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 6392501..4638d2a0 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -223,12 +223,12 @@
 const char kIsInAppKioskMode[] = "isInAppKioskMode";
 // Name of a dictionary field holding the UI locale.
 const char kUiLocale[] = "uiLocale";
-// Name of a dictionary field holding the thousands delimeter according to the
+// Name of a dictionary field holding the thousands delimiter according to the
 // locale.
-const char kThousandsDelimeter[] = "thousandsDelimeter";
-// Name of a dictionary field holding the decimal delimeter according to the
+const char kThousandsDelimiter[] = "thousandsDelimiter";
+// Name of a dictionary field holding the decimal delimiter according to the
 // locale.
-const char kDecimalDelimeter[] = "decimalDelimeter";
+const char kDecimalDelimiter[] = "decimalDelimiter";
 // Name of a dictionary field holding the measurement system according to the
 // locale.
 const char kUnitType[] = "unitType";
@@ -933,8 +933,8 @@
   // Getting the number formatting based on the locale and writing to
   // dictionary.
   base::string16 number_format = base::FormatDouble(123456.78, 2);
-  settings->SetStringKey(kDecimalDelimeter, number_format.substr(7, 1));
-  settings->SetStringKey(kThousandsDelimeter, number_format.substr(3, 1));
+  settings->SetStringKey(kDecimalDelimiter, number_format.substr(7, 1));
+  settings->SetStringKey(kThousandsDelimiter, number_format.substr(3, 1));
   settings->SetIntKey(kUnitType, system);
 }
 
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
index a3991fb..b79d3a8 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "base/base64.h"
 #include "base/containers/flat_set.h"
+#include "base/i18n/number_formatting.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/memory/ref_counted_memory.h"
@@ -25,6 +26,7 @@
 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
 #include "chrome/browser/ui/webui/print_preview/printer_handler.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_service.h"
 #include "components/printing/common/print_messages.h"
@@ -284,18 +286,30 @@
     return std::make_unique<TestPrinterHandler>(printers);
   }
 
-  void Initialize() {
-    // Set locale since the delimeters we check in VerifyInitialSettings()
-    // depend on it.
-    base::test::ScopedRestoreICUDefaultLocale scoped_locale("en");
+  void Initialize() { InitializeWithLocale("en"); }
 
+  void InitializeWithLocale(const std::string& locale) {
     // Sending this message will enable javascript, so it must always be called
     // before any other messages are sent.
     base::Value args(base::Value::Type::LIST);
     args.GetList().emplace_back("test-callback-id-0");
     std::unique_ptr<base::ListValue> list_args =
         base::ListValue::From(base::Value::ToUniquePtrValue(std::move(args)));
-    handler()->HandleGetInitialSettings(list_args.get());
+
+    auto* browser_process = TestingBrowserProcess::GetGlobal();
+    std::string original_locale = browser_process->GetApplicationLocale();
+    {
+      // Set locale since the delimiters checked in VerifyInitialSettings()
+      // depend on it. This has to be done in several ways to make various
+      // locale code sync up correctly.
+      browser_process->SetApplicationLocale(locale);
+      base::test::ScopedRestoreICUDefaultLocale scoped_locale(locale);
+      base::testing::ResetFormatters();
+      handler()->HandleGetInitialSettings(list_args.get());
+    }
+    // Reset again now that |scoped_locale| has been destroyed.
+    browser_process->SetApplicationLocale(original_locale);
+    base::testing::ResetFormatters();
 
     // In response to get initial settings, the initial settings are sent back.
     ASSERT_EQ(1u, web_ui()->call_data().size());
@@ -321,18 +335,33 @@
     EXPECT_EQ(expect_success, success);
   }
 
+  void ValidateInitialSettings(const content::TestWebUI::CallData& data,
+                               const std::string& default_printer_name,
+                               const std::string& initiator_title,
+                               base::Optional<bool> expected_header_footer) {
+    ValidateInitialSettingsForLocale(data, default_printer_name,
+                                     initiator_title, "en", ",", ".",
+                                     expected_header_footer);
+  }
+
   // Validates the initial settings structure in the response matches the
   // print_preview.NativeInitialSettings type in
   // chrome/browser/resources/print_preview/native_layer.js. Checks that:
   //   - |default_printer_name| is the printer name returned
   //   - |initiator_title| is the initiator title returned
   //   - |expected_header_footer| is the header/footer state returned, if any
-  // Also validates that delimeters are correct for "en" locale (set in
-  // Initialize()).  Assumes "test-callback-id-0" was used as the callback id.
-  void ValidateInitialSettings(const content::TestWebUI::CallData& data,
-                               const std::string& default_printer_name,
-                               const std::string& initiator_title,
-                               base::Optional<bool> expected_header_footer) {
+  // Also validates that delimiters are correct for |locale| (set in
+  // InitializeWithLocale()) with the associated |thousands_delimiter| and
+  // |decimal_delimiter|.
+  // Assumes "test-callback-id-0" was used as the callback id.
+  void ValidateInitialSettingsForLocale(
+      const content::TestWebUI::CallData& data,
+      const std::string& default_printer_name,
+      const std::string& initiator_title,
+      const std::string& locale,
+      const std::string& thousands_delimiter,
+      const std::string& decimal_delimiter,
+      base::Optional<bool> expected_header_footer) {
     CheckWebUIResponse(data, "test-callback-id-0", true);
     const base::Value* settings = data.arg3();
     ASSERT_TRUE(settings->FindKeyOfType("isInKioskAutoPrintMode",
@@ -340,18 +369,17 @@
     ASSERT_TRUE(settings->FindKeyOfType("isInAppKioskMode",
                                         base::Value::Type::BOOLEAN));
 
-    const base::Value* locale =
-        settings->FindKeyOfType("uiLocale", base::Value::Type::STRING);
-    ASSERT_TRUE(locale);
-    EXPECT_EQ("en", locale->GetString());
-    const base::Value* thousands_delimeter = settings->FindKeyOfType(
-        "thousandsDelimeter", base::Value::Type::STRING);
-    ASSERT_TRUE(thousands_delimeter);
-    EXPECT_EQ(",", thousands_delimeter->GetString());
-    const base::Value* decimal_delimeter =
-        settings->FindKeyOfType("decimalDelimeter", base::Value::Type::STRING);
-    ASSERT_TRUE(decimal_delimeter);
-    EXPECT_EQ(".", decimal_delimeter->GetString());
+    const std::string* actual_locale = settings->FindStringKey("uiLocale");
+    ASSERT_TRUE(actual_locale);
+    EXPECT_EQ(locale, *actual_locale);
+    const std::string* actual_thousands_delimiter =
+        settings->FindStringKey("thousandsDelimiter");
+    ASSERT_TRUE(actual_thousands_delimiter);
+    EXPECT_EQ(thousands_delimiter, *actual_thousands_delimiter);
+    const std::string* actual_decimal_delimiter =
+        settings->FindStringKey("decimalDelimiter");
+    ASSERT_TRUE(actual_decimal_delimiter);
+    EXPECT_EQ(decimal_delimiter, *actual_decimal_delimiter);
 
     ASSERT_TRUE(
         settings->FindKeyOfType("unitType", base::Value::Type::INTEGER));
@@ -437,6 +465,25 @@
                           kDummyInitiatorName, {});
 }
 
+TEST_F(PrintPreviewHandlerTest, InitialSettingsHiLocale) {
+  InitializeWithLocale("hi");
+
+  // Verify initial settings were sent for Hindi.
+  // TODO(crbug.com/998039): Fix the incorrect delimiters.
+  ValidateInitialSettingsForLocale(*web_ui()->call_data().back(),
+                                   kDummyPrinterName, kDummyInitiatorName, "hi",
+                                   "3", "6", {});
+}
+
+TEST_F(PrintPreviewHandlerTest, InitialSettingsRuLocale) {
+  InitializeWithLocale("ru");
+
+  // Verify initial settings were sent for Russian.
+  ValidateInitialSettingsForLocale(*web_ui()->call_data().back(),
+                                   kDummyPrinterName, kDummyInitiatorName, "ru",
+                                   "\xC2\xA0", ",", {});
+}
+
 TEST_F(PrintPreviewHandlerTest, InitialSettingsEnableHeaderFooter) {
   // Set a pref that should take priority over StickySettings.
   prefs()->SetBoolean(prefs::kPrintHeaderFooter, true);
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index 96b65f4..3d5fc205 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -52,6 +52,7 @@
 #include "content/public/test/test_web_ui.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension_builder.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -1560,9 +1561,10 @@
         6355, 0, "Google", "Widget", "789XYZ");
 
     auto* chooser_context = UsbChooserContextFactory::GetForProfile(profile());
-    device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-    device_manager_.AddBinding(mojo::MakeRequest(&device_manager_ptr));
-    chooser_context->SetDeviceManagerForTesting(std::move(device_manager_ptr));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
+    device_manager_.AddReceiver(
+        device_manager.InitWithNewPipeAndPassReceiver());
+    chooser_context->SetDeviceManagerForTesting(std::move(device_manager));
     chooser_context->GetDevices(
         base::DoNothing::Once<std::vector<device::mojom::UsbDeviceInfoPtr>>());
     base::RunLoop().RunUntilIdle();
@@ -1602,9 +1604,10 @@
     CreateIncognitoProfile();
     auto* chooser_context =
         UsbChooserContextFactory::GetForProfile(incognito_profile());
-    device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-    device_manager_.AddBinding(mojo::MakeRequest(&device_manager_ptr));
-    chooser_context->SetDeviceManagerForTesting(std::move(device_manager_ptr));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
+    device_manager_.AddReceiver(
+        device_manager.InitWithNewPipeAndPassReceiver());
+    chooser_context->SetDeviceManagerForTesting(std::move(device_manager));
     chooser_context->GetDevices(
         base::DoNothing::Once<std::vector<device::mojom::UsbDeviceInfoPtr>>());
     base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/ui/webui/site_settings_helper_unittest.cc b/chrome/browser/ui/webui/site_settings_helper_unittest.cc
index f3be3294..fa6bc52 100644
--- a/chrome/browser/ui/webui/site_settings_helper_unittest.cc
+++ b/chrome/browser/ui/webui/site_settings_helper_unittest.cc
@@ -22,6 +22,7 @@
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/extension_registry.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -457,9 +458,10 @@
         device_manager_.CreateAndAddDevice(6354, 0, "Google", "Gadget", "");
 
     auto* chooser_context = UsbChooserContextFactory::GetForProfile(profile());
-    device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-    device_manager_.AddBinding(mojo::MakeRequest(&device_manager_ptr));
-    chooser_context->SetDeviceManagerForTesting(std::move(device_manager_ptr));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
+    device_manager_.AddReceiver(
+        device_manager.InitWithNewPipeAndPassReceiver());
+    chooser_context->SetDeviceManagerForTesting(std::move(device_manager));
     chooser_context->GetDevices(
         base::DoNothing::Once<std::vector<device::mojom::UsbDeviceInfoPtr>>());
     base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/ui/webui/usb_internals/usb_internals.mojom b/chrome/browser/ui/webui/usb_internals/usb_internals.mojom
index 88b50f7..e83daa2 100644
--- a/chrome/browser/ui/webui/usb_internals/usb_internals.mojom
+++ b/chrome/browser/ui/webui/usb_internals/usb_internals.mojom
@@ -8,8 +8,9 @@
 import "services/device/public/mojom/usb_manager_test.mojom";
 
 interface UsbInternalsPageHandler {
-  // Bind the UsbDeviceManager interface to get all devices that connected.
-  BindUsbDeviceManagerInterface(device.mojom.UsbDeviceManager& request);
+  // Bind the UsbDeviceManager receiver to get all devices that connected.
+  BindUsbDeviceManagerInterface(
+      pending_receiver<device.mojom.UsbDeviceManager> receiver);
 
   // Simulate the connection of a new device with the given properties.
   BindTestInterface(device.mojom.UsbDeviceManagerTest& request);
diff --git a/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.cc b/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.cc
index 53993fa..8e15929b 100644
--- a/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.cc
+++ b/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.cc
@@ -24,8 +24,8 @@
 }
 
 void UsbInternalsPageHandler::BindUsbDeviceManagerInterface(
-    device::mojom::UsbDeviceManagerRequest request) {
+    mojo::PendingReceiver<device::mojom::UsbDeviceManager> receiver) {
   // Forward the request to the DeviceService.
-  content::GetSystemConnector()->BindInterface(device::mojom::kServiceName,
-                                               std::move(request));
+  content::GetSystemConnector()->Connect(device::mojom::kServiceName,
+                                         std::move(receiver));
 }
diff --git a/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h b/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h
index 7ca5ce7..95a9311 100644
--- a/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h
+++ b/chrome/browser/ui/webui/usb_internals/usb_internals_page_handler.h
@@ -19,7 +19,7 @@
   ~UsbInternalsPageHandler() override;
 
   void BindUsbDeviceManagerInterface(
-      device::mojom::UsbDeviceManagerRequest request) override;
+      mojo::PendingReceiver<device::mojom::UsbDeviceManager> receiver) override;
 
   void BindTestInterface(
       device::mojom::UsbDeviceManagerTestRequest request) override;
diff --git a/chrome/browser/usb/usb_browsertest.cc b/chrome/browser/usb/usb_browsertest.cc
index 6d162e7..68e8df2 100644
--- a/chrome/browser/usb/usb_browsertest.cc
+++ b/chrome/browser/usb/usb_browsertest.cc
@@ -24,6 +24,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
@@ -36,8 +37,8 @@
 
 using content::RenderFrameHost;
 using device::FakeUsbDeviceManager;
-using device::mojom::UsbDeviceManagerPtr;
 using device::mojom::UsbDeviceInfoPtr;
+using device::mojom::UsbDeviceManager;
 
 namespace {
 
@@ -136,10 +137,11 @@
     AddFakeDevice("123456");
 
     // Connect with the FakeUsbDeviceManager.
-    UsbDeviceManagerPtr device_manager_ptr;
-    device_manager_.AddBinding(mojo::MakeRequest(&device_manager_ptr));
+    mojo::PendingRemote<UsbDeviceManager> device_manager;
+    device_manager_.AddReceiver(
+        device_manager.InitWithNewPipeAndPassReceiver());
     UsbChooserContextFactory::GetForProfile(browser()->profile())
-        ->SetDeviceManagerForTesting(std::move(device_manager_ptr));
+        ->SetDeviceManagerForTesting(std::move(device_manager));
 
     original_content_browser_client_ =
         content::SetBrowserClientForTesting(&test_content_browser_client_);
diff --git a/chrome/browser/usb/usb_chooser_context.cc b/chrome/browser/usb/usb_chooser_context.cc
index 0a3578ec..170b732e 100644
--- a/chrome/browser/usb/usb_chooser_context.cc
+++ b/chrome/browser/usb/usb_chooser_context.cc
@@ -179,16 +179,17 @@
   if (device_manager_)
     return;
 
-  // Request UsbDeviceManagerPtr from DeviceService.
-  content::GetSystemConnector()->BindInterface(
-      device::mojom::kServiceName, mojo::MakeRequest(&device_manager_));
+  // Receive mojo::Remote<UsbDeviceManager> from DeviceService.
+  content::GetSystemConnector()->Connect(
+      device::mojom::kServiceName,
+      device_manager_.BindNewPipeAndPassReceiver());
 
   SetUpDeviceManagerConnection();
 }
 
 void UsbChooserContext::SetUpDeviceManagerConnection() {
   DCHECK(device_manager_);
-  device_manager_.set_connection_error_handler(
+  device_manager_.set_disconnect_handler(
       base::BindOnce(&UsbChooserContext::OnDeviceManagerConnectionError,
                      base::Unretained(this)));
 
@@ -608,9 +609,9 @@
 }
 
 void UsbChooserContext::SetDeviceManagerForTesting(
-    device::mojom::UsbDeviceManagerPtr fake_device_manager) {
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_device_manager) {
   DCHECK(!device_manager_);
   DCHECK(fake_device_manager);
-  device_manager_ = std::move(fake_device_manager);
+  device_manager_.Bind(std::move(fake_device_manager));
   SetUpDeviceManagerConnection();
 }
diff --git a/chrome/browser/usb/usb_chooser_context.h b/chrome/browser/usb/usb_chooser_context.h
index a3920bb..447c27c 100644
--- a/chrome/browser/usb/usb_chooser_context.h
+++ b/chrome/browser/usb/usb_chooser_context.h
@@ -20,6 +20,8 @@
 #include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/usb/usb_policy_allowed_devices.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 #include "services/device/public/mojom/usb_manager_client.mojom.h"
 #include "url/origin.h"
@@ -85,7 +87,7 @@
   base::WeakPtr<UsbChooserContext> AsWeakPtr();
 
   void SetDeviceManagerForTesting(
-      device::mojom::UsbDeviceManagerPtr fake_device_manager);
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_device_manager);
 
   // ChooserContextBase implementation.
   bool IsValidObject(const base::Value& object) override;
@@ -120,7 +122,7 @@
   std::unique_ptr<UsbPolicyAllowedDevices> usb_policy_allowed_devices_;
 
   // Connection to |device_manager_instance_|.
-  device::mojom::UsbDeviceManagerPtr device_manager_;
+  mojo::Remote<device::mojom::UsbDeviceManager> device_manager_;
   mojo::AssociatedBinding<device::mojom::UsbDeviceManagerClient>
       client_binding_;
   base::ObserverList<DeviceObserver> device_observer_list_;
diff --git a/chrome/browser/usb/usb_chooser_context_unittest.cc b/chrome/browser/usb/usb_chooser_context_unittest.cc
index 90d4199..f9de110 100644
--- a/chrome/browser/usb/usb_chooser_context_unittest.cc
+++ b/chrome/browser/usb/usb_chooser_context_unittest.cc
@@ -59,9 +59,10 @@
 
   UsbChooserContext* GetChooserContext(Profile* profile) {
     auto* chooser_context = UsbChooserContextFactory::GetForProfile(profile);
-    device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-    device_manager_.AddBinding(mojo::MakeRequest(&device_manager_ptr));
-    chooser_context->SetDeviceManagerForTesting(std::move(device_manager_ptr));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
+    device_manager_.AddReceiver(
+        device_manager.InitWithNewPipeAndPassReceiver());
+    chooser_context->SetDeviceManagerForTesting(std::move(device_manager));
 
     // Call GetDevices once to make sure the connection with DeviceManager has
     // been set up, so that it can be notified when device is removed.
diff --git a/chrome/browser/usb/usb_chooser_controller_unittest.cc b/chrome/browser/usb/usb_chooser_controller_unittest.cc
index 8249e6f0..fb20276 100644
--- a/chrome/browser/usb/usb_chooser_controller_unittest.cc
+++ b/chrome/browser/usb/usb_chooser_controller_unittest.cc
@@ -16,6 +16,7 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/web_contents_tester.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_enumeration_options.mojom.h"
@@ -59,10 +60,11 @@
     web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
 
     // Set fake device manager for UsbChooserContext.
-    device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-    device_manager_.AddBinding(mojo::MakeRequest(&device_manager_ptr));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
+    device_manager_.AddReceiver(
+        device_manager.InitWithNewPipeAndPassReceiver());
     UsbChooserContextFactory::GetForProfile(profile())
-        ->SetDeviceManagerForTesting(std::move(device_manager_ptr));
+        ->SetDeviceManagerForTesting(std::move(device_manager));
 
     usb_chooser_controller_.reset(new UsbChooserController(
         main_rfh(), std::move(device_filters), std::move(callback)));
diff --git a/chrome/browser/usb/web_usb_detector.cc b/chrome/browser/usb/web_usb_detector.cc
index d8f23f14..2cffcf57 100644
--- a/chrome/browser/usb/web_usb_detector.cc
+++ b/chrome/browser/usb/web_usb_detector.cc
@@ -194,12 +194,13 @@
   SCOPED_UMA_HISTOGRAM_TIMER("WebUsb.DetectorInitialization");
   // Tests may set a fake manager.
   if (!device_manager_) {
-    // Request UsbDeviceManagerPtr from DeviceService.
-    content::GetSystemConnector()->BindInterface(
-        device::mojom::kServiceName, mojo::MakeRequest(&device_manager_));
+    // Receive mojo::Remote<UsbDeviceManager> from DeviceService.
+    content::GetSystemConnector()->Connect(
+        device::mojom::kServiceName,
+        device_manager_.BindNewPipeAndPassReceiver());
   }
   DCHECK(device_manager_);
-  device_manager_.set_connection_error_handler(base::BindOnce(
+  device_manager_.set_disconnect_handler(base::BindOnce(
       &WebUsbDetector::OnDeviceManagerConnectionError, base::Unretained(this)));
 
   // Listen for added/removed device events.
@@ -282,9 +283,9 @@
 }
 
 void WebUsbDetector::SetDeviceManagerForTesting(
-    device::mojom::UsbDeviceManagerPtr fake_device_manager) {
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_device_manager) {
   DCHECK(!device_manager_);
   DCHECK(!client_binding_);
   DCHECK(fake_device_manager);
-  device_manager_ = std::move(fake_device_manager);
+  device_manager_.Bind(std::move(fake_device_manager));
 }
diff --git a/chrome/browser/usb/web_usb_detector.h b/chrome/browser/usb/web_usb_detector.h
index efdc4445..7742e92c 100644
--- a/chrome/browser/usb/web_usb_detector.h
+++ b/chrome/browser/usb/web_usb_detector.h
@@ -9,6 +9,8 @@
 
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 #include "services/device/public/mojom/usb_manager_client.mojom.h"
 #include "url/gurl.h"
@@ -22,7 +24,7 @@
   void Initialize();
 
   void SetDeviceManagerForTesting(
-      device::mojom::UsbDeviceManagerPtr fake_device_manager);
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_device_manager);
   void RemoveNotification(const std::string& id);
 
  private:
@@ -36,7 +38,7 @@
   std::map<std::string, GURL> open_notifications_by_id_;
 
   // Connection to |device_manager_instance_|.
-  device::mojom::UsbDeviceManagerPtr device_manager_;
+  mojo::Remote<device::mojom::UsbDeviceManager> device_manager_;
   mojo::AssociatedBinding<device::mojom::UsbDeviceManagerClient>
       client_binding_;
 
diff --git a/chrome/browser/usb/web_usb_detector_unittest.cc b/chrome/browser/usb/web_usb_detector_unittest.cc
index 0f11a64..ca2cf41 100644
--- a/chrome/browser/usb/web_usb_detector_unittest.cc
+++ b/chrome/browser/usb/web_usb_detector_unittest.cc
@@ -86,10 +86,10 @@
 
     web_usb_detector_.reset(new WebUsbDetector());
     // Set a fake USB device manager before Initialize().
-    device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-    device_manager_.AddBinding(mojo::MakeRequest(&device_manager_ptr));
-    web_usb_detector_->SetDeviceManagerForTesting(
-        std::move(device_manager_ptr));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
+    device_manager_.AddReceiver(
+        device_manager.InitWithNewPipeAndPassReceiver());
+    web_usb_detector_->SetDeviceManagerForTesting(std::move(device_manager));
   }
 
   void TearDown() override {
diff --git a/chrome/browser/usb/web_usb_service_impl_unittest.cc b/chrome/browser/usb/web_usb_service_impl_unittest.cc
index 928adb35..402a03c 100644
--- a/chrome/browser/usb/web_usb_service_impl_unittest.cc
+++ b/chrome/browser/usb/web_usb_service_impl_unittest.cc
@@ -20,6 +20,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/web_contents_tester.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/cpp/test/fake_usb_device_info.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
@@ -67,10 +68,12 @@
       mojo::PendingReceiver<blink::mojom::WebUsbService> receiver) {
     // Set fake device manager for UsbChooserContext.
     if (!device_manager()->IsBound()) {
-      device::mojom::UsbDeviceManagerPtr device_manager_ptr;
-      device_manager()->AddBinding(mojo::MakeRequest(&device_manager_ptr));
+      mojo::PendingRemote<device::mojom::UsbDeviceManager>
+          pending_device_manager;
+      device_manager()->AddReceiver(
+          pending_device_manager.InitWithNewPipeAndPassReceiver());
       GetChooserContext()->SetDeviceManagerForTesting(
-          std::move(device_manager_ptr));
+          std::move(pending_device_manager));
     }
 
     if (!web_usb_service_)
diff --git a/chrome/browser/vr/test/mock_xr_device_hook_base.cc b/chrome/browser/vr/test/mock_xr_device_hook_base.cc
index 10b66a8..f373b72 100644
--- a/chrome/browser/vr/test/mock_xr_device_hook_base.cc
+++ b/chrome/browser/vr/test/mock_xr_device_hook_base.cc
@@ -145,6 +145,12 @@
   std::move(callback).Run(DeviceToMojoControllerFrameData(data));
 }
 
+void MockXRDeviceHookBase::WaitGetSessionStateStopping(
+    device_test::mojom::XRTestHook::WaitGetSessionStateStoppingCallback
+        callback) {
+  std::move(callback).Run(false);
+}
+
 unsigned int MockXRDeviceHookBase::ConnectController(
     const device::ControllerFrameData& initial_data) {
   // Find the first open tracked device slot and fill that.
diff --git a/chrome/browser/vr/test/mock_xr_device_hook_base.h b/chrome/browser/vr/test/mock_xr_device_hook_base.h
index 47dfd2f..9b9c0d2d 100644
--- a/chrome/browser/vr/test/mock_xr_device_hook_base.h
+++ b/chrome/browser/vr/test/mock_xr_device_hook_base.h
@@ -40,6 +40,9 @@
       unsigned int index,
       device_test::mojom::XRTestHook::WaitGetControllerDataCallback callback)
       override;
+  void WaitGetSessionStateStopping(
+      device_test::mojom::XRTestHook::WaitGetSessionStateStoppingCallback
+          callback) override;
 
   // MockXRDeviceHookBase
   void TerminateDeviceServiceProcessForTesting();
diff --git a/chrome/browser/vr/webxr_vr_transition_browser_test.cc b/chrome/browser/vr/webxr_vr_transition_browser_test.cc
index 4dbdac09..e05ebafa 100644
--- a/chrome/browser/vr/webxr_vr_transition_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_transition_browser_test.cc
@@ -17,6 +17,22 @@
 
 namespace vr {
 
+class TransitionXRMock : public MockXRDeviceHookBase {
+ public:
+  void WaitGetSessionStateStopping(
+      device_test::mojom::XRTestHook::WaitGetSessionStateStoppingCallback
+          callback) final;
+
+  bool session_state_stopping_ = false;
+};
+
+void TransitionXRMock::WaitGetSessionStateStopping(
+    device_test::mojom::XRTestHook::WaitGetSessionStateStoppingCallback
+        callback) {
+  std::move(callback).Run(session_state_stopping_);
+  session_state_stopping_ = false;
+}
+
 // Tests that WebVR/WebXR is not exposed if the flag is not on and the page does
 // not have an origin trial token.
 void TestApiDisabledWithoutFlagSetImpl(WebXrVrBrowserTestBase* t,
@@ -133,6 +149,28 @@
   t->EndTest();
 }
 
+#if BUILDFLAG(ENABLE_OPENXR)
+IN_PROC_BROWSER_TEST_F(WebXrVrOpenXrBrowserTest, TestSessionEnded) {
+  TransitionXRMock transition_mock;
+
+  // Load the test page, and enter presentation.
+  this->LoadUrlAndAwaitInitialization(
+      this->GetFileUrlForHtmlTestFile("test_webxr_presentation_ended"));
+  this->EnterSessionWithUserGestureOrFail();
+
+  // Wait for JavaScript to submit at least one frame.
+  ASSERT_TRUE(this->PollJavaScriptBoolean("hasPresentedFrame",
+                                          this->kPollTimeoutMedium))
+      << "No frame submitted";
+  // Trigger the OpenXr Runtime to send the stop event and wait until we see the
+  // session get terminated.
+  transition_mock.session_state_stopping_ = true;
+  // Tell JavaScript that it is done with the test.
+  this->WaitOnJavaScriptStep();
+  this->EndTest();
+}
+#endif  // BUILDFLAG(ENABLE_OPENXR)
+
 #endif  // OS_WIN
 
 }  // namespace vr
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
index 2142a07..5149c42 100644
--- a/chrome/browser/web_applications/web_app_registrar.cc
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -146,25 +146,16 @@
   std::vector<AppId> app_ids;
   app_ids.reserve(registry_.size());
 
-  for (auto& app : AllApps())
+  for (const WebApp& app : AllApps())
     app_ids.push_back(app.app_id());
 
   return app_ids;
 }
 
-WebAppRegistrar::AppSet::Iter::Iter(InternalIter&& internal_iter)
-    : internal_iter_(std::move(internal_iter)) {}
-
-WebAppRegistrar::AppSet::Iter::Iter(Iter&&) = default;
-
-WebAppRegistrar::AppSet::Iter::~Iter() = default;
-
 WebAppRegistrar::AppSet::AppSet(const WebAppRegistrar* registrar)
-    : begin_(registrar->registry_.begin()),
-      end_(registrar->registry_.end())
+    : registrar_(registrar)
 #if DCHECK_IS_ON()
       ,
-      registrar_(registrar),
       mutations_count_(registrar->mutations_count_)
 #endif
 {
@@ -176,7 +167,27 @@
 #endif
 }
 
-WebAppRegistrar::AppSet WebAppRegistrar::AllApps() const {
+WebAppRegistrar::AppSet::iterator WebAppRegistrar::AppSet::begin() {
+  return iterator(registrar_->registry_.begin());
+}
+
+WebAppRegistrar::AppSet::iterator WebAppRegistrar::AppSet::end() {
+  return iterator(registrar_->registry_.end());
+}
+
+WebAppRegistrar::AppSet::const_iterator WebAppRegistrar::AppSet::begin() const {
+  return const_iterator(registrar_->registry_.begin());
+}
+
+WebAppRegistrar::AppSet::const_iterator WebAppRegistrar::AppSet::end() const {
+  return const_iterator(registrar_->registry_.end());
+}
+
+const WebAppRegistrar::AppSet WebAppRegistrar::AllApps() const {
+  return AppSet(this);
+}
+
+WebAppRegistrar::AppSet WebAppRegistrar::AllAppsMutable() {
   return AppSet(this);
 }
 
diff --git a/chrome/browser/web_applications/web_app_registrar.h b/chrome/browser/web_applications/web_app_registrar.h
index acf871d..ac3ebfce 100644
--- a/chrome/browser/web_applications/web_app_registrar.h
+++ b/chrome/browser/web_applications/web_app_registrar.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/callback_forward.h"
+#include "base/gtest_prod_util.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -56,16 +57,18 @@
   class AppSet {
    public:
     // An iterator class that can be used to access the list of apps.
+    template <typename WebAppType>
     class Iter {
      public:
       using InternalIter = Registry::const_iterator;
 
-      explicit Iter(InternalIter&& internal_iter);
-      Iter(Iter&&);
-      ~Iter();
+      explicit Iter(InternalIter&& internal_iter)
+          : internal_iter_(std::move(internal_iter)) {}
+      Iter(Iter&&) = default;
+      ~Iter() = default;
 
       void operator++() { ++internal_iter_; }
-      WebApp& operator*() const { return *internal_iter_->second.get(); }
+      WebAppType& operator*() const { return *internal_iter_->second.get(); }
       bool operator!=(const Iter& iter) const {
         return internal_iter_ != iter.internal_iter_;
       }
@@ -79,27 +82,31 @@
     AppSet(AppSet&&) = default;
     ~AppSet();
 
-    using iterator = Iter;
-    using const_iterator = Iter;
+    using iterator = Iter<WebApp>;
+    using const_iterator = Iter<const WebApp>;
 
-    iterator begin() { return std::move(begin_); }
-    iterator end() { return std::move(end_); }
+    iterator begin();
+    iterator end();
+    const_iterator begin() const;
+    const_iterator end() const;
 
    private:
-    Iter begin_;
-    Iter end_;
-#if DCHECK_IS_ON()
     const WebAppRegistrar* registrar_;
+#if DCHECK_IS_ON()
     const size_t mutations_count_;
 #endif
     DISALLOW_COPY_AND_ASSIGN(AppSet);
   };
 
-  AppSet AllApps() const;
+  const AppSet AllApps() const;
 
   const Registry& registry_for_testing() const { return registry_; }
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(WebAppRegistrarTest, AllAppsMutable);
+
+  AppSet AllAppsMutable();
+
   void OnDatabaseOpened(base::OnceClosure callback, Registry registry);
 
   void CountMutation();
diff --git a/chrome/browser/web_applications/web_app_registrar_unittest.cc b/chrome/browser/web_applications/web_app_registrar_unittest.cc
index 90bc3e3..508ec3e 100644
--- a/chrome/browser/web_applications/web_app_registrar_unittest.cc
+++ b/chrome/browser/web_applications/web_app_registrar_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "base/bind_helpers.h"
 #include "base/strings/string_number_conversions.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/test/test_web_app_database.h"
 #include "chrome/browser/web_applications/test/web_app_test.h"
@@ -172,12 +173,24 @@
   EXPECT_TRUE(ids.empty());
 }
 
+TEST_F(WebAppRegistrarTest, AllAppsMutable) {
+  std::set<AppId> ids = InitRegistrarWithApps("https://example.com/path", 10);
+
+  for (WebApp& web_app : registrar().AllAppsMutable()) {
+    web_app.SetLaunchContainer(LaunchContainer::kWindow);
+    const size_t num_removed = ids.erase(web_app.app_id());
+    EXPECT_EQ(1U, num_removed);
+  }
+
+  EXPECT_TRUE(ids.empty());
+}
+
 TEST_F(WebAppRegistrarTest, DoForEachAndUnregisterAllApps) {
   Registry registry = CreateRegistryForTesting("https://example.com/path", 100);
   auto ids = RegisterAppsForTesting(std::move(registry));
   EXPECT_EQ(100UL, ids.size());
 
-  for (WebApp& web_app : registrar().AllApps()) {
+  for (const WebApp& web_app : registrar().AllApps()) {
     const size_t num_removed = ids.erase(web_app.app_id());
     EXPECT_EQ(1U, num_removed);
   }
diff --git a/chrome/services/cups_proxy/public/cpp/cups_util_unittest.cc b/chrome/services/cups_proxy/public/cpp/cups_util_unittest.cc
index 0d59350..17ea811 100644
--- a/chrome/services/cups_proxy/public/cpp/cups_util_unittest.cc
+++ b/chrome/services/cups_proxy/public/cpp/cups_util_unittest.cc
@@ -56,7 +56,7 @@
 }
 
 // Embedded 'printer-uri' attribute must contain a '/'.
-TEST(GetPrinterIdTest, MissingPathDelimeter) {
+TEST(GetPrinterIdTest, MissingPathDelimiter) {
   ipp_t* ret = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
   if (!ret) {
     return;
@@ -82,7 +82,7 @@
 }
 
 // Endpoints must contain a '/'.
-TEST(ParseEndpointForPrinterIdTest, MissingPathDelimeter) {
+TEST(ParseEndpointForPrinterIdTest, MissingPathDelimiter) {
   EXPECT_FALSE(ParseEndpointForPrinterId(kDefaultPrinterId));
 }
 
diff --git a/chrome/services/isolated_xr_device/xr_test_hook_wrapper.cc b/chrome/services/isolated_xr_device/xr_test_hook_wrapper.cc
index df6426a..2fc4e718 100644
--- a/chrome/services/isolated_xr_device/xr_test_hook_wrapper.cc
+++ b/chrome/services/isolated_xr_device/xr_test_hook_wrapper.cc
@@ -166,6 +166,15 @@
   return {};
 }
 
+bool XRTestHookWrapper::WaitGetSessionStateStopping() {
+  if (hook_) {
+    bool stopping = false;
+    hook_->WaitGetSessionStateStopping(&stopping);
+    return stopping;
+  }
+  return false;
+}
+
 void XRTestHookWrapper::AttachCurrentThread() {
   if (hook_info_) {
     hook_.Bind(std::move(hook_info_));
diff --git a/chrome/services/isolated_xr_device/xr_test_hook_wrapper.h b/chrome/services/isolated_xr_device/xr_test_hook_wrapper.h
index e21c8e7..b6b5a27 100644
--- a/chrome/services/isolated_xr_device/xr_test_hook_wrapper.h
+++ b/chrome/services/isolated_xr_device/xr_test_hook_wrapper.h
@@ -30,6 +30,7 @@
       unsigned int index) override;
   TrackedDeviceClass WaitGetTrackedDeviceClass(unsigned int index) override;
   ControllerFrameData WaitGetControllerData(unsigned int index) override;
+  bool WaitGetSessionStateStopping() override;
   void AttachCurrentThread() override;
   void DetachCurrentThread() override;
 
diff --git a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
index 8df25d7..57beb41 100644
--- a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
+++ b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
@@ -26,8 +26,8 @@
     const initialSettings = {
       isInKioskAutoPrintMode: false,
       isInAppKioskMode: false,
-      thousandsDelimeter: ',',
-      decimalDelimeter: '.',
+      thousandsDelimiter: ',',
+      decimalDelimiter: '.',
       unitType: 1,
       previewModifiable: true,
       documentTitle: 'title',
diff --git a/chrome/test/data/webui/print_preview/print_preview_app_test.js b/chrome/test/data/webui/print_preview/print_preview_app_test.js
index 890dd83..96ee612 100644
--- a/chrome/test/data/webui/print_preview/print_preview_app_test.js
+++ b/chrome/test/data/webui/print_preview/print_preview_app_test.js
@@ -27,8 +27,8 @@
     const initialSettings = {
       isInKioskAutoPrintMode: false,
       isInAppKioskMode: false,
-      thousandsDelimeter: ',',
-      decimalDelimeter: '.',
+      thousandsDelimiter: ',',
+      decimalDelimiter: '.',
       unitType: 1,
       previewModifiable: true,
       documentTitle: 'DocumentABC123',
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.js b/chrome/test/data/webui/print_preview/print_preview_test_utils.js
index 23aab7b..ea088e16 100644
--- a/chrome/test/data/webui/print_preview/print_preview_test_utils.js
+++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.js
@@ -8,8 +8,8 @@
     return {
       isInKioskAutoPrintMode: false,
       isInAppKioskMode: false,
-      thousandsDelimeter: ',',
-      decimalDelimeter: '.',
+      thousandsDelimiter: ',',
+      decimalDelimiter: '.',
       unitType: 1,
       previewModifiable: true,
       documentTitle: 'title',
diff --git a/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js b/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
index 967d3800..dcc1bb25 100644
--- a/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
@@ -130,35 +130,53 @@
 }
 
 /**
- * Removes a saved printer located at |index| and then verify that saved
- * printers list is updated accordingly.
+ * Removes a saved printer located at |index|.
  * @param {!TestCupsPrintersBrowserProxy} cupsPrintersBrowserProxy
- * @param {!HTMLElement} savedPrinters
- * @param {!HTMLElement} entryList
- * @param {!Array<!CupsPrinterInfo>} printerList
+ * @param {!HTMLElement} savedPrintersElement
  * @param {number} index
  * @return {!Promise}
  */
-function removeAndVerifyPrinters(
-    cupsPrintersBrowserProxy, savedPrinters, entryList, printerList, index) {
-  clickThreeDotMenu(entryList[index]);
-  savedPrinters.$$('#removeButton').click();
+function removePrinter(cupsPrintersBrowserProxy, savedPrintersElement, index) {
+  let printerList = cupsPrintersBrowserProxy.printerList.printerList;
+  let savedPrinterEntries = getPrinterEntries(savedPrintersElement);
+
+  clickThreeDotMenu(savedPrinterEntries[index]);
+  savedPrintersElement.$$('#removeButton').click();
 
   return cupsPrintersBrowserProxy.whenCalled('removeCupsPrinter')
       .then(function() {
         // Simulate removing the printer from |cupsPrintersBrowserProxy|.
         printerList.splice(index, 1);
 
+        // Simuluate saved printer changes.
+        cr.webUIListenerCallback(
+            'on-printers-changed', cupsPrintersBrowserProxy.printerList);
         Polymer.dom.flush();
-        return cupsPrintersBrowserProxy.whenCalled('getCupsPrintersList');
-      })
-      .then(function() {
-        entryList = getPrinterEntries(savedPrinters);
-        verifyPrintersList(entryList, printerList);
       });
 }
 
 /**
+ * Removes all saved printers through recursion.
+ * @param {!TestCupsPrintersBrowserProxy} cupsPrintersBrowserProxy
+ * @param {!HTMLElement} savedPrintersElement
+ * @return {!Promise}
+ */
+function removeAllPrinters(cupsPrintersBrowserProxy, savedPrintersElement) {
+  let printerList = cupsPrintersBrowserProxy.printerList.printerList;
+  let savedPrinterEntries = getPrinterEntries(savedPrintersElement);
+
+  if (!printerList.length) {
+    return Promise.resolve();
+  }
+
+  return removePrinter(
+             cupsPrintersBrowserProxy, savedPrintersElement, 0 /* index */)
+      .then(test_util.flushTasks)
+      .then(removeAllPrinters.bind(
+          this, cupsPrintersBrowserProxy, savedPrintersElement));
+}
+
+/**
  * @param {string} printerName
  * @param {string} printerAddress
  * @param {string} printerId
@@ -237,6 +255,8 @@
     api_.enableNetworkType('WiFi');
 
     PolymerTest.clearBody();
+    settings.navigateTo(settings.routes.CUPS_PRINTERS);
+
     page = document.createElement('settings-cups-printers');
     // Enable feature flag to show the new saved printers list.
     // TODO(jimmyxgong): Remove this line when the feature flag is removed.
@@ -245,9 +265,6 @@
     assertTrue(!!page);
 
     Polymer.dom.flush();
-
-    savedPrintersElement = page.$$('settings-cups-saved-printers');
-    assertTrue(!!savedPrintersElement);
   });
 
   teardown(function() {
@@ -260,49 +277,63 @@
   });
 
   test('SavedPrintersSuccessfullyPopulates', function() {
-    // List component contained by CupsSavedPrinters.
-    const savedPrintersList =
-        savedPrintersElement.$$('settings-cups-printers-entry-list');
+    // Wait for saved printers to populate.
+    return test_util.flushTasks().then(() => {
+      savedPrintersElement = page.$$('settings-cups-saved-printers');
+      assertTrue(!!savedPrintersElement);
 
-    return cupsPrintersBrowserProxy.whenCalled('getCupsPrintersList')
-        .then(function() {
-          // Wait for saved printers to populate.
-          Polymer.dom.flush();
+      // List component contained by CupsSavedPrinters.
+      const savedPrintersList =
+          savedPrintersElement.$$('settings-cups-printers-entry-list');
 
-          const printerListEntries = getPrinterEntries(savedPrintersElement);
+      const printerListEntries = getPrinterEntries(savedPrintersElement);
 
-          verifyPrintersList(printerListEntries, printerList);
-        });
+      verifyPrintersList(printerListEntries, printerList);
+    });
   });
 
   test('SuccessfullyRemoveMultipleSavedPrinters', function() {
     let savedPrinterEntries = [];
 
-    return cupsPrintersBrowserProxy.whenCalled('getCupsPrintersList')
-        .then(function() {
-          // Wait for saved printers to populate.
-          Polymer.dom.flush();
+    // Wait for saved printers to populate.
+    return test_util.flushTasks()
+        .then(() => {
+          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          assertTrue(!!savedPrintersElement);
 
+          return removeAllPrinters(
+              cupsPrintersBrowserProxy, savedPrintersElement);
+        })
+        .then(() => {
+          let entryList = getPrinterEntries(savedPrintersElement);
+          verifyPrintersList(entryList, printerList);
+        });
+  });
+
+  test('HideSavedPrintersWhenEmpty', function() {
+    // List component contained by CupsSavedPrinters.
+    let savedPrintersList = [];
+    let savedPrinterEntries = [];
+
+    // Wait for saved printers to populate.
+    return test_util.flushTasks()
+        .then(() => {
+          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          assertTrue(!!savedPrintersElement);
+
+          savedPrintersList =
+              savedPrintersElement.$$('settings-cups-printers-entry-list');
           savedPrinterEntries = getPrinterEntries(savedPrintersElement);
 
-          return removeAndVerifyPrinters(
-              cupsPrintersBrowserProxy, savedPrintersElement,
-              savedPrinterEntries, printerList, 1 /** index */);
+          verifyPrintersList(savedPrinterEntries, printerList);
+
+          assertTrue(!!page.$$('#savedPrinters'));
+
+          return removeAllPrinters(
+              cupsPrintersBrowserProxy, savedPrintersElement);
         })
-        .then(function() {
-          return removeAndVerifyPrinters(
-              cupsPrintersBrowserProxy, savedPrintersElement,
-              savedPrinterEntries, printerList, 0 /** index */);
-        })
-        .then(function() {
-          return removeAndVerifyPrinters(
-              cupsPrintersBrowserProxy, savedPrintersElement,
-              savedPrinterEntries, printerList, 1 /** index */);
-        })
-        .then(function() {
-          return removeAndVerifyPrinters(
-              cupsPrintersBrowserProxy, savedPrintersElement,
-              savedPrinterEntries, printerList, 0 /** index */);
+        .then(() => {
+          assertFalse(!!page.$$('#savedPrinters'));
         });
   });
 
@@ -312,10 +343,11 @@
     let editDialog = null;
     let savedPrinterEntries = null;
 
-    return cupsPrintersBrowserProxy.whenCalled('getCupsPrintersList')
-        .then(function() {
-          // Wait for saved printers to populate.
-          Polymer.dom.flush();
+    // Wait for saved printers to populate.
+    return test_util.flushTasks()
+        .then(() => {
+          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          assertTrue(!!savedPrintersElement);
 
           savedPrinterEntries = getPrinterEntries(savedPrintersElement);
 
@@ -339,7 +371,7 @@
 
           return cupsPrintersBrowserProxy.whenCalled('updateCupsPrinter');
         })
-        .then(function() {
+        .then(() => {
           assertEquals(expectedName, editDialog.activePrinter.printerName);
 
           // Mimic changes to |cupsPrintersBrowserProxy.printerList|.
@@ -356,10 +388,11 @@
     let savedPrinterEntries = null;
     let editDialog = null;
 
-    return cupsPrintersBrowserProxy.whenCalled('getCupsPrintersList')
-        .then(function() {
-          // Wait for saved printers to populate.
-          Polymer.dom.flush();
+    // Wait for saved printers to populate.
+    return test_util.flushTasks()
+        .then(() => {
+          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          assertTrue(!!savedPrintersElement);
 
           savedPrinterEntries = getPrinterEntries(savedPrintersElement);
 
@@ -387,7 +420,7 @@
 
           return cupsPrintersBrowserProxy.whenCalled('reconfigureCupsPrinter');
         })
-        .then(function() {
+        .then(() => {
           assertEquals(expectedName, editDialog.activePrinter.printerName);
           assertEquals(
               expectedAddress, editDialog.activePrinter.printerAddress);
@@ -458,6 +491,8 @@
     setNetworksForTest(api_, activeNetworks_);
 
     PolymerTest.clearBody();
+    settings.navigateTo(settings.routes.CUPS_PRINTERS);
+
     page = document.createElement('settings-cups-printers');
     // Enable feature flag to show the new saved printers list.
     // TODO(jimmyxgong): Remove this line when the feature flag is removed.
diff --git a/chrome/test/data/xr/e2e_test_files/html/test_webxr_presentation_ended.html b/chrome/test/data/xr/e2e_test_files/html/test_webxr_presentation_ended.html
new file mode 100644
index 0000000..83f1e228
--- /dev/null
+++ b/chrome/test/data/xr/e2e_test_files/html/test_webxr_presentation_ended.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+Tests WebXR Using OpenXR End Session correctly when XR_SESSION_STATE_STOPPING
+event received.
+-->
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
+  </head>
+  <body>
+    <canvas id="webgl-canvas"></canvas>
+    <script src="../../../../../../third_party/blink/web_tests/resources/testharness.js"></script>
+    <script src="../resources/webxr_e2e.js"></script>
+    <script src="../resources/webxr_boilerplate.js"></script>
+    <script>
+      onSessionEnded = function(event) {
+        sessionInfos[getSessionType(event.session)].clearSession();
+        if (hasPresentedFrame) {
+          done();
+        }
+      }
+    </script>
+  </body>
+</html>
diff --git a/chrome/test/media_router/BUILD.gn b/chrome/test/media_router/BUILD.gn
index 7f789e6..c894251 100644
--- a/chrome/test/media_router/BUILD.gn
+++ b/chrome/test/media_router/BUILD.gn
@@ -144,8 +144,7 @@
   group("media_router_perf_tests") {
     testonly = true
     data = [
-      "$root_out_dir/media_router/telemetry_extension/",
-      "$root_out_dir/mr_extension/release/",
+      "$root_out_dir/mr_extension/",
       "internal/",
       "telemetry/",
     ]
diff --git a/chromeos/OWNERS b/chromeos/OWNERS
index b1fd1cf1..301eaa5 100644
--- a/chromeos/OWNERS
+++ b/chromeos/OWNERS
@@ -14,3 +14,4 @@
 satorux@chromium.org
 stevenjb@chromium.org
 xiyuan@chromium.org
+# COMPONENT: UI>Shell
diff --git a/chromeos/chromeos_strings_grd/OWNERS b/chromeos/chromeos_strings_grd/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/chromeos/components/OWNERS b/chromeos/components/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/components/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/chromeos/components/nearby/condition_variable_impl_unittest.cc b/chromeos/components/nearby/condition_variable_impl_unittest.cc
index f8efbe5c..3a4e3f0 100644
--- a/chromeos/components/nearby/condition_variable_impl_unittest.cc
+++ b/chromeos/components/nearby/condition_variable_impl_unittest.cc
@@ -186,6 +186,7 @@
 }
 
 TEST_F(ConditionVariableImplTest, ThreadCannotWaitIfStillOwnsLock) {
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
   fake_lock()->set_is_held_by_current_thread(true);
   WaitOnConditionVariableFromParallelSequence(false /* should_succeed */);
 }
diff --git a/chromeos/resources/OWNERS b/chromeos/resources/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/resources/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/chromeos/services/OWNERS b/chromeos/services/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/services/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/chromeos/services/media_perception/OWNERS b/chromeos/services/media_perception/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/services/media_perception/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/chromeos/strings/OWNERS b/chromeos/strings/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/strings/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/chromeos/system/OWNERS b/chromeos/system/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/system/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/chromeos/test/OWNERS b/chromeos/test/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/test/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/chromeos/third_party/OWNERS b/chromeos/third_party/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/third_party/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/chromeos/tools/OWNERS b/chromeos/tools/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/tools/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/chromeos/tpm/OWNERS b/chromeos/tpm/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/chromeos/tpm/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 75b42beb..6e5d07aa 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -60,6 +60,8 @@
     "obb_mounter/arc_obb_mounter_bridge.h",
     "power/arc_power_bridge.cc",
     "power/arc_power_bridge.h",
+    "print_spooler/arc_print_renderer.cc",
+    "print_spooler/arc_print_renderer.h",
     "property/arc_property_bridge.cc",
     "property/arc_property_bridge.h",
     "rotation_lock/arc_rotation_lock_bridge.cc",
@@ -105,6 +107,7 @@
     "//components/google/core/common",
     "//components/onc",
     "//components/prefs",
+    "//components/printing/common:mojo_interfaces",
     "//components/session_manager/core",
     "//components/timers",
     "//components/url_formatter",
diff --git a/components/arc/mojom/print_spooler.mojom b/components/arc/mojom/print_spooler.mojom
index 21c5b544..7c2964a 100644
--- a/components/arc/mojom/print_spooler.mojom
+++ b/components/arc/mojom/print_spooler.mojom
@@ -2,15 +2,41 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 1
+// Next MinVersion: 2
 
 module arc.mojom;
 
 import "components/arc/mojom/print_common.mojom";
 
+// Allows ARC to interact with the ARC Custom Tab used for printing.
+// Closing the interface will close the ARC Custom Tab.
 // Next method ID: 0
+interface PrintSession {
+};
+
+// Interface used by Chrome to ask ARC to render a print document for Chrome
+// print preview.
+// Next method ID: 0
+interface PrintRendererDelegate {
+  // TODO(jschettler): Add methods to render a print document and signal the
+  // close of Chrome print preview.
+};
+
+// Next method ID: 1
 interface PrintSpoolerHost {
-  // TODO(jschettler): Add methods to open and close Chrome print preview.
+  // Opens the file owned by |scoped_handle| in Chrome print preview in an ARC
+  // Custom Tab.
+  // The |task_id| and |surface_id| specify the Android task and the surface on
+  // which the ARC Custom Tab should be shown.
+  // The |top_margin| is the height of the space at the top of the window.
+  // The returned |session| will be null if errors occur while saving the print
+  // document or locating the Android surface.
+  [MinVersion=1] StartPrintInCustomTab@0(handle scoped_handle,
+                                         int32 task_id,
+                                         int32 surface_id,
+                                         int32 top_margin,
+                                         PrintRendererDelegate delegate)
+      => (PrintSession? session);
 };
 
 // Next method ID: 1
diff --git a/components/arc/print_spooler/DEPS b/components/arc/print_spooler/DEPS
new file mode 100644
index 0000000..58877124
--- /dev/null
+++ b/components/arc/print_spooler/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/printing/common",
+]
diff --git a/components/arc/print_spooler/OWNERS b/components/arc/print_spooler/OWNERS
new file mode 100644
index 0000000..060b5bf
--- /dev/null
+++ b/components/arc/print_spooler/OWNERS
@@ -0,0 +1,2 @@
+bmgordon@chromium.org
+jschettler@chromium.org
diff --git a/components/arc/print_spooler/arc_print_renderer.cc b/components/arc/print_spooler/arc_print_renderer.cc
new file mode 100644
index 0000000..64a3d69
--- /dev/null
+++ b/components/arc/print_spooler/arc_print_renderer.cc
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/arc/print_spooler/arc_print_renderer.h"
+
+#include <utility>
+
+#include "content/public/browser/web_contents.h"
+
+ArcPrintRenderer::ArcPrintRenderer(content::WebContents* web_contents)
+    : binding_(web_contents, this) {}
+
+ArcPrintRenderer::~ArcPrintRenderer() = default;
+
+void ArcPrintRenderer::SetDelegate(
+    arc::mojom::PrintRendererDelegatePtr delegate) {
+  delegate_ = std::move(delegate);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(ArcPrintRenderer)
diff --git a/components/arc/print_spooler/arc_print_renderer.h b/components/arc/print_spooler/arc_print_renderer.h
new file mode 100644
index 0000000..6fb17846
--- /dev/null
+++ b/components/arc/print_spooler/arc_print_renderer.h
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_ARC_PRINT_SPOOLER_ARC_PRINT_RENDERER_H_
+#define COMPONENTS_ARC_PRINT_SPOOLER_ARC_PRINT_RENDERER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/arc/mojom/print_spooler.mojom.h"
+#include "components/printing/common/print.mojom.h"
+#include "content/public/browser/web_contents_binding_set.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+// Implementation of PrintRenderer interface. Allows ARC to render print
+// documents for Chrome print preview.
+class ArcPrintRenderer : public printing::mojom::PrintRenderer,
+                         public content::WebContentsUserData<ArcPrintRenderer> {
+ public:
+  ~ArcPrintRenderer() override;
+
+  // Used to set the PrintRendererDelegate used by the PrintRenderer.
+  void SetDelegate(arc::mojom::PrintRendererDelegatePtr delegate);
+
+ private:
+  explicit ArcPrintRenderer(content::WebContents* web_contents);
+  friend class content::WebContentsUserData<ArcPrintRenderer>;
+
+  // Used to associate the PrintRenderer interface with a RenderFrameHost.
+  content::WebContentsFrameBindingSet<printing::mojom::PrintRenderer> binding_;
+
+  // Used to forward render requests to ARC.
+  arc::mojom::PrintRendererDelegatePtr delegate_;
+
+  // Note: This should remain the last member so it'll be destroyed and
+  // invalidate its weak pointers before any other members are destroyed.
+  base::WeakPtrFactory<ArcPrintRenderer> weak_ptr_factory_{this};
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  DISALLOW_COPY_AND_ASSIGN(ArcPrintRenderer);
+};
+
+#endif  // COMPONENTS_ARC_PRINT_SPOOLER_ARC_PRINT_RENDERER_H_
diff --git a/components/arc/usb/usb_host_bridge.cc b/components/arc/usb/usb_host_bridge.cc
index c0c12d8..6b1400c4 100644
--- a/components/arc/usb/usb_host_bridge.cc
+++ b/components/arc/usb/usb_host_bridge.cc
@@ -214,10 +214,10 @@
   if (delegate_)
     delegate_->AttachDevicesToArcVm();
 
-  // Request UsbDeviceManagerPtr from DeviceService.
-  content::GetSystemConnector()->BindInterface(
-      device::mojom::kServiceName, mojo::MakeRequest(&usb_manager_));
-  usb_manager_.set_connection_error_handler(
+  // Receive mojo::Remote<UsbDeviceManager> from DeviceService.
+  content::GetSystemConnector()->Connect(
+      device::mojom::kServiceName, usb_manager_.BindNewPipeAndPassReceiver());
+  usb_manager_.set_disconnect_handler(
       base::BindOnce(&ArcUsbHostBridge::Disconnect, base::Unretained(this)));
 
   // Listen for added/removed device events.
diff --git a/components/arc/usb/usb_host_bridge.h b/components/arc/usb/usb_host_bridge.h
index afcd3f1..28bf5d89 100644
--- a/components/arc/usb/usb_host_bridge.h
+++ b/components/arc/usb/usb_host_bridge.h
@@ -16,6 +16,7 @@
 #include "components/arc/session/connection_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 #include "services/device/public/mojom/usb_manager_client.mojom.h"
@@ -110,7 +111,7 @@
   mojom::UsbHostHostPtr usb_host_ptr_;
 
   // Connection to the DeviceService for usb manager.
-  device::mojom::UsbDeviceManagerPtr usb_manager_;
+  mojo::Remote<device::mojom::UsbDeviceManager> usb_manager_;
   mojo::AssociatedBinding<device::mojom::UsbDeviceManagerClient>
       client_binding_{this};
 
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
index ba1c4b5..bd4ec2c 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
@@ -11,7 +11,6 @@
 
 #include "base/base64.h"
 #include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "base/pickle.h"
 #include "components/autofill/core/browser/data_model/autofill_metadata.h"
@@ -469,26 +468,10 @@
   for (const auto& it : addresses_metadata) {
     cache_[GetStorageKeyForWalletMetadataTypeAndId(
         WalletMetadataSpecifics::ADDRESS, it.first)] = it.second;
-    // TODO(crbug.com/949034): Consider adding standard functions for recording
-    // large times in seconds/minutes.
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Autofill.WalletUseDateInMinutes.Address",
-        /*sample=*/(AutofillClock::Now() - it.second.use_date).InMinutes(),
-        /*min=*/base::TimeDelta::FromMinutes(1).InMinutes(),
-        /*max=*/base::TimeDelta::FromDays(365).InMinutes(),
-        /*bucket_count=*/50);
   }
   for (const auto& it : cards_metadata) {
     cache_[GetStorageKeyForWalletMetadataTypeAndId(
         WalletMetadataSpecifics::CARD, it.first)] = it.second;
-    // TODO(crbug.com/949034): Consider adding standard functions for recording
-    // large times in seconds/minutes.
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Autofill.WalletUseDateInMinutes.Card",
-        /*sample=*/(AutofillClock::Now() - it.second.use_date).InMinutes(),
-        /*min=*/base::TimeDelta::FromMinutes(1).InMinutes(),
-        /*max=*/base::TimeDelta::FromDays(365).InMinutes(),
-        /*bucket_count=*/50);
   }
 
   // Load the metadata and send to the processor.
@@ -542,7 +525,6 @@
     return;
   }
 
-  int deleted_count = 0;
   std::unique_ptr<MetadataChangeList> metadata_change_list =
       CreateMetadataChangeList();
   for (const std::string storage_key : old_orphan_keys) {
@@ -552,11 +534,8 @@
                              parsed_storage_key.metadata_id)) {
       cache_.erase(storage_key);
       change_processor()->Delete(storage_key, metadata_change_list.get());
-      ++deleted_count;
     }
   }
-  UMA_HISTOGRAM_COUNTS_100("Sync.WalletMetadata.DeletedOldOrphans",
-                           deleted_count);
 
   // Commit the transaction to make sure the data and the metadata is written
   // down (especially on Android where we cannot rely on committing transactions
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
index ed4fadfb..eeae78f 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
@@ -17,7 +17,6 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/data_model/autofill_metadata.h"
@@ -867,7 +866,6 @@
 
 // Verify that old orphan metadata gets deleted on startup.
 TEST_F(AutofillWalletMetadataSyncBridgeTest, DeleteOldOrphanMetadataOnStartup) {
-  base::HistogramTester histogram_tester;
   WalletMetadataSpecifics profile =
       CreateWalletMetadataSpecificsForAddressWithDetails(
           kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/20);
@@ -889,8 +887,6 @@
   EXPECT_CALL(*backend(), CommitChanges());
 
   ResetBridge();
-  histogram_tester.ExpectBucketCount("Sync.WalletMetadata.DeletedOldOrphans",
-                                     /*bucket=*/2, /*count=*/1);
 
   ASSERT_THAT(GetAllLocalDataInclRestart(), IsEmpty());
 }
@@ -898,7 +894,6 @@
 // Verify that recent orphan metadata does not get deleted on startup.
 TEST_F(AutofillWalletMetadataSyncBridgeTest,
        DoNotDeleteOldNonOrphanMetadataOnStartup) {
-  base::HistogramTester histogram_tester;
   WalletMetadataSpecifics profile =
       CreateWalletMetadataSpecificsForAddressWithDetails(
           kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/20);
@@ -918,8 +913,6 @@
   EXPECT_CALL(*backend(), CommitChanges()).Times(0);
 
   ResetBridge();
-  histogram_tester.ExpectTotalCount("Sync.WalletMetadata.DeletedOldOrphans",
-                                    /*count=*/0);
 
   EXPECT_THAT(
       GetAllLocalDataInclRestart(),
@@ -929,7 +922,6 @@
 // Verify that recent orphan metadata does not get deleted on startup.
 TEST_F(AutofillWalletMetadataSyncBridgeTest,
        DoNotDeleteRecentOrphanMetadataOnStartup) {
-  base::HistogramTester histogram_tester;
   WalletMetadataSpecifics profile =
       CreateWalletMetadataSpecificsForAddressWithDetails(
           kAddr1SpecificsId, /*use_count=*/10, /*use_date=*/20);
@@ -948,8 +940,6 @@
   EXPECT_CALL(*backend(), CommitChanges()).Times(0);
 
   ResetBridge();
-  histogram_tester.ExpectTotalCount("Sync.WalletMetadata.DeletedOldOrphans",
-                                    /*count=*/0);
 
   EXPECT_THAT(
       GetAllLocalDataInclRestart(),
diff --git a/components/base32/OWNERS b/components/base32/OWNERS
index 86ef814e..388bb34c 100644
--- a/components/base32/OWNERS
+++ b/components/base32/OWNERS
@@ -1,2 +1,4 @@
 gab@chromium.org
 robpercival@chromium.org
+
+# COMPONENT: Internals
diff --git a/components/exo/display.cc b/components/exo/display.cc
index aad19dd..6d547ef4 100644
--- a/components/exo/display.cc
+++ b/components/exo/display.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
+#include "build/build_config.h"
 #include "components/exo/data_device.h"
 #include "components/exo/file_helper.h"
 #include "components/exo/input_method_surface_manager.h"
@@ -110,9 +111,13 @@
   // Using zero-copy for optimal performance.
   bool use_zero_copy = true;
 
+#if defined(ARCH_CPU_X86_FAMILY)
   // TODO(dcastagna): Re-enable NV12 format as HW overlay once b/113362843
   // is addressed.
   bool is_overlay_candidate = format != gfx::BufferFormat::YUV_420_BIPLANAR;
+#else
+  bool is_overlay_candidate = true;
+#endif
 
   return std::make_unique<Buffer>(
       std::move(gpu_memory_buffer),
diff --git a/components/feedback/anonymizer_tool.cc b/components/feedback/anonymizer_tool.cc
index ae7244e3..ed0974bb 100644
--- a/components/feedback/anonymizer_tool.cc
+++ b/components/feedback/anonymizer_tool.cc
@@ -58,10 +58,13 @@
 
     // Serial numbers. The actual serial number itself can include any alphanum
     // char as well as dashes, periods, colons, slashes and unprintable ASCII
-    // chars (except newline).
+    // chars (except newline). The second one is for a special case in
+    // edid-decode, where if we genericized it further then we would catch too
+    // many other cases that we don't want to anonymize.
     {"Serial",
      "(?i-s)(\\bserial\\s*_?(?:number)?['\"]?\\s*[:=]\\s*['\"]?)"
      "([0-9a-zA-Z\\-.:\\/\\\\\\x00-\\x09\\x0B-\\x1F]+)(\\b)"},
+    {"Serial", "( Serial Number )(\\d+)(\\b)"},
 
     // GAIA IDs
     {"GAIA", R"xxx((\"?\bgaia_id\"?[=:]['\"])(\d+)(\b['\"]))xxx"},
diff --git a/components/feedback/anonymizer_tool_unittest.cc b/components/feedback/anonymizer_tool_unittest.cc
index 686ac80..853338dd 100644
--- a/components/feedback/anonymizer_tool_unittest.cc
+++ b/components/feedback/anonymizer_tool_unittest.cc
@@ -193,6 +193,12 @@
             AnonymizeCustomPatterns("SerialNumber: 5:00:14.0"));
   EXPECT_EQ("Serial: <Serial: 6>",
             AnonymizeCustomPatterns("Serial: ABCEFG\x01kjmn-as:342/234\\432"));
+  // Don't overly anonymize serial numbers, we only do this for a specific
+  // formatting case for edid-decode.
+  EXPECT_EQ("Foo serial number 123",
+            AnonymizeCustomPatterns("Foo serial number 123"));
+  EXPECT_EQ("Foo Serial Number <Serial: 7>",
+            AnonymizeCustomPatterns("Foo Serial Number 123"));
 
   EXPECT_EQ("\"gaia_id\":\"<GAIA: 1>\"",
             AnonymizeCustomPatterns("\"gaia_id\":\"1234567890\""));
diff --git a/components/login/OWNERS b/components/login/OWNERS
index b280edc9..f9350fc 100644
--- a/components/login/OWNERS
+++ b/components/login/OWNERS
@@ -1,3 +1,4 @@
 achuith@chromium.org
 alemate@chromium.org
 xiyuan@chromium.org
+# COMPONENT: UI>Shell>OOBE
diff --git a/components/omnibox/browser/document_suggestions_service.cc b/components/omnibox/browser/document_suggestions_service.cc
index 7b8c5f7..03cb102d 100644
--- a/components/omnibox/browser/document_suggestions_service.cc
+++ b/components/omnibox/browser/document_suggestions_service.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/feature_list.h"
+#include "base/i18n/rtl.h"
 #include "base/json/json_writer.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/field_trial_params.h"
@@ -28,14 +29,16 @@
 
 namespace {
 
-// Builds a document search request body. Inputs are:
-//   |query|: Current query text.
+// Builds a document search request body. Inputs that affect the request are:
+//   |query|: Current omnibox query text, passed as an argument.
 // The format of the request is:
 //     {
-//       query: "the search text",
+//       query: "|query|",
 //       start: 0,
 //       pageSize: 10,
-//       sourceOptions: [{source: {predefinedSource: "GOOGLE_DRIVE"}}]
+//       requestOptions: {
+//            searchApplicationId: "searchapplications/chrome",
+//       }
 //     }
 std::string BuildDocumentSuggestionRequest(const base::string16& query) {
   base::Value root(base::Value::Type::DICTIONARY);
@@ -43,13 +46,10 @@
   root.SetKey("start", base::Value(0));
   root.SetKey("pageSize", base::Value(10));
 
-  base::Value::ListStorage storage_options_list;
-  base::Value source_definition(base::Value::Type::DICTIONARY);
-  source_definition.SetPath({"source", "predefinedSource"},
-                            base::Value("GOOGLE_DRIVE"));
-  storage_options_list.emplace_back(std::move(source_definition));
-  root.SetKey("dataSourceRestrictions",
-              base::Value(std::move(storage_options_list)));
+  base::Value request_options(base::Value::Type::DICTIONARY);
+  request_options.SetKey("searchApplicationId",
+                         base::Value("searchapplications/chrome"));
+  root.SetKey("requestOptions", std::move(request_options));
 
   std::string result;
   base::JSONWriter::Write(root, &result);
diff --git a/components/policy/core/browser/browser_policy_connector.cc b/components/policy/core/browser/browser_policy_connector.cc
index 7993291..91ae55c 100644
--- a/components/policy/core/browser/browser_policy_connector.cc
+++ b/components/policy/core/browser/browser_policy_connector.cc
@@ -183,12 +183,6 @@
       policy_prefs::kCloudManagementEnrollmentMandatory, false);
   registry->RegisterBooleanPref(
       policy_prefs::kCloudPolicyOverridesPlatformPolicy, false);
-  registry->RegisterBooleanPref(policy_prefs::kUnsafeEventsReportingEnabled,
-                                false);
-  registry->RegisterIntegerPref(policy_prefs::kBlockLargeFileTransfer, 0);
-  registry->RegisterIntegerPref(policy_prefs::kDelayDeliveryUntilVerdict, 0);
-  registry->RegisterIntegerPref(policy_prefs::kAllowPasswordProtectedFiles, 0);
-  registry->RegisterIntegerPref(policy_prefs::kCheckContentCompliance, 0);
 }
 
 }  // namespace policy
diff --git a/components/policy/core/common/policy_pref_names.cc b/components/policy/core/common/policy_pref_names.cc
index 336aaa8f..c5c3c58 100644
--- a/components/policy/core/common/policy_pref_names.cc
+++ b/components/policy/core/common/policy_pref_names.cc
@@ -39,24 +39,5 @@
 // machine policy.
 const char kCloudPolicyOverridesPlatformPolicy[] = "policy.cloud_override";
 
-// Boolean that indidicates if Chrome reports unsafe events to Google.
-const char kUnsafeEventsReportingEnabled[] = "policy.unsafe_events_reporting";
-
-// Integer that specifies if large files are blocked form either uploads or
-// downloads or both.
-const char kBlockLargeFileTransfer[] = "policy.block_large_file_transfers";
-
-// Integer that specifies if delivery to the user of potentially unsafe data
-// is delayed until a verdict about the data is known.
-const char kDelayDeliveryUntilVerdict[] = "policy.delay_delivery_until_verdict";
-
-// Integer that specifies if password protected files can be either uploaded
-// or downloaded or both.
-const char kAllowPasswordProtectedFiles[] =
-    "policy.allow_password_protected_files";
-
-// Boolean that indidicates if Chrome checks data for content compliance.
-const char kCheckContentCompliance[] = "policy.check_content_compliance";
-
 }  // namespace policy_prefs
 }  // namespace policy
diff --git a/components/policy/core/common/policy_pref_names.h b/components/policy/core/common/policy_pref_names.h
index 23704a3..ed1a9b1 100644
--- a/components/policy/core/common/policy_pref_names.h
+++ b/components/policy/core/common/policy_pref_names.h
@@ -18,11 +18,6 @@
 POLICY_EXPORT extern const char kUserPolicyRefreshRate[];
 POLICY_EXPORT extern const char kCloudManagementEnrollmentMandatory[];
 POLICY_EXPORT extern const char kCloudPolicyOverridesPlatformPolicy[];
-POLICY_EXPORT extern const char kUnsafeEventsReportingEnabled[];
-POLICY_EXPORT extern const char kBlockLargeFileTransfer[];
-POLICY_EXPORT extern const char kDelayDeliveryUntilVerdict[];
-POLICY_EXPORT extern const char kAllowPasswordProtectedFiles[];
-POLICY_EXPORT extern const char kCheckContentCompliance[];
 
 }  // namespace policy_prefs
 }  // namespace policy
diff --git a/components/printing/common/BUILD.gn b/components/printing/common/BUILD.gn
index 40da037..a6698e1 100644
--- a/components/printing/common/BUILD.gn
+++ b/components/printing/common/BUILD.gn
@@ -2,6 +2,8 @@
 # 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")
+
 static_library("common") {
   sources = [
     "cloud_print_cdd_conversion.cc",
@@ -24,3 +26,13 @@
     "//ui/gfx/ipc/skia",
   ]
 }
+
+mojom("mojo_interfaces") {
+  sources = [
+    "print.mojom",
+  ]
+
+  public_deps = [
+    "//mojo/public/mojom/base",
+  ]
+}
diff --git a/components/printing/common/OWNERS b/components/printing/common/OWNERS
index c894089..e840734 100644
--- a/components/printing/common/OWNERS
+++ b/components/printing/common/OWNERS
@@ -4,6 +4,9 @@
 per-file *_messages.cc=set noparent
 per-file *_messages.cc=file://ipc/SECURITY_OWNERS
 
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+
 per-file *_param_traits*.*=set noparent
 per-file *_param_traits*.*=file://ipc/SECURITY_OWNERS
 # COMPONENT: Internals>Printing
diff --git a/components/printing/common/print.mojom b/components/printing/common/print.mojom
new file mode 100644
index 0000000..e977a3b
--- /dev/null
+++ b/components/printing/common/print.mojom
@@ -0,0 +1,12 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module printing.mojom;
+
+// Interface implemented by a class that desires to render print documents for
+// Chrome print preview.
+interface PrintRenderer {
+  // TODO(jschettler): Add methods to render a print document and signal the
+  // close of Chrome print preview.
+};
diff --git a/components/safe_browsing/DEPS b/components/safe_browsing/DEPS
index e8a169b2..74f5a3b 100644
--- a/components/safe_browsing/DEPS
+++ b/components/safe_browsing/DEPS
@@ -19,6 +19,7 @@
   "+net/traffic_annotation",
   "+net/url_request",
   "+services/network/public",
+  "+services/network/test",
   "+services/service_manager/public",
   "+testing/gtest",
   "+third_party/blink/public/common/loader/url_loader_throttle.h",
diff --git a/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc b/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc
index 236acbba..5f83d51a 100644
--- a/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc
+++ b/components/safe_browsing/browser/safe_browsing_url_checker_impl.cc
@@ -289,7 +289,8 @@
     auto* rt_lookup_service = database_manager_->GetRealTimeUrlLookupService();
     if (RealTimePolicyEngine::CanPerformFullURLLookupForResourceType(
             resource_type_) &&
-        rt_lookup_service && !rt_lookup_service->IsInBackoffMode()) {
+        rt_lookup_service && rt_lookup_service->CanCheckUrl(url) &&
+        !rt_lookup_service->IsInBackoffMode()) {
       UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.RT.ResourceTypes.Checked",
                                 resource_type_);
       safe_synchronously = false;
diff --git a/components/safe_browsing/common/safe_browsing_prefs.cc b/components/safe_browsing/common/safe_browsing_prefs.cc
index 9b0c88e..49ef7d96 100644
--- a/components/safe_browsing/common/safe_browsing_prefs.cc
+++ b/components/safe_browsing/common/safe_browsing_prefs.cc
@@ -112,6 +112,15 @@
     "safebrowsing.real_time_lookup_enabled";
 const char kSafeBrowsingSendFilesForMalwareCheck[] =
     "safebrowsing.send_files_for_malware_check";
+const char kUnsafeEventsReportingEnabled[] = "policy.unsafe_events_reporting";
+const char kBlockLargeFileTransfer[] = "policy.block_large_file_transfers";
+const char kDelayDeliveryUntilVerdict[] = "policy.delay_delivery_until_verdict";
+const char kAllowPasswordProtectedFiles[] =
+    "policy.allow_password_protected_files";
+const char kCheckContentCompliance[] = "policy.check_content_compliance";
+const char kDomainsToCheckComplianceOfDownloadedContent[] =
+    "policy.domains_to_check_compliance_of_downloaded_content";
+
 }  // namespace prefs
 
 namespace safe_browsing {
@@ -182,6 +191,11 @@
 
 void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
   registry->RegisterDictionaryPref(prefs::kSafeBrowsingTriggerEventTimestamps);
+  registry->RegisterBooleanPref(prefs::kUnsafeEventsReportingEnabled, false);
+  registry->RegisterIntegerPref(prefs::kBlockLargeFileTransfer, 0);
+  registry->RegisterIntegerPref(prefs::kDelayDeliveryUntilVerdict, 0);
+  registry->RegisterIntegerPref(prefs::kAllowPasswordProtectedFiles, 0);
+  registry->RegisterIntegerPref(prefs::kCheckContentCompliance, 0);
 }
 
 void SetExtendedReportingPrefAndMetric(
diff --git a/components/safe_browsing/common/safe_browsing_prefs.h b/components/safe_browsing/common/safe_browsing_prefs.h
index 2fd8108..b912f70 100644
--- a/components/safe_browsing/common/safe_browsing_prefs.h
+++ b/components/safe_browsing/common/safe_browsing_prefs.h
@@ -91,6 +91,28 @@
 // Whether or not to send downloads to Safe Browsing for deep scanning. This
 // is configured by enterprise policy.
 extern const char kSafeBrowsingSendFilesForMalwareCheck[];
+
+// Boolean that indidicates if Chrome reports unsafe events to Google.
+extern const char kUnsafeEventsReportingEnabled[];
+
+// Integer that specifies if large files are blocked form either uploads or
+// downloads or both.
+extern const char kBlockLargeFileTransfer[];
+
+// Integer that specifies if delivery to the user of potentially unsafe data
+// is delayed until a verdict about the data is known.
+extern const char kDelayDeliveryUntilVerdict[];
+
+// Integer that specifies if password protected files can be either uploaded
+// or downloaded or both.
+extern const char kAllowPasswordProtectedFiles[];
+
+// Integer that indidicates if Chrome checks data for content compliance.
+extern const char kCheckContentCompliance[];
+
+// List of domains where Chrome should check compliance of downloaded files.
+extern const char kDomainsToCheckComplianceOfDownloadedContent[];
+
 }  // namespace prefs
 
 namespace safe_browsing {
@@ -138,6 +160,23 @@
   PASSWORD_PROTECTION_TRIGGER_MAX,
 };
 
+// Enum representing possible values of the SendFilesForMalwareCheck policy.
+// This must be kept in sync with policy_templates.json
+enum SendFilesForMalwareCheckValues {
+  DO_NOT_SCAN = 0,
+  SEND_FILES_DISABLED = 1,
+  SEND_DOWNLOADS = 2,
+};
+
+// Enum representing possible values of the CheckContentCompliance policy. This
+// must be kept in sync with policy_templates.json
+enum CheckContentComplianceValues {
+  NONE = 0,
+  CHECK_DOWNLOADS = 1,
+  CHECK_UPLOADS = 2,
+  CHECK_UPLOADS_AND_DOWNLOADS = 3,
+};
+
 // Returns whether the currently active Safe Browsing Extended Reporting
 // preference exists (eg: has been set before).
 bool ExtendedReportingPrefExists(const PrefService& prefs);
diff --git a/components/safe_browsing/realtime/BUILD.gn b/components/safe_browsing/realtime/BUILD.gn
index 91c2c9ca..2d90ba3 100644
--- a/components/safe_browsing/realtime/BUILD.gn
+++ b/components/safe_browsing/realtime/BUILD.gn
@@ -38,11 +38,16 @@
   testonly = true
   sources = [
     "policy_engine_unittest.cc",
+    "url_lookup_service_unittest.cc",
   ]
   deps = [
     ":policy_engine",
+    ":url_lookup_service",
     "//base/test:test_support",
     "//components/safe_browsing:features",
+    "//content/test:test_support",
+    "//services/network:test_support",
+    "//services/network/public/cpp:cpp",
     "//testing/gtest",
   ]
 }
diff --git a/components/safe_browsing/realtime/url_lookup_service.cc b/components/safe_browsing/realtime/url_lookup_service.cc
index 5279bde..e765c89 100644
--- a/components/safe_browsing/realtime/url_lookup_service.cc
+++ b/components/safe_browsing/realtime/url_lookup_service.cc
@@ -5,6 +5,7 @@
 #include "components/safe_browsing/realtime/url_lookup_service.h"
 
 #include "base/base64url.h"
+#include "base/time/time.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/load_flags.h"
@@ -23,6 +24,12 @@
 const char kRealTimeLookupUrlPrefix[] =
     "http://localhost:8000/safebrowsing/clientreport/realtime";
 
+const size_t kMaxFailuresToEnforceBackoff = 3;
+
+const size_t kBackOffResetDurationInSeconds = 5 * 60;  // 5 minutes.
+
+const size_t kURLLookupTimeoutDurationInSeconds = 1 * 60;  // 1 minute.
+
 }  // namespace
 
 RealTimeUrlLookupService::RealTimeUrlLookupService(
@@ -85,12 +92,12 @@
                                        traffic_annotation);
   network::SimpleURLLoader* loader = owned_loader.get();
   owned_loader->AttachStringForUpload(req_data, "application/octet-stream");
+  owned_loader->SetTimeoutDuration(
+      base::TimeDelta::FromSeconds(kURLLookupTimeoutDurationInSeconds));
   owned_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
       url_loader_factory_.get(),
       base::BindOnce(&RealTimeUrlLookupService::OnURLLoaderComplete,
-                     base::Unretained(this), loader));
-
-  // TODO(crbug.com/992099): Implement timeout and backoff.
+                     GetWeakPtr(), loader));
 
   pending_requests_[owned_loader.release()] = std::move(callback);
 }
@@ -121,24 +128,54 @@
       "SafeBrowsing.RT.Network.Result", net_error, response_code);
 
   auto response = std::make_unique<RTLookupResponse>();
-  if (net_error == net::OK && response_code == net::HTTP_OK) {
-    response->ParseFromString(*response_body);
-  } else {
-    HandleResponseError();
-  }
+  bool success = (net_error == net::OK) && (response_code == net::HTTP_OK) &&
+                 response->ParseFromString(*response_body);
+  success ? HandleLookupSuccess() : HandleLookupError();
 
   std::move(it->second).Run(std::move(response));
   delete it->first;
   pending_requests_.erase(it);
 }
 
-void RealTimeUrlLookupService::HandleResponseError() {
-  // TODO(crbug.com/992099): Implement a backoff mechanism.
+bool RealTimeUrlLookupService::CanCheckUrl(const GURL& url) const {
+  return url.SchemeIsHTTPOrHTTPS();
+}
+
+void RealTimeUrlLookupService::ExitBackoff() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  ResetFailures();
+}
+
+void RealTimeUrlLookupService::HandleLookupError() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  consecutive_failures_++;
+
+  if (IsInBackoffMode()) {
+    reset_backoff_timer_.Stop();
+    reset_backoff_timer_.Start(
+        FROM_HERE, base::TimeDelta::FromSeconds(kBackOffResetDurationInSeconds),
+        this, &RealTimeUrlLookupService::ExitBackoff);
+  }
+}
+
+void RealTimeUrlLookupService::HandleLookupSuccess() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  ResetFailures();
 }
 
 bool RealTimeUrlLookupService::IsInBackoffMode() {
-  // TODO(crbug.com/992099): Implement a backoff mechanism.
-  return false;
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  return consecutive_failures_ >= kMaxFailuresToEnforceBackoff;
+}
+
+void RealTimeUrlLookupService::ResetFailures() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  consecutive_failures_ = 0;
+  reset_backoff_timer_.Stop();
+}
+
+base::WeakPtr<RealTimeUrlLookupService> RealTimeUrlLookupService::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
 }
 
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/realtime/url_lookup_service.h b/components/safe_browsing/realtime/url_lookup_service.h
index 7c695bb..7cb6423 100644
--- a/components/safe_browsing/realtime/url_lookup_service.h
+++ b/components/safe_browsing/realtime/url_lookup_service.h
@@ -10,6 +10,8 @@
 
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
 #include "components/safe_browsing/proto/realtimeapi.pb.h"
 #include "url/gurl.h"
 
@@ -31,30 +33,65 @@
       scoped_refptr<network::SharedURLLoaderFactory>);
   ~RealTimeUrlLookupService();
 
-  void StartLookup(const GURL& url, RTLookupResponseCallback callback);
+  // Returns true if |url|'s scheme can be checked.
+  bool CanCheckUrl(const GURL& url) const;
 
   // Returns true if the real time lookups are currently in backoff mode due to
   // too many prior errors. If this happens, the checking falls back to
   // local hash-based method.
   bool IsInBackoffMode();
 
+  // Start the full URL lookup for |url| and call |callback| on the same thread
+  // when done.
+  void StartLookup(const GURL& url, RTLookupResponseCallback callback);
+
  private:
   using PendingRTLookupRequests =
       base::flat_map<network::SimpleURLLoader*, RTLookupResponseCallback>;
 
   // Called when the request to remote endpoint fails. May initiate or extend
   // backoff.
-  void HandleResponseError();
+  void HandleLookupError();
+
+  // Called when the request to remote endpoint succeeds. Resets error count and
+  // ends backoff.
+  void HandleLookupSuccess();
+
+  // Resets the error count and ends backoff mode. Functionally same as
+  // |HandleLookupSuccess| for now.
+  void ResetFailures();
+
+  // Called when the timer to end backoff mode fires. Resets error count.
+  void ExitBackoff();
 
   // Called when the response from the real-time lookup remote endpoint is
   // received.
   void OnURLLoaderComplete(network::SimpleURLLoader* url_loader,
                            std::unique_ptr<std::string> response_body);
 
+  // Helper function to return a weak pointer.
+  base::WeakPtr<RealTimeUrlLookupService> GetWeakPtr();
+
   PendingRTLookupRequests pending_requests_;
 
+  // Count of consecutive failures to complete URL lookup requests. When it
+  // reaches |kMaxFailuresToEnforceBackoff|, we enter the backoff mode. It gets
+  // reset when we complete a lookup successfully or when the backoff reset
+  // timer fires.
+  size_t consecutive_failures_ = 0;
+
+  // Started when we enter backoff. We exit the backoff mode when this fires.
+  base::OneShotTimer reset_backoff_timer_;
+
   // The URLLoaderFactory we use to issue network requests.
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  friend class RealTimeUrlLookupServiceTest;
+
+  base::WeakPtrFactory<RealTimeUrlLookupService> weak_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(RealTimeUrlLookupService);
+
 };  // class RealTimeUrlLookupService
 
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/realtime/url_lookup_service_unittest.cc b/components/safe_browsing/realtime/url_lookup_service_unittest.cc
new file mode 100644
index 0000000..155b66e
--- /dev/null
+++ b/components/safe_browsing/realtime/url_lookup_service_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/safe_browsing/realtime/url_lookup_service.h"
+
+#include "base/test/task_environment.h"
+#include "content/public/test/browser_task_environment.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/platform_test.h"
+
+namespace safe_browsing {
+
+class RealTimeUrlLookupServiceTest : public PlatformTest {
+ public:
+  RealTimeUrlLookupServiceTest()
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+  void SetUp() override {
+    PlatformTest::SetUp();
+
+    test_shared_loader_factory_ =
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_);
+
+    rt_service_ =
+        std::make_unique<RealTimeUrlLookupService>(test_shared_loader_factory_);
+  }
+
+  void HandleLookupError() { rt_service_->HandleLookupError(); }
+  void HandleLookupSuccess() { rt_service_->HandleLookupSuccess(); }
+  bool IsInBackoffMode() { return rt_service_->IsInBackoffMode(); }
+
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+  std::unique_ptr<RealTimeUrlLookupService> rt_service_;
+  content::BrowserTaskEnvironment task_environment_;
+};
+
+TEST_F(RealTimeUrlLookupServiceTest, TestBackoffAndTimerReset) {
+  // Not in backoff at the beginning.
+  ASSERT_FALSE(IsInBackoffMode());
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 2: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 3: Entered backoff.
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 1 second.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff not reset after 299 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(298));
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Backoff should have been reset after 300 seconds.
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+  EXPECT_FALSE(IsInBackoffMode());
+}
+
+TEST_F(RealTimeUrlLookupServiceTest, TestBackoffAndLookupSuccessReset) {
+  // Not in backoff at the beginning.
+  ASSERT_FALSE(IsInBackoffMode());
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Lookup success resets the backoff counter.
+  HandleLookupSuccess();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 2: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Lookup success resets the backoff counter.
+  HandleLookupSuccess();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 1: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 2: No backoff.
+  HandleLookupError();
+  EXPECT_FALSE(IsInBackoffMode());
+
+  // Failure 3: Entered backoff.
+  HandleLookupError();
+  EXPECT_TRUE(IsInBackoffMode());
+
+  // Lookup success resets the backoff counter.
+  HandleLookupSuccess();
+  EXPECT_FALSE(IsInBackoffMode());
+}
+
+}  // namespace safe_browsing
diff --git a/components/session_manager/OWNERS b/components/session_manager/OWNERS
index b280edc9..22db12b 100644
--- a/components/session_manager/OWNERS
+++ b/components/session_manager/OWNERS
@@ -1,3 +1,4 @@
 achuith@chromium.org
 alemate@chromium.org
 xiyuan@chromium.org
+# COMPONENT: UI>Shell
diff --git a/components/session_manager/core/OWNERS b/components/session_manager/core/OWNERS
new file mode 100644
index 0000000..036a36bd
--- /dev/null
+++ b/components/session_manager/core/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: UI>Shell
diff --git a/components/web_resource/OWNERS b/components/web_resource/OWNERS
index 0173e25..d029a52 100644
--- a/components/web_resource/OWNERS
+++ b/components/web_resource/OWNERS
@@ -2,5 +2,6 @@
 rsesek@chromium.org
 
 # For ResourceRequestAllowedNotifier and EulaAcceptedNotifier:
-per-file eula_accepted_notifier*=file://components/variations/OWNERS 
-per-file resource_request_allowed_notifier*=file://components/variations/OWNERS 
+per-file eula_accepted_notifier*=file://components/variations/OWNERS
+per-file resource_request_allowed_notifier*=file://components/variations/OWNERS
+# COMPONENT: UI>Shell
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
index 8e75489..ac47f93 100644
--- a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
@@ -201,39 +201,6 @@
   }
 }
 
-static AtkObject* GetParentFrameIfToplevelDocument(AtkObject* object) {
-  while (object) {
-    if (atk_object_get_role(object) == ATK_ROLE_DOCUMENT_WEB)
-      return nullptr;
-    if (atk_object_get_role(object) == ATK_ROLE_FRAME)
-      return object;
-    object = atk_object_get_parent(object);
-  }
-  return nullptr;
-}
-
-static void EstablishEmbeddedRelationship(AtkObject* document_object) {
-  if (!document_object)
-    return;
-
-  AtkObject* window =
-      GetParentFrameIfToplevelDocument(atk_object_get_parent(document_object));
-  if (!window)
-    return;
-
-  ui::AXPlatformNodeAuraLinux* window_platform_node =
-      static_cast<ui::AXPlatformNodeAuraLinux*>(
-          ui::AXPlatformNode::FromNativeViewAccessible(window));
-  ui::AXPlatformNodeAuraLinux* document_platform_node =
-      static_cast<ui::AXPlatformNodeAuraLinux*>(
-          ui::AXPlatformNode::FromNativeViewAccessible(document_object));
-  if (!window_platform_node || !document_platform_node)
-    return;
-
-  window_platform_node->SetEmbeddedDocument(document_object);
-  document_platform_node->SetEmbeddingWindow(window);
-}
-
 void BrowserAccessibilityManagerAuraLinux::OnNodeDataWillChange(
     ui::AXTree* tree,
     const ui::AXNodeData& old_node_data,
@@ -274,14 +241,6 @@
   BrowserAccessibilityManager::OnAtomicUpdateFinished(tree, root_changed,
                                                       changes);
 
-  // Ideally we would like to do this only when `root_changed` is true, but it
-  // seems that our parent ATK frame can switch between multiple
-  // BrowserAccessibilityManagers with no way to detect that here. Instead
-  // whenever an update happens we reestablish the relationship to our parent
-  // frame.
-  if (GetRoot() && GetRoot()->IsNative() && IsRootTree())
-    EstablishEmbeddedRelationship(GetRoot()->GetNativeViewAccessible());
-
   // This is the second step in what will be a three step process mirroring that
   // used in BrowserAccessibilityManagerWin.
   for (const auto& change : changes) {
diff --git a/content/browser/background_sync/periodic_background_sync_browsertest.cc b/content/browser/background_sync/periodic_background_sync_browsertest.cc
index 8ff2874..5efcf61 100644
--- a/content/browser/background_sync/periodic_background_sync_browsertest.cc
+++ b/content/browser/background_sync/periodic_background_sync_browsertest.cc
@@ -15,7 +15,7 @@
   PeriodicBackgroundSyncBrowserTest() {}
   ~PeriodicBackgroundSyncBrowserTest() override {}
 
-  void SetUpOnMainThread() override;
+  void SetUp() override;
   bool Register(const std::string& tag, int min_interval_ms);
   bool RegisterNoMinInterval(const std::string& tag);
   bool RegisterFromServiceWorker(const std::string& tag, int min_interval_ms);
@@ -33,9 +33,9 @@
   DISALLOW_COPY_AND_ASSIGN(PeriodicBackgroundSyncBrowserTest);
 };
 
-void PeriodicBackgroundSyncBrowserTest::SetUpOnMainThread() {
+void PeriodicBackgroundSyncBrowserTest::SetUp() {
   scoped_feature_list_.InitAndEnableFeature(features::kPeriodicBackgroundSync);
-  BackgroundSyncBaseBrowserTest::SetUpOnMainThread();
+  BackgroundSyncBaseBrowserTest::SetUp();
 }
 
 bool PeriodicBackgroundSyncBrowserTest::Register(const std::string& tag,
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index d97e75b..d60e7f7 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -202,6 +202,7 @@
 
 #if defined(OS_WIN)
 #include "media/device_monitors/system_message_window_win.h"
+#include "sandbox/win/src/process_mitigations.h"
 #elif defined(OS_LINUX) && defined(USE_UDEV)
 #include "media/device_monitors/device_monitor_udev.h"
 #elif defined(OS_MACOSX)
@@ -388,6 +389,15 @@
 }
 #endif  // defined(OS_CHROMEOS)
 
+#if defined(OS_WIN)
+// Disable dynamic code using ACG. Prevents the browser process from generating
+// dynamic code or modifying executable code. See comments in
+// sandbox/win/src/security_level.h. Only available on Windows 10 RS1 (1607,
+// Build 14393) onwards.
+const base::Feature kBrowserDynamicCodeDisabled{
+    "BrowserDynamicCodeDisabled", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif  // defined(OS_WIN)
+
 }  // namespace
 
 #if defined(USE_X11)
@@ -637,6 +647,15 @@
   InitDefaultJob();
 #endif
 
+#if defined(OS_WIN)
+  if (!parsed_command_line_.HasSwitch(switches::kSingleProcess)) {
+    if (base::FeatureList::IsEnabled(kBrowserDynamicCodeDisabled)) {
+      sandbox::ApplyProcessMitigationsToCurrentProcess(
+          sandbox::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT);
+    }
+  }
+#endif
+
   if (parsed_command_line_.HasSwitch(switches::kRendererProcessLimit)) {
     std::string limit_string = parsed_command_line_.GetSwitchValueASCII(
         switches::kRendererProcessLimit);
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index 6092cc0..8d9adb3 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -399,9 +399,12 @@
       DevToolsNetworkInterceptor::TakeResponseBodyPipeCallback callback) {
     auto it = jobs_.find(interception_id);
     if (it == jobs_.end()) {
-      std::move(callback).Run(
-          protocol::Response::InvalidParams("Invalid InterceptionId."),
-          mojo::ScopedDataPipeConsumerHandle(), std::string());
+      base::PostTask(
+          FROM_HERE, {BrowserThread::UI},
+          base::BindOnce(
+              std::move(callback),
+              protocol::Response::InvalidParams("Invalid InterceptionId."),
+              mojo::ScopedDataPipeConsumerHandle(), std::string()));
       return;
     }
     it->second->TakeResponseBodyPipe(std::move(callback));
diff --git a/content/browser/devtools/protocol/fetch_handler.cc b/content/browser/devtools/protocol/fetch_handler.cc
index f6bf824..28f654d 100644
--- a/content/browser/devtools/protocol/fetch_handler.cc
+++ b/content/browser/devtools/protocol/fetch_handler.cc
@@ -14,6 +14,7 @@
 #include "content/browser/devtools/devtools_stream_pipe.h"
 #include "content/browser/devtools/devtools_url_loader_interceptor.h"
 #include "content/browser/devtools/protocol/network_handler.h"
+#include "content/public/browser/browser_thread.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
@@ -324,6 +325,7 @@
     Response response,
     mojo::ScopedDataPipeConsumerHandle pipe,
     const std::string& mime_type) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(response.isSuccess(), pipe.is_valid());
   if (!response.isSuccess()) {
     callback->sendFailure(std::move(response));
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index dee82b0..0ca38a0 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1899,6 +1899,7 @@
     Response response,
     mojo::ScopedDataPipeConsumerHandle pipe,
     const std::string& mime_type) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(response.isSuccess(), pipe.is_valid());
   if (!response.isSuccess()) {
     callback->sendFailure(std::move(response));
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 3386edc..7a89da2 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -2189,12 +2189,11 @@
   UpdateCommitNavigationParamsHistory();
   frame_tree_node_->TransferNavigationRequestOwnership(render_frame_host_);
   // Error pages commit in an opaque origin in the renderer process. If this
-  // NavigationRequest resulted in committing an error page, just clear the
-  // |origin_to_commit| and let the renderer process calculate the origin.
-  // TODO(nasko): Create an opque origin here and pass it for the renderer
-  // to commit into it. Potentially also make it an opaque origin derived from
-  // the error page URL, so it can be checked at DidCommit processing.
-  commit_params_->origin_to_commit.reset();
+  // NavigationRequest resulted in committing an error page, set
+  // |origin_to_commit| to an opaque origin that has precursor information
+  // consistent with the URL being requested.
+  commit_params_->origin_to_commit =
+      url::Origin::Create(common_params_->url).DeriveNewOpaqueOrigin();
   if (IsPerNavigationMojoInterfaceEnabled() && request_navigation_client_ &&
       request_navigation_client_.is_bound()) {
     if (associated_site_instance_id_ ==
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index af0afc9..5198803 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -174,6 +174,40 @@
   DISALLOW_COPY_AND_ASSIGN(RequestBlockingNavigationThrottle);
 };
 
+// Helper function for error page navigations that makes sure that the last
+// committed origin on |node| is an opaque origin with a precursor that matches
+// |url|'s origin.
+// Returns true if the frame has an opaque origin with the expected precursor
+// information. Otherwise returns false.
+bool IsOriginOpaqueAndCompatibleWithURL(FrameTreeNode* node, const GURL& url) {
+  url::Origin frame_origin =
+      node->current_frame_host()->GetLastCommittedOrigin();
+
+  if (!frame_origin.opaque()) {
+    LOG(ERROR) << "Frame origin was not opaque. " << frame_origin;
+    return false;
+  }
+
+  const GURL url_origin = url.GetOrigin();
+  const GURL precursor_origin =
+      frame_origin.GetTupleOrPrecursorTupleIfOpaque().GetURL();
+  if (url_origin != precursor_origin) {
+    LOG(ERROR) << "url_origin '" << url_origin << "' !=  precursor_origin '"
+               << precursor_origin << "'";
+    return false;
+  }
+  return true;
+}
+
+bool IsMainFrameOriginOpaqueAndCompatibleWithURL(Shell* shell,
+                                                 const GURL& url) {
+  return IsOriginOpaqueAndCompatibleWithURL(
+      static_cast<WebContentsImpl*>(shell->web_contents())
+          ->GetFrameTree()
+          ->root(),
+      url);
+}
+
 }  // anonymous namespace
 
 class RenderFrameHostManagerTest : public ContentBrowserTest {
@@ -4377,6 +4411,8 @@
     EXPECT_EQ(
         GURL(kUnreachableWebDataURL),
         policy->GetOriginLock(error_site_instance->GetProcess()->GetID()));
+    EXPECT_TRUE(
+        IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
   }
 
   // Navigate successfully again to a document, then perform a
@@ -4450,6 +4486,7 @@
       child->current_frame_host()->GetSiteInstance();
   EXPECT_EQ(success_site_instance, error_site_instance);
   EXPECT_NE(GURL(kUnreachableWebDataURL), error_site_instance->GetSiteURL());
+  EXPECT_TRUE(IsOriginOpaqueAndCompatibleWithURL(child, error_url));
 }
 
 // Test to verify that navigations in new window, which result in an error
@@ -4488,6 +4525,8 @@
   EXPECT_EQ(GURL(kUnreachableWebDataURL),
             ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(
                 error_site_instance->GetProcess()->GetID()));
+  EXPECT_TRUE(
+      IsMainFrameOriginOpaqueAndCompatibleWithURL(new_shell, error_url));
 }
 
 // Test to verify that windows that are not part of the same
@@ -4513,6 +4552,8 @@
     EXPECT_TRUE(observer.is_error());
     EXPECT_EQ(net::ERR_DNS_TIMED_OUT, observer.net_error_code());
     EXPECT_EQ(GURL(kUnreachableWebDataURL), error_site_instance->GetSiteURL());
+    EXPECT_TRUE(
+        IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
   }
 
   // Creat a new, unrelated, window, navigate it to an error page and
@@ -4526,6 +4567,8 @@
     EXPECT_TRUE(observer.is_error());
     EXPECT_EQ(net::ERR_DNS_TIMED_OUT, observer.net_error_code());
     EXPECT_EQ(GURL(kUnreachableWebDataURL), error_site_instance->GetSiteURL());
+    EXPECT_TRUE(
+        IsMainFrameOriginOpaqueAndCompatibleWithURL(new_shell, error_url));
   }
 
   // Verify the two SiteInstanes are not related, but they end up using the
@@ -4607,6 +4650,7 @@
   int process_id =
       shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
   EXPECT_EQ(GURL(kUnreachableWebDataURL), policy->GetOriginLock(process_id));
+  EXPECT_TRUE(IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
 
   // Reload while it will still fail to ensure it stays in the same process.
   {
@@ -4619,6 +4663,7 @@
   }
   EXPECT_EQ(process_id,
             shell()->web_contents()->GetMainFrame()->GetProcess()->GetID());
+  EXPECT_TRUE(IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
 
   // Reload the error page after clearing the error condition, such that the
   // navigation is successful and verify that no new entry was added to
@@ -4670,6 +4715,7 @@
       GURL(kUnreachableWebDataURL),
       policy->GetOriginLock(
           shell()->web_contents()->GetSiteInstance()->GetProcess()->GetID()));
+  EXPECT_TRUE(IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
 
   url_interceptor.reset();
   {
@@ -4756,10 +4802,7 @@
     EXPECT_EQ(test_url, child->current_frame_host()->GetLastCommittedURL());
 
     // Error pages should commit in an opaque origin.
-    url::Origin origin = child->current_frame_host()->GetLastCommittedOrigin();
-    EXPECT_TRUE(origin.opaque());
-    EXPECT_EQ(test_url.GetOrigin(),
-              origin.GetTupleOrPrecursorTupleIfOpaque().GetURL());
+    EXPECT_TRUE(IsOriginOpaqueAndCompatibleWithURL(child, test_url));
 
     // Error page isolation should apply only to the main frame - subframes
     // should commit in their usual process / SiteInstance.
@@ -4846,13 +4889,7 @@
     EXPECT_EQ(test_url, child1->current_frame_host()->GetLastCommittedURL());
 
     // Error pages should commit in an opaque origin.
-    url::Origin error_origin =
-        child1->current_frame_host()->GetLastCommittedOrigin();
-    url::Origin initiator_origin =
-        child2->current_frame_host()->GetLastCommittedOrigin();
-    EXPECT_TRUE(error_origin.opaque());
-    EXPECT_EQ(initiator_origin.GetURL(),
-              error_origin.GetTupleOrPrecursorTupleIfOpaque().GetURL());
+    EXPECT_TRUE(IsOriginOpaqueAndCompatibleWithURL(child1, test_url));
 
     // net::ERR_BLOCKED_BY_CLIENT errors in subframes should commit in their
     // initiator's process (not in their parent's process).
@@ -5033,6 +5070,7 @@
       ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(
           shell()->web_contents()->GetSiteInstance()->GetProcess()->GetID()));
   EXPECT_EQ(2, nav_controller.GetEntryCount());
+  EXPECT_TRUE(IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
 
   // Navigate again to the initial successful document, expecting a new
   // navigation and new SiteInstance. A new SiteInstance is expected here
@@ -5113,6 +5151,7 @@
             ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(
                 web_contents->GetSiteInstance()->GetProcess()->GetID()));
   EXPECT_EQ(2, nav_controller.GetEntryCount());
+  EXPECT_TRUE(IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
 
   // Terminate the renderer process.
   {
@@ -5179,6 +5218,7 @@
   EXPECT_EQ(GURL(kUnreachableWebDataURL),
             ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(
                 web_contents->GetSiteInstance()->GetProcess()->GetID()));
+  EXPECT_TRUE(IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), url1));
 }
 
 // Test to verify that a successful navigation to existing history entry,
@@ -5208,6 +5248,7 @@
   EXPECT_EQ(GURL(kUnreachableWebDataURL),
             ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(
                 web_contents->GetSiteInstance()->GetProcess()->GetID()));
+  EXPECT_TRUE(IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), url2));
 
   // There should be two NavigationEntries.
   NavigationControllerImpl& nav_controller =
@@ -5272,6 +5313,7 @@
                 error_site_instance->GetProcess()->GetID()));
   EXPECT_FALSE(ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(
       error_site_instance->GetProcess()->GetID()));
+  EXPECT_TRUE(IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
 }
 
 // A test ContentBrowserClient implementation which enforces
@@ -5333,6 +5375,7 @@
   EXPECT_EQ(GURL(kUnreachableWebDataURL), initial_instance->GetSiteURL());
   EXPECT_TRUE(
       success_site_instance->IsRelatedSiteInstance(initial_instance.get()));
+  EXPECT_TRUE(IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
 
   // Reload of the error page that still results in an error should stay in
   // the same SiteInstance. Ensure this works for both browser-initiated
@@ -5345,6 +5388,8 @@
     EXPECT_EQ(2, nav_controller.GetEntryCount());
     EXPECT_EQ(initial_instance,
               shell()->web_contents()->GetMainFrame()->GetSiteInstance());
+    EXPECT_TRUE(
+        IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
   }
   {
     TestNavigationObserver reload_observer(shell()->web_contents());
@@ -5354,6 +5399,8 @@
     EXPECT_EQ(2, nav_controller.GetEntryCount());
     EXPECT_EQ(initial_instance,
               shell()->web_contents()->GetMainFrame()->GetSiteInstance());
+    EXPECT_TRUE(
+        IsMainFrameOriginOpaqueAndCompatibleWithURL(shell(), error_url));
   }
 
   // Allow the navigation to succeed and ensure it swapped to a non-related
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
index f8ba1076..29eee1df 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
@@ -95,10 +95,9 @@
   // If the directory does not exist already, ensure that the parent directory
   // exists, which is usually the User Data directory. If it exists, we can try
   // creating the cache directory.
-  return !cache_directory.empty() &&
-         (base::DirectoryExists(cache_directory) ||
-          (base::DirectoryExists(cache_directory.DirName()) &&
-           CreateDirectory(cache_directory)));
+  return base::DirectoryExists(cache_directory) ||
+         (base::DirectoryExists(cache_directory.DirName()) &&
+          CreateDirectory(cache_directory));
 }
 
 }  // namespace
@@ -116,10 +115,6 @@
 
 DWriteFontLookupTableBuilder::DWriteFontLookupTableBuilder()
     : font_indexing_timeout_(kFontIndexingTimeoutDefault) {
-  InitializeCacheDirectoryFromProfile();
-}
-
-void DWriteFontLookupTableBuilder::InitializeCacheDirectoryFromProfile() {
   // In FontUniqueNameBrowserTest the DWriteFontLookupTableBuilder is
   // instantiated to configure the cache directory for testing explicitly before
   // GetContentClient() is available. Catch this case here. It is safe to not
@@ -127,7 +122,7 @@
   // detected by TableCacheFilePath and the LoadFromFile and PersistToFile
   // methods.
   cache_directory_ =
-      GetContentClient() && GetContentClient()->browser()
+      GetContentClient()
           ? GetContentClient()->browser()->GetFontLookupTableCacheDir()
           : base::FilePath();
 }
@@ -136,9 +131,6 @@
 
 base::ReadOnlySharedMemoryRegion
 DWriteFontLookupTableBuilder::DuplicateMemoryRegion() {
-  DCHECK(!TableCacheFilePath().empty())
-      << "Ensure that a cache_directory_ is set (see "
-         "InitializeCacheDirectoryFromProfile())";
   DCHECK(FontUniqueNameTableReady());
   return font_table_memory_.region.Duplicate();
 }
@@ -160,6 +152,10 @@
     return;
   }
 
+  // QueryInterface for IDWriteFactory2. It's ok for this to fail if we are
+  // running an older version of DirectWrite (earlier than Win8.1).
+  factory.As<IDWriteFactory2>(&factory2_);
+
   // QueryInterface for IDwriteFactory3, needed for MatchUniqueFont on Windows
   // 10. May fail on older versions, in which case, unique font matching must be
   // done through indexing system fonts using DWriteFontLookupTableBuilder.
@@ -319,18 +315,10 @@
     InitializeDirectWrite();
   }
 
-  // Nothing to do if we have API to directly lookup local fonts by unique name
-  // (as on Windows 10, IDWriteFactory3 available).
+  // Nothing to do if we have API to directly lookup local fonts by unique name.
   if (HasDWriteUniqueFontLookups())
     return;
 
-  // Do not schedule indexing if we do not have a profile or temporary directory
-  // to store the cached table. This prevents repetitive and redundant scanning
-  // when the ContentBrowserClient did not provide a cache directory, as is the
-  // case in content_unittests.
-  if (TableCacheFilePath().empty())
-    return;
-
   start_time_table_ready_ = base::TimeTicks::Now();
 
   scoped_refptr<base::SequencedTaskRunner> results_collection_task_runner =
@@ -668,15 +656,6 @@
   font_table_built_.Reset();
 }
 
-void DWriteFontLookupTableBuilder::ResetStateForTesting() {
-  ResetLookupTableForTesting();
-  // Recreate fFactory3 if available, to reset
-  // OverrideDWriteVersionChecksForTesting().
-  direct_write_initialized_ = false;
-  InitializeDirectWrite();
-  InitializeCacheDirectoryFromProfile();
-}
-
 void DWriteFontLookupTableBuilder::ResumeFromHangForTesting() {
   hang_event_for_testing_->Signal();
 }
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
index 4d23f8ac..2c0c17d 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
@@ -78,15 +78,10 @@
   void SetSlowDownIndexingForTestingWithTimeout(SlowDownMode slowdown_mode,
                                                 base::TimeDelta new_timeout);
 
-  // Reset timeout overrides and empty table. Needed to trigger rebuilding the
-  // lookup table, when testing using slowed-down indexing. Otherwise, the test
-  // methods would use the already cached lookup table.
+  // Needed to trigger rebuilding the lookup table, when testing using
+  // slowed-down indexing. Otherwise, the test methods would use the already
+  // cached lookup table.
   void ResetLookupTableForTesting();
-
-  // Resets other overrides such as the DWrite version check override and cache
-  // directory back to its default values.
-  void ResetStateForTesting();
-
   // Signals hang_event_for_testing_ which is used in testing hanging one of the
   // font name retrieval tasks.
   void ResumeFromHangForTesting();
@@ -107,9 +102,8 @@
   // repeated rebuilding of the font table lookup structure.
   void SetCachingEnabledForTesting(bool caching_enabled);
 
-  // Disable DCHECKs that ensure DWriteFontLookupTableBuilder is only
-  // run pre Windows 10, used for testing only to allow running the tests on
-  // Windows 10.
+  // Disables DCHECKs that ensure DWriteFontLookupTableBuilder is only run pre
+  // Windows 10, used for testing only to allow running the tests on Windows 10.
   void OverrideDWriteVersionChecksForTesting();
 
  private:
@@ -138,11 +132,6 @@
   // specified at construction time.
   bool PersistToFile();
 
-  // Initialize the cache directory from the user profile directory if
-  // DWriteFontLookupTableBuilder is executed in an environment where the
-  // profile is accessible.
-  void InitializeCacheDirectoryFromProfile();
-
   // Load from cache or construct the font unique name lookup table. If the
   // cache is up to date, do not schedule a run to scan all Windows-enumerated
   // fonts.
@@ -204,6 +193,7 @@
   base::TimeTicks start_time_table_ready_;
   base::TimeTicks start_time_table_build_;
   base::FilePath cache_directory_;
+  std::string persistence_hash_;
 
   bool caching_enabled_ = true;
   base::Optional<base::WaitableEvent> hang_event_for_testing_;
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
index c7fdcf1..b02ef48 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc
@@ -51,10 +51,6 @@
         scoped_temp_dir_.GetPath());
   }
 
-  void TearDown() override {
-    font_lookup_table_builder_->ResetStateForTesting();
-  }
-
   void TestMatchFonts() {
     base::ReadOnlySharedMemoryRegion font_table_memory =
         font_lookup_table_builder_->DuplicateMemoryRegion();
diff --git a/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc b/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc
index 71c515c..50186030 100644
--- a/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc
+++ b/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc
@@ -95,11 +95,10 @@
 class DWriteFontProxyTableMatchingTest
     : public DWriteFontProxyLocalMatchingTest {
  public:
-  void SetUp() override {
+  DWriteFontProxyTableMatchingTest() {
     DWriteFontLookupTableBuilder* table_builder_instance =
         DWriteFontLookupTableBuilder::GetInstance();
-    bool temp_dir_success = scoped_temp_dir_.CreateUniqueTempDir();
-    ASSERT_TRUE(temp_dir_success);
+    DCHECK(scoped_temp_dir_.CreateUniqueTempDir());
     table_builder_instance->OverrideDWriteVersionChecksForTesting();
     table_builder_instance->SetCacheDirectoryForTesting(
         scoped_temp_dir_.GetPath());
@@ -107,12 +106,6 @@
     table_builder_instance->SchedulePrepareFontUniqueNameTableIfNeeded();
   }
 
-  void TearDown() override {
-    DWriteFontLookupTableBuilder* table_builder_instance =
-        DWriteFontLookupTableBuilder::GetInstance();
-    table_builder_instance->ResetStateForTesting();
-  }
-
  private:
   base::ScopedTempDir scoped_temp_dir_;
 };
diff --git a/content/renderer/loader/OWNERS b/content/renderer/loader/OWNERS
index acbf93af..9981dc45 100644
--- a/content/renderer/loader/OWNERS
+++ b/content/renderer/loader/OWNERS
@@ -1 +1,3 @@
 yhirano@chromium.org
+# COMPONENT: Blink>Loader
+# TEAM: loading-dev@chromium.org
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index fd8be9b..33810df2 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -2058,27 +2058,31 @@
       replicated_state, devtools_frame_token);
 }
 
-void StartEmbeddedWorkerInstanceClientOnIOThread(
+void StartEmbeddedWorkerInstanceClientOnThreadPool(
     mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient>
         client_receiver,
-    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
-  DCHECK(io_task_runner->BelongsToCurrentThread());
+    scoped_refptr<base::SingleThreadTaskRunner> initiator_task_runner) {
+  DCHECK(initiator_task_runner->BelongsToCurrentThread());
   EmbeddedWorkerInstanceClientImpl::Create(std::move(client_receiver),
-                                           std::move(io_task_runner));
+                                           std::move(initiator_task_runner));
 }
 
 void RenderThreadImpl::SetUpEmbeddedWorkerChannelForServiceWorker(
     mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient>
         client_receiver) {
-  // TODO(bashi): This is a tentative workaround to start service worker on the
-  // IO thread. We should decouple EmbeddedWorkerInstanceClient from Renderer
-  // and bind EmbeddedWorkerInstanceClient on the IO thread.
+  // TODO(bashi): This is a tentative workaround to start service worker on a
+  // background thread. We should decouple EmbeddedWorkerInstanceClient from
+  // Renderer and bind EmbeddedWorkerInstanceClient on a background thread.
   if (base::FeatureList::IsEnabled(
           blink::features::kOffMainThreadServiceWorkerStartup)) {
-    GetIOTaskRunner()->PostTask(
+    auto task_runner = base::CreateSingleThreadTaskRunner(
+        {base::ThreadPool(), base::MayBlock(),
+         base::TaskPriority::USER_BLOCKING,
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+    task_runner->PostTask(
         FROM_HERE,
-        base::BindOnce(&StartEmbeddedWorkerInstanceClientOnIOThread,
-                       std::move(client_receiver), GetIOTaskRunner()));
+        base::BindOnce(&StartEmbeddedWorkerInstanceClientOnThreadPool,
+                       std::move(client_receiver), std::move(task_runner)));
     return;
   }
 
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.h b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
index 168126c8..6f627901 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.h
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
@@ -28,8 +28,8 @@
 // service worker to stop and then deletes itself.
 //
 // All methods are called on the thread that creates the instance of this class.
-// Currently it's the main thread but it could be the IO thread in the future.
-// https://crbug.com/692909
+// Currently it's the main thread but it could be a background thread in the
+// future. https://crbug.com/692909
 class CONTENT_EXPORT EmbeddedWorkerInstanceClientImpl
     : public blink::mojom::EmbeddedWorkerInstanceClient {
  public:
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 701092a..330cfab 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -26,6 +26,9 @@
 [ win ] WebglExtension_WEBGL_compressed_texture_astc [ Skip ]
 [ mac ] WebglExtension_WEBGL_compressed_texture_astc [ Skip ]
 [ linux ] WebglExtension_WEBGL_compressed_texture_astc [ Skip ]
+[ win ] WebglExtension_WEBGL_compressed_texture_etc [ Skip ]
+[ mac ] WebglExtension_WEBGL_compressed_texture_etc [ Skip ]
+[ linux ] WebglExtension_WEBGL_compressed_texture_etc [ Skip ]
 [ win ] WebglExtension_WEBGL_compressed_texture_etc1 [ Skip ]
 [ mac ] WebglExtension_WEBGL_compressed_texture_etc1 [ Skip ]
 [ linux ] WebglExtension_WEBGL_compressed_texture_etc1 [ Skip ]
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py b/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
index d34c144e..d9e3960a 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
@@ -156,6 +156,7 @@
         'OES_vertex_array_object',
         'WEBGL_color_buffer_float',
         'WEBGL_compressed_texture_astc',
+        'WEBGL_compressed_texture_etc',
         'WEBGL_compressed_texture_etc1',
         'WEBGL_compressed_texture_pvrtc',
         'WEBGL_compressed_texture_s3tc',
diff --git a/device/vr/buildflags/buildflags.gni b/device/vr/buildflags/buildflags.gni
index 37cba3e..dfe975ea 100644
--- a/device/vr/buildflags/buildflags.gni
+++ b/device/vr/buildflags/buildflags.gni
@@ -17,7 +17,9 @@
 
   # To build with OpenXR support, the OpenXR Loader needs to be pulled to
   # third_party/openxr.
-  enable_openxr = checkout_openxr && is_win
+  # TODO (crbug.com/998160): there's an issue affecting the __preload macro
+  # used in DirectXMathVector.inl on Windows on Arm.
+  enable_openxr = checkout_openxr && (is_win && current_cpu != "arm64")
 
   # To build with Oculus support, the Oculus SDK for Windows will need to be
   # installed in third_party/libovr/src.  See
diff --git a/device/vr/openxr/openxr_api_wrapper.cc b/device/vr/openxr/openxr_api_wrapper.cc
index 5c5f529..68e8af1 100644
--- a/device/vr/openxr/openxr_api_wrapper.cc
+++ b/device/vr/openxr/openxr_api_wrapper.cc
@@ -103,6 +103,7 @@
 }
 
 void OpenXrApiWrapper::Reset() {
+  session_ended_ = false;
   local_space_ = XR_NULL_HANDLE;
   stage_space_ = XR_NULL_HANDLE;
   view_space_ = XR_NULL_HANDLE;
@@ -138,7 +139,7 @@
   DCHECK(IsInitialized());
 
   if (test_hook_) {
-    // Allow our mock implementation of OpenXR to be controlled by tests.
+    // Allow our mock implementation of OpenXr to be controlled by tests.
     // The mock implementation of xrCreateInstance returns a pointer to the
     // service test hook (g_test_helper) as the instance.
     service_test_hook_ = reinterpret_cast<ServiceTestHook*>(instance_);
@@ -275,7 +276,7 @@
 // Callers of this function must check the XrResult return value and destroy
 // this OpenXrApiWrapper object on failure to clean up any intermediate
 // objects that may have been created before the failure.
-XrResult OpenXrApiWrapper::StartSession(
+XrResult OpenXrApiWrapper::InitSession(
     const Microsoft::WRL::ComPtr<ID3D11Device>& d3d_device,
     std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper) {
   DCHECK(d3d_device.Get());
@@ -289,7 +290,6 @@
       CreateSpace(XR_REFERENCE_SPACE_TYPE_LOCAL, &local_space_));
   RETURN_IF_XR_FAILED(CreateSpace(XR_REFERENCE_SPACE_TYPE_VIEW, &view_space_));
   RETURN_IF_XR_FAILED(CreateGamepadHelper(gamepad_helper));
-  RETURN_IF_XR_FAILED(BeginSession());
 
   // It's ok if stage_space_ fails since not all OpenXR devices are required to
   // support this reference space.
@@ -411,6 +411,8 @@
 
   XrResult xr_result;
 
+  RETURN_IF_XR_FAILED(ProcessEvents());
+
   XrFrameWaitInfo wait_frame_info = {XR_TYPE_FRAME_WAIT_INFO};
   XrFrameState frame_state = {XR_TYPE_FRAME_STATE};
   RETURN_IF_XR_FAILED(xrWaitFrame(session_, &wait_frame_info, &frame_state));
@@ -580,6 +582,35 @@
   return xr_result;
 }
 
+XrResult OpenXrApiWrapper::ProcessEvents() {
+  XrEventDataBuffer event_data{XR_TYPE_EVENT_DATA_BUFFER};
+  XrResult xr_result = xrPollEvent(instance_, &event_data);
+
+  while (XR_SUCCEEDED(xr_result) && xr_result != XR_EVENT_UNAVAILABLE) {
+    if (event_data.type == XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED) {
+      XrEventDataSessionStateChanged* session_state_changed =
+          reinterpret_cast<XrEventDataSessionStateChanged*>(&event_data);
+      // We only have will only have one session and we should make sure the
+      // session that is having state_changed event is ours.
+      DCHECK(session_state_changed->session == session_);
+      switch (session_state_changed->state) {
+        case XR_SESSION_STATE_READY:
+          RETURN_IF_XR_FAILED(BeginSession());
+          break;
+        case XR_SESSION_STATE_STOPPING:
+          session_ended_ = true;
+          RETURN_IF_XR_FAILED(xrEndSession(session_));
+          break;
+        default:
+          break;
+      }
+    }
+    event_data.type = XR_TYPE_EVENT_DATA_BUFFER;
+    xr_result = xrPollEvent(instance_, &event_data);
+  }
+  return xr_result;
+}
+
 gfx::Size OpenXrApiWrapper::GetViewSize() const {
   DCHECK(IsInitialized());
   CHECK(view_configs_.size() == kNumViews);
diff --git a/device/vr/openxr/openxr_api_wrapper.h b/device/vr/openxr/openxr_api_wrapper.h
index 64ed4ede..3c64f5bbc 100644
--- a/device/vr/openxr/openxr_api_wrapper.h
+++ b/device/vr/openxr/openxr_api_wrapper.h
@@ -43,8 +43,10 @@
 
   static VRTestHook* GetTestHook();
 
-  XrResult StartSession(const Microsoft::WRL::ComPtr<ID3D11Device>& d3d_device,
-                        std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper);
+  bool session_ended() const { return session_ended_; }
+
+  XrResult InitSession(const Microsoft::WRL::ComPtr<ID3D11Device>& d3d_device,
+                       std::unique_ptr<OpenXrGamepadHelper>* gamepad_helper);
 
   XrResult BeginFrame(Microsoft::WRL::ComPtr<ID3D11Texture2D>* texture);
   XrResult EndFrame();
@@ -70,6 +72,7 @@
 
   XrResult InitializeSystem();
   XrResult PickEnvironmentBlendMode(XrSystemId system);
+  XrResult ProcessEvents();
 
   XrResult CreateSession(
       const Microsoft::WRL::ComPtr<ID3D11Device>& d3d_device);
@@ -92,6 +95,8 @@
   uint32_t GetRecommendedSwapchainSampleCount() const;
   XrResult GetStageBounds(XrExtent2Df* stage_bounds) const;
 
+  bool session_ended_;
+
   // Testing objects
   static VRTestHook* test_hook_;
   static ServiceTestHook* service_test_hook_;
diff --git a/device/vr/openxr/openxr_render_loop.cc b/device/vr/openxr/openxr_render_loop.cc
index 41369d5..888a0468 100644
--- a/device/vr/openxr/openxr_render_loop.cc
+++ b/device/vr/openxr/openxr_render_loop.cc
@@ -98,8 +98,8 @@
   if (XR_FAILED(openxr->GetLuid(&luid)) ||
       !texture_helper_.SetAdapterLUID(luid) ||
       !texture_helper_.EnsureInitialized() ||
-      XR_FAILED(openxr->StartSession(texture_helper_.GetDevice(),
-                                     &gamepad_helper_))) {
+      XR_FAILED(
+          openxr->InitSession(texture_helper_.GetDevice(), &gamepad_helper_))) {
     texture_helper_.Reset();
     return false;
   }
@@ -133,6 +133,10 @@
   return true;
 }
 
+bool OpenXrRenderLoop::HasSessionEnded() {
+  return openxr_->session_ended();
+}
+
 bool OpenXrRenderLoop::SubmitCompositedFrame() {
   return XR_SUCCEEDED(openxr_->EndFrame());
 }
diff --git a/device/vr/openxr/openxr_render_loop.h b/device/vr/openxr/openxr_render_loop.h
index 3580858b..6c1cb17e 100644
--- a/device/vr/openxr/openxr_render_loop.h
+++ b/device/vr/openxr/openxr_render_loop.h
@@ -34,6 +34,7 @@
   void StopRuntime() override;
   void OnSessionStart() override;
   bool PreComposite() override;
+  bool HasSessionEnded() override;
   bool SubmitCompositedFrame() override;
 
   bool UpdateDisplayInfo();
diff --git a/device/vr/openxr/test/fake_openxr_impl_api.cc b/device/vr/openxr/test/fake_openxr_impl_api.cc
index 2222176f..85c71ad2e 100644
--- a/device/vr/openxr/test/fake_openxr_impl_api.cc
+++ b/device/vr/openxr/test/fake_openxr_impl_api.cc
@@ -73,6 +73,7 @@
             "XrSessionBeginInfo primaryViewConfigurationType invalid");
 
   RETURN_IF_XR_FAILED(g_test_helper.BeginSession());
+
   return XR_SUCCESS;
 }
 
@@ -200,7 +201,7 @@
             "D3D11Device is null");
 
   g_test_helper.SetD3DDevice(binding->device);
-  *session = g_test_helper.GetSession();
+  RETURN_IF_XR_FAILED(g_test_helper.GetSession(session));
 
   return XR_SUCCESS;
 }
@@ -466,10 +467,9 @@
   return XR_SUCCESS;
 }
 
-XrResult XRAPI_CALL
-xrGetActionStateVector2f(XrSession session,
-                         const XrActionStateGetInfo* get_info,
-                         XrActionStateVector2f* state) {
+XrResult xrGetActionStateVector2f(XrSession session,
+                                  const XrActionStateGetInfo* get_info,
+                                  XrActionStateVector2f* state) {
   DLOG(INFO) << __FUNCTION__;
   XrResult xr_result;
 
@@ -586,6 +586,28 @@
   return XR_SUCCESS;
 }
 
+XrResult xrPollEvent(XrInstance instance, XrEventDataBuffer* event_data) {
+  DLOG(INFO) << __FUNCTION__;
+  XrResult xr_result;
+
+  RETURN_IF_XR_FAILED(g_test_helper.ValidateInstance(instance));
+
+  RETURN_IF_FALSE(event_data->type == XR_TYPE_EVENT_DATA_BUFFER,
+                  XR_ERROR_VALIDATION_FAILURE,
+                  "xrPollEvent event_data type invalid");
+  RETURN_IF_FALSE(g_test_helper.UpdateSessionStateEventQueue(),
+                  XR_ERROR_VALIDATION_FAILURE,
+                  "Update SessionStateEventQueue failed.");
+  if (g_test_helper.HasPendingSessionStateEvent()) {
+    XrEventDataSessionStateChanged* event_data_ptr =
+        reinterpret_cast<XrEventDataSessionStateChanged*>(event_data);
+    *event_data_ptr = g_test_helper.GetNextSessionStateEvent();
+    return XR_SUCCESS;
+  }
+
+  return XR_EVENT_UNAVAILABLE;
+}
+
 XrResult xrReleaseSwapchainImage(
     XrSwapchain swapchain,
     const XrSwapchainImageReleaseInfo* release_info) {
diff --git a/device/vr/openxr/test/openxr_negotiate.h b/device/vr/openxr/test/openxr_negotiate.h
index 9687d50dd..a1b0221c 100644
--- a/device/vr/openxr/test/openxr_negotiate.h
+++ b/device/vr/openxr/test/openxr_negotiate.h
@@ -87,6 +87,8 @@
     *function = reinterpret_cast<PFN_xrVoidFunction>(xrLocateSpace);
   } else if (strcmp(name, "xrLocateViews") == 0) {
     *function = reinterpret_cast<PFN_xrVoidFunction>(xrLocateViews);
+  } else if (strcmp(name, "xrPollEvent") == 0) {
+    *function = reinterpret_cast<PFN_xrVoidFunction>(xrPollEvent);
   } else if (strcmp(name, "xrReleaseSwapchainImage") == 0) {
     *function = reinterpret_cast<PFN_xrVoidFunction>(xrReleaseSwapchainImage);
   } else if (strcmp(name, "xrSuggestInteractionProfileBindings") == 0) {
diff --git a/device/vr/openxr/test/openxr_test_helper.cc b/device/vr/openxr/test/openxr_test_helper.cc
index ccf0866a..e246cb18 100644
--- a/device/vr/openxr/test/openxr_test_helper.cc
+++ b/device/vr/openxr/test/openxr_test_helper.cc
@@ -50,10 +50,10 @@
 OpenXrTestHelper::OpenXrTestHelper()
     : system_id_(0),
       session_(XR_NULL_HANDLE),
+      session_state_(XR_SESSION_STATE_UNKNOWN),
       swapchain_(XR_NULL_HANDLE),
       local_space_(XR_NULL_HANDLE),
       view_space_(XR_NULL_HANDLE),
-      session_running_(false),
       acquired_swapchain_texture_(0),
       next_action_space_(0),
       next_predicted_display_time_(0) {}
@@ -112,9 +112,14 @@
   return system_id_;
 }
 
-XrSession OpenXrTestHelper::GetSession() {
+XrResult OpenXrTestHelper::GetSession(XrSession* session) {
+  RETURN_IF(session_state_ != XR_SESSION_STATE_UNKNOWN,
+            XR_ERROR_VALIDATION_FAILURE,
+            "SessionState is not unknown before xrCreateSession");
   session_ = TreatIntegerAsHandle<XrSession>(2);
-  return session_;
+  *session = session_;
+  SetSessionState(XR_SESSION_STATE_READY);
+  return XR_SUCCESS;
 }
 
 XrSwapchain OpenXrTestHelper::GetSwapchain() {
@@ -232,18 +237,18 @@
 }
 
 XrResult OpenXrTestHelper::BeginSession() {
-  RETURN_IF(session_running_, XR_ERROR_SESSION_RUNNING,
-            "Session is already running");
-
-  session_running_ = true;
+  RETURN_IF(session_state_ != XR_SESSION_STATE_READY,
+            XR_ERROR_VALIDATION_FAILURE,
+            "Session is not XR_ERROR_SESSION_NOT_READY");
+  SetSessionState(XR_SESSION_STATE_SYNCHRONIZED);
   return XR_SUCCESS;
 }
 
 XrResult OpenXrTestHelper::EndSession() {
-  RETURN_IF_FALSE(session_running_, XR_ERROR_SESSION_NOT_RUNNING,
-                  "Session is not currently running");
-
-  session_running_ = false;
+  RETURN_IF(session_state_ != XR_SESSION_STATE_STOPPING,
+            XR_ERROR_VALIDATION_FAILURE,
+            "Session state is not XR_ERROR_SESSION_NOT_STOPPING");
+  SetSessionState(XR_SESSION_STATE_IDLE);
   return XR_SUCCESS;
 }
 
@@ -364,6 +369,23 @@
   return XR_SUCCESS;
 }
 
+void OpenXrTestHelper::SetSessionState(XrSessionState state) {
+  session_state_ = state;
+  XrEventDataSessionStateChanged event = {
+      XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED};
+  event.session = session_;
+  event.state = session_state_;
+  event.time = next_predicted_display_time_;
+  session_state_event_queue_.push(event);
+}
+
+XrEventDataSessionStateChanged OpenXrTestHelper::GetNextSessionStateEvent() {
+  DCHECK(HasPendingSessionStateEvent());
+  XrEventDataSessionStateChanged front = session_state_event_queue_.front();
+  session_state_event_queue_.pop();
+  return front;
+}
+
 const std::vector<Microsoft::WRL::ComPtr<ID3D11Texture2D>>&
 OpenXrTestHelper::GetSwapchainTextures() const {
   return textures_arr_;
@@ -379,6 +401,22 @@
   return ++next_predicted_display_time_;
 }
 
+bool OpenXrTestHelper::UpdateSessionStateEventQueue() {
+  base::AutoLock auto_lock(lock_);
+  if (test_hook_) {
+    if (test_hook_->WaitGetSessionStateStopping()) {
+      SetSessionState(XR_SESSION_STATE_STOPPING);
+    }
+    return true;
+  }
+
+  return false;
+}
+
+bool OpenXrTestHelper::HasPendingSessionStateEvent() {
+  return !session_state_event_queue_.empty();
+}
+
 void OpenXrTestHelper::GetPose(XrPosef* pose) {
   *pose = device::PoseIdentity();
 
diff --git a/device/vr/openxr/test/openxr_test_helper.h b/device/vr/openxr/test/openxr_test_helper.h
index 661b89a..bd691fe 100644
--- a/device/vr/openxr/test/openxr_test_helper.h
+++ b/device/vr/openxr/test/openxr_test_helper.h
@@ -9,6 +9,7 @@
 #include <unknwn.h>
 #include <wrl.h>
 #include <array>
+#include <queue>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
@@ -36,7 +37,6 @@
   // state of the runtime.
 
   XrSystemId GetSystemId();
-  XrSession GetSession();
   XrSwapchain GetSwapchain();
   XrResult GetActionStateBoolean(XrAction action,
                                  XrActionStateBoolean* data) const;
@@ -51,6 +51,7 @@
   XrSpace CreateActionSpace();
   XrPath GetPath(const char* path_string);
 
+  XrResult GetSession(XrSession* session);
   XrResult BeginSession();
   XrResult EndSession();
 
@@ -67,6 +68,10 @@
   uint32_t NextSwapchainImageIndex();
   XrTime NextPredictedDisplayTime();
 
+  bool UpdateSessionStateEventQueue();
+  bool HasPendingSessionStateEvent();
+  XrEventDataSessionStateChanged GetNextSessionStateEvent();
+
   // Methods that validate the parameter with the current state of the runtime.
   XrResult ValidateAction(XrAction action) const;
   XrResult ValidateActionCreateInfo(
@@ -107,6 +112,7 @@
   };
 
   XrResult UpdateAction(XrAction action);
+  void SetSessionState(XrSessionState state);
 
   // Properties of the mock OpenXR runtime that doesn't change throughout the
   // lifetime of the instance. However, these aren't static because they are
@@ -115,12 +121,12 @@
   // to validate that they were queried before being used.
   XrSystemId system_id_;
   XrSession session_;
+  XrSessionState session_state_;
   XrSwapchain swapchain_;
   XrSpace local_space_;
   XrSpace view_space_;
 
   // Properties that changes depending on the state of the runtime.
-  bool session_running_;
   Microsoft::WRL::ComPtr<ID3D11Device> d3d_device_;
   std::vector<Microsoft::WRL::ComPtr<ID3D11Texture2D>> textures_arr_;
   uint32_t acquired_swapchain_texture_;
@@ -149,6 +155,10 @@
 
   std::array<device::ControllerFrameData, device::kMaxTrackedDevices> data_arr_;
 
+  // session_state_event_queue_ is used to store XrEventDataSessionStateChanged
+  // event whenever session state changes.
+  std::queue<XrEventDataSessionStateChanged> session_state_event_queue_;
+
   device::VRTestHook* test_hook_ GUARDED_BY(lock_) = nullptr;
   base::Lock lock_;
 };
diff --git a/device/vr/public/mojom/browser_test_interfaces.mojom b/device/vr/public/mojom/browser_test_interfaces.mojom
index 430cb8d..bbf8640 100644
--- a/device/vr/public/mojom/browser_test_interfaces.mojom
+++ b/device/vr/public/mojom/browser_test_interfaces.mojom
@@ -117,6 +117,11 @@
   // the controller data of the device that the test has registered at the
   // given index, e.g. its current position and pressed buttons.
   [Sync] WaitGetControllerData(uint32 index) => (ControllerFrameData data);
+
+  // Called by the OpenXR test. Test can inform runtime when it should stop
+  // session by calling this function and test if such events are handled
+  // correctly.
+  [Sync] WaitGetSessionStateStopping() => (bool stopping);
 };
 
 // Interface exposed by IsolatedXRService to allow browser tests to hook VR APIs
diff --git a/device/vr/test/test_hook.h b/device/vr/test/test_hook.h
index 3032a5d..47937717 100644
--- a/device/vr/test/test_hook.h
+++ b/device/vr/test/test_hook.h
@@ -145,6 +145,7 @@
       unsigned int index) = 0;
   virtual TrackedDeviceClass WaitGetTrackedDeviceClass(unsigned int index) = 0;
   virtual ControllerFrameData WaitGetControllerData(unsigned int index) = 0;
+  virtual bool WaitGetSessionStateStopping() = 0;
 
   virtual void AttachCurrentThread() = 0;
   virtual void DetachCurrentThread() = 0;
diff --git a/device/vr/windows/compositor_base.cc b/device/vr/windows/compositor_base.cc
index 86ac40e..0c8d6d1 100644
--- a/device/vr/windows/compositor_base.cc
+++ b/device/vr/windows/compositor_base.cc
@@ -32,6 +32,9 @@
 bool XRDeviceAbstraction::PreComposite() {
   return true;
 }
+bool XRDeviceAbstraction::HasSessionEnded() {
+  return false;
+}
 void XRDeviceAbstraction::OnLayerBoundsChanged() {}
 
 XRCompositorCommon::OutstandingFrame::OutstandingFrame() = default;
@@ -314,6 +317,11 @@
     mojom::XRFrameDataRequestOptionsPtr options,
     mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
   TRACE_EVENT0("xr", "GetFrameData");
+  if (HasSessionEnded()) {
+    ExitPresent();
+    return;
+  }
+
   if (!is_presenting_) {
     return;
   }
diff --git a/device/vr/windows/compositor_base.h b/device/vr/windows/compositor_base.h
index eadcc30..6b924faf 100644
--- a/device/vr/windows/compositor_base.h
+++ b/device/vr/windows/compositor_base.h
@@ -31,6 +31,7 @@
   virtual void StopRuntime() = 0;
   virtual void OnSessionStart();
   virtual bool PreComposite();
+  virtual bool HasSessionEnded();
   virtual bool SubmitCompositedFrame() = 0;
   virtual void HandleDeviceLost();
   virtual void OnLayerBoundsChanged();
diff --git a/docs/disassemble_code.md b/docs/disassemble_code.md
index cb5b3ef6..0206dba 100644
--- a/docs/disassemble_code.md
+++ b/docs/disassemble_code.md
@@ -1,5 +1,7 @@
 # Dumping the compiled code from a chrome binary
 
+[Rendered](https://chromium.googlesource.com/chromium/src/+/master/docs/disassemble_code.md)
+
 ## Background
 
 Sometimes you want to look at the disassembled code of a method,
diff --git a/docs/lldbinit.md b/docs/lldbinit.md
new file mode 100644
index 0000000..3624357
--- /dev/null
+++ b/docs/lldbinit.md
@@ -0,0 +1,13 @@
+# Usage of tools/lldb/lldbinit.py
+
+Usage of Chromium's [lldbinit.py](../tools/lldb/lldbinit.py) is recommended when
+debugging with lldb. This is necessary for source-level debugging when
+`strip_absolute_paths_from_debug_symbols` is enabled.
+
+To use, add the following to your `~/.lldbinit`
+
+```
+# So that lldbinit.py takes precedence.
+script sys.path[:0] = ['<.../path/to/chromium/src/tools/lldb>']
+script import lldbinit
+```
diff --git a/docs/security/faq.md b/docs/security/faq.md
index 2ffe1c3..58603b70 100644
--- a/docs/security/faq.md
+++ b/docs/security/faq.md
@@ -262,43 +262,46 @@
 `http://evil.example.com` after the page loads.
 
 <a name="TOC-Why-isn-t-passive-browser-fingerprinting-including-passive-cookies-in-Chrome-s-threat-model-"></a>
-## Why isn't passive browser fingerprinting (including passive cookies) in Chrome's threat model?
+<a name="TOC-What-is-Chrome-s-threat-model-for-fingerprinting-"></a>
+## What is Chrome's threat model for fingerprinting?
 
-As discussed in [Issue 49075](https://crbug.com/49075), we currently do not
-attempt to defeat "passive fingerprinting" or
-"[evercookies](https://en.wikipedia.org/wiki/Evercookie)" or [ETag
-cookies](https://en.wikipedia.org/wiki/HTTP_ETag#Tracking_using_ETags), because
-defeating such fingerprinting is likely not practical without fundamental
-changes to how the Web works. One needs roughly 33 bits of non-correlated,
-distinguishing information to have a good chance of telling apart most user
-agents on the planet (see
-[Arvind Narayanan's site](https://33bits.wordpress.com/about/)
-and [Peter Eckersley's discussion of the information theory behind
-Panopticlick](https://www.eff.org/deeplinks/2010/01/primer-information-theory-and-privacy).)
+> **Update, August 2019:** Please note that this answer has changed. We have
+> updated our threat model to include fingerprinting.
 
-Although Chrome developers could try to reduce the fingerprintability of the
-browser by taking away (e.g.) JavaScript APIs, doing so would not achieve the
-security goal for a few reasons: (a) we could not likely get the
-distinguishability below 33 bits; (b) reducing fingerprintability requires
-breaking many (or even most) useful web features; and (c) so few people would
-tolerate the breakage that it would likely be easier to distinguish people who
-use the fingerprint-defense configuration. (See "[Anonymity Loves Company:
-Usability and the Network
-Effect](https://freehaven.net/anonbib/cache/usability:weis2006.pdf)" by
-Dingledine and Mathewson for more information.)
+Although [we do not consider fingerprinting issues to be *security
+vulnerabilities*](#TOC-Are-privacy-issues-considered-security-bugs-), we do now
+consider them to be privacy bugs that we will try to resolve. We distinguish two
+forms of fingerprinting.
 
-There is a pretty good analysis of in-browser fingerprinting vectors on [this
-wiki
-page](https://dev.chromium.org/Home/chromium-security/client-identification-mechanisms).
-Browser vectors aside, it's possible that the browser could be accurately
-fingerprinted entirely passively, without access to JavaScript or other web
-features or APIs, by its network traffic profile alone. (See e.g. *[Silence on
-the Wire](http://lcamtuf.coredump.cx/silence.shtml#/)* by Michal Zalewski
-generally.)
+* **Passive fingerprinting** refers to fingerprinting techniques that do not
+require a JavaScript API call to achieve. This includes (but is not limited to)
+mechanisms like [ETag
+cookies](https://en.wikipedia.org/wiki/HTTP_ETag#Tracking_using_ETags) and [HSTS
+cookies](https://security.stackexchange.com/questions/79518/what-are-hsts-super-cookies).
+* **Active fingerprinting** refers to fingerprinting techniques that do require
+a JavaScript API call to achieve. Examples include most of the techniques in
+[EFF's Panopticlick proof of concept](https://panopticlick.eff.org).
 
-Since we don't believe it's feasible to provide some mode of Chrome that can
-truly prevent passive fingerprinting, we will mark all related bugs and feature
-requests as WontFix.
+For passive fingerprinting, our ultimate goal is (to the extent possible) to
+reduce the information content available to below the threshold for usefulness.
+
+For active fingerprinting, our ultimate goal is to establish a [privacy
+budget](https://github.com/bslassey/privacy-budget) and to keep web origins
+below the budget (such as by rejecting some API calls when the origin exceeds
+its budget). To avoid breaking rich web applications that people want to use,
+Chrome may increase an origin's budget when it detects that a person is using
+the origin heavily. As with passive fingerprinting, our goal is to set the
+default budget below the threshold of usefulness for fingerprinting.
+
+These are both long-term goals. As of this writing (August 2019) we do not
+expect that Chrome will immediately achieve them.
+
+For background on fingerprinting and the difficulty of stopping it, see [Arvind
+Narayanan's site](https://33bits.wordpress.com/about/) and [Peter Eckersley's
+discussion of the information theory behind
+Panopticlick](https://www.eff.org/deeplinks/2010/01/primer-information-theory-and-privacy).
+There is also [a pretty good analysis of in-browser fingerprinting
+vectors](https://dev.chromium.org/Home/chromium-security/client-identification-mechanisms).
 
 <a name="TOC-Where-are-the-security-indicators-located-in-the-browser-window-"></a>
 ## Where are the security indicators located in the browser window?
diff --git a/extensions/browser/api/usb/usb_apitest.cc b/extensions/browser/api/usb/usb_apitest.cc
index 1a62273..0872b2d 100644
--- a/extensions/browser/api/usb/usb_apitest.cc
+++ b/extensions/browser/api/usb/usb_apitest.cc
@@ -14,6 +14,7 @@
 #include "extensions/shell/browser/shell_extensions_api_client.h"
 #include "extensions/shell/test/shell_apitest.h"
 #include "extensions/test/extension_test_message_listener.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/public/cpp/test/fake_usb_device_info.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/device/public/cpp/test/mock_usb_mojo_device.h"
@@ -123,10 +124,10 @@
     ShellApiTest::SetUpOnMainThread();
 
     // Set fake USB device manager for extensions::UsbDeviceManager.
-    device::mojom::UsbDeviceManagerPtr usb_manager_ptr;
-    fake_usb_manager_.AddBinding(mojo::MakeRequest(&usb_manager_ptr));
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager;
+    fake_usb_manager_.AddReceiver(usb_manager.InitWithNewPipeAndPassReceiver());
     UsbDeviceManager::Get(browser_context())
-        ->SetDeviceManagerForTesting(std::move(usb_manager_ptr));
+        ->SetDeviceManagerForTesting(std::move(usb_manager));
     base::RunLoop().RunUntilIdle();
 
     std::vector<device::mojom::UsbConfigurationInfoPtr> configs;
diff --git a/extensions/browser/api/usb/usb_device_manager.cc b/extensions/browser/api/usb/usb_device_manager.cc
index c7f23b6d..28428ca0 100644
--- a/extensions/browser/api/usb/usb_device_manager.cc
+++ b/extensions/browser/api/usb/usb_device_manager.cc
@@ -191,19 +191,20 @@
   if (device_manager_)
     return;
 
-  // Request UsbDeviceManagerPtr from DeviceService.
+  // Receive mojo::Remote<UsbDeviceManager> from DeviceService.
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  content::GetSystemConnector()->BindInterface(
-      device::mojom::kServiceName, mojo::MakeRequest(&device_manager_));
+  content::GetSystemConnector()->Connect(
+      device::mojom::kServiceName,
+      device_manager_.BindNewPipeAndPassReceiver());
 
   SetUpDeviceManagerConnection();
 }
 
 void UsbDeviceManager::SetDeviceManagerForTesting(
-    device::mojom::UsbDeviceManagerPtr fake_device_manager) {
+    mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_device_manager) {
   DCHECK(!device_manager_);
   DCHECK(fake_device_manager);
-  device_manager_ = std::move(fake_device_manager);
+  device_manager_.Bind(std::move(fake_device_manager));
   SetUpDeviceManagerConnection();
 }
 
@@ -265,7 +266,7 @@
 
 void UsbDeviceManager::SetUpDeviceManagerConnection() {
   DCHECK(device_manager_);
-  device_manager_.set_connection_error_handler(
+  device_manager_.set_disconnect_handler(
       base::BindOnce(&UsbDeviceManager::OnDeviceManagerConnectionError,
                      base::Unretained(this)));
 
diff --git a/extensions/browser/api/usb/usb_device_manager.h b/extensions/browser/api/usb/usb_device_manager.h
index 5e15444f..a99c3d7b 100644
--- a/extensions/browser/api/usb/usb_device_manager.h
+++ b/extensions/browser/api/usb/usb_device_manager.h
@@ -15,6 +15,8 @@
 #include "extensions/browser/event_router.h"
 #include "extensions/common/api/usb.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 #include "services/device/public/mojom/usb_manager_client.mojom.h"
 
@@ -73,7 +75,7 @@
   void EnsureConnectionWithDeviceManager();
 
   void SetDeviceManagerForTesting(
-      device::mojom::UsbDeviceManagerPtr fake_device_manager);
+      mojo::PendingRemote<device::mojom::UsbDeviceManager> fake_device_manager);
 
  private:
   friend class BrowserContextKeyedAPIFactory<UsbDeviceManager>;
@@ -116,7 +118,7 @@
   std::map<std::string, device::mojom::UsbDeviceInfoPtr> devices_;
 
   // Connection to |device_manager_instance_|.
-  device::mojom::UsbDeviceManagerPtr device_manager_;
+  mojo::Remote<device::mojom::UsbDeviceManager> device_manager_;
   mojo::AssociatedBinding<device::mojom::UsbDeviceManagerClient>
       client_binding_;
 
diff --git a/fuchsia/fidl/cast/application_config.fidl b/fuchsia/fidl/cast/application_config.fidl
index 070bb95..6e7505b8 100644
--- a/fuchsia/fidl/cast/application_config.fidl
+++ b/fuchsia/fidl/cast/application_config.fidl
@@ -21,6 +21,10 @@
   /// If false, then touch input is disabled.
   /// If unset, then the caller is allowed to enable or disable input.
   4: bool touch_enabled_policy;
+
+  // If true, enable remote debugging for this application.
+  // if false or unset, remote debugging is disabled for this application.
+  5: bool enable_remote_debugging;
 };
 
 /// Service interface for working with application configurations.
diff --git a/gpu/config/gpu_info_collector_win.cc b/gpu/config/gpu_info_collector_win.cc
index dedcb757..65feab8 100644
--- a/gpu/config/gpu_info_collector_win.cc
+++ b/gpu/config/gpu_info_collector_win.cc
@@ -29,6 +29,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/scoped_com_initializer.h"
+#include "base/win/windows_version.h"
 #include "build/branding_buildflags.h"
 #include "third_party/vulkan/include/vulkan/vulkan.h"
 
@@ -111,7 +112,7 @@
 
   bool found_amd = false;
   bool found_intel = false;
-  bool amd_is_primary = false;
+  bool found_nvidia = false;
 
   UINT i;
   Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
@@ -135,6 +136,19 @@
       DLOG(ERROR) << "Unable to retrieve the umd version of adapter: "
                   << desc.Description << " HR: " << std::hex << hr;
     }
+    switch (device.vendor_id) {
+      case 0x8086:
+        found_intel = true;
+        break;
+      case 0x1002:
+        found_amd = true;
+        break;
+      case 0x10de:
+        found_nvidia = true;
+        break;
+      default:
+        break;
+    }
 
     if (i == 0) {
       gpu_info->gpu = device;
@@ -143,19 +157,20 @@
     }
   }
 
-  if (found_amd && found_intel) {
-    // Potential AMD Switchable system found.
-    if (!amd_is_primary) {
-      // Some machines aren't properly detected as AMD switchable, but count
-      // them anyway. This may erroneously count machines where there are
-      // independent AMD and Intel cards and the AMD isn't hooked up to
-      // anything, but that should be rare.
-      gpu_info->amd_switchable = true;
-    } else {
+  if (found_intel && base::win::GetVersion() < base::win::Version::WIN10) {
+    // Since Windows 10 (and Windows 8.1 on some systems), switchable graphics
+    // platforms are managed by Windows and each adapter is accessible as
+    // separate devices.
+    // See https://msdn.microsoft.com/en-us/windows/dn265501(v=vs.80)
+    if (found_amd) {
       bool is_amd_switchable = false;
       uint32_t active_vendor = 0, active_device = 0;
       GetAMDSwitchableInfo(&is_amd_switchable, &active_vendor, &active_device);
       gpu_info->amd_switchable = is_amd_switchable;
+    } else if (found_nvidia) {
+      // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
+      HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
+      gpu_info->optimus = nvd3d9wrap != nullptr;
     }
   }
 
@@ -511,13 +526,7 @@
 
 bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
   TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo");
-
   DCHECK(gpu_info);
-
-  // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
-  HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
-  gpu_info->optimus = nvd3d9wrap != nullptr;
-
   // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE.
   return CollectDriverInfoD3D(gpu_info);
 }
diff --git a/gpu/config/gpu_test_config.cc b/gpu/config/gpu_test_config.cc
index dea06bd2..776cd48 100644
--- a/gpu/config/gpu_test_config.cc
+++ b/gpu/config/gpu_test_config.cc
@@ -71,6 +71,8 @@
         return GPUTestConfig::kOsMacHighSierra;
       case 14:
         return GPUTestConfig::kOsMacMojave;
+      case 15:
+        return GPUTestConfig::kOsMacCatalina;
     }
   }
 #elif defined(OS_ANDROID)
@@ -194,6 +196,7 @@
     case kOsMacSierra:
     case kOsMacHighSierra:
     case kOsMacMojave:
+    case kOsMacCatalina:
     case kOsLinux:
     case kOsChromeOS:
     case kOsAndroid:
diff --git a/gpu/config/gpu_test_config.h b/gpu/config/gpu_test_config.h
index 79a3529a..315f670 100644
--- a/gpu/config/gpu_test_config.h
+++ b/gpu/config/gpu_test_config.h
@@ -25,25 +25,31 @@
     kOsWinVista = 1 << 1,
     kOsWin7 = 1 << 2,
     kOsWin8 = 1 << 3,
-    kOsMacLeopard = 1 << 4,
-    kOsMacSnowLeopard = 1 << 5,
-    kOsMacLion = 1 << 6,
-    kOsMacMountainLion = 1 << 7,
-    kOsMacMavericks = 1 << 8,
-    kOsMacYosemite = 1 << 9,
-    kOsMacElCapitan = 1 << 10,
-    kOsMacSierra = 1 << 11,
-    kOsMacHighSierra = 1 << 12,
-    kOsMacMojave = 1 << 13,
+    kOsWin10 = 1 << 4,
+    kOsWin = kOsWinXP | kOsWinVista | kOsWin7 | kOsWin8 | kOsWin10,
+    // Jump over a few bits for future Windows versions.
+    kOsMacLeopard = 1 << 10,
+    kOsMacSnowLeopard = 1 << 11,
+    kOsMacLion = 1 << 12,
+    kOsMacMountainLion = 1 << 13,
+    kOsMacMavericks = 1 << 14,
+    kOsMacYosemite = 1 << 15,
+    kOsMacElCapitan = 1 << 16,
+    kOsMacSierra = 1 << 17,
+    kOsMacHighSierra = 1 << 18,
+    kOsMacMojave = 1 << 19,
+    kOsMacCatalina = 1 << 20,
     kOsMac = kOsMacLeopard | kOsMacSnowLeopard | kOsMacLion |
              kOsMacMountainLion | kOsMacMavericks | kOsMacYosemite |
-             kOsMacElCapitan | kOsMacSierra | kOsMacHighSierra | kOsMacMojave,
-    kOsLinux = 1 << 14,
-    kOsChromeOS = 1 << 15,
-    kOsAndroid = 1 << 16,
-    kOsWin10 = 1 << 17,
-    kOsWin = kOsWinXP | kOsWinVista | kOsWin7 | kOsWin8 | kOsWin10,
-    kOsFuchsia = 1 << 18,
+             kOsMacElCapitan | kOsMacSierra | kOsMacHighSierra | kOsMacMojave |
+             kOsMacCatalina,
+    // Jump over a few bits for future OSX versions.
+    kOsLinux = 1 << 25,
+    kOsChromeOS = 1 << 26,
+    kOsAndroid = 1 << 27,
+    kOsFuchsia = 1 << 28,
+    // If we run out of bits, please retire older OS versions, like WinXP,
+    // MacLeopard, etc., for which we no longer have bots.
   };
 
   enum BuildType {
@@ -141,4 +147,3 @@
 }  // namespace gpu
 
 #endif  // GPU_CONFIG_GPU_TEST_CONFIG_H_
-
diff --git a/gpu/config/gpu_test_expectations_parser.cc b/gpu/config/gpu_test_expectations_parser.cc
index d106862..75c0802 100644
--- a/gpu/config/gpu_test_expectations_parser.cc
+++ b/gpu/config/gpu_test_expectations_parser.cc
@@ -46,6 +46,7 @@
   kConfigMacSierra,
   kConfigMacHighSierra,
   kConfigMacMojave,
+  kConfigMacCatalina,
   kConfigMac,
   kConfigLinux,
   kConfigChromeOS,
@@ -103,6 +104,7 @@
     {"sierra", GPUTestConfig::kOsMacSierra},
     {"highsierra", GPUTestConfig::kOsMacHighSierra},
     {"mojave", GPUTestConfig::kOsMacMojave},
+    {"catalina", GPUTestConfig::kOsMacCatalina},
     {"mac", GPUTestConfig::kOsMac},
     {"linux", GPUTestConfig::kOsLinux},
     {"chromeos", GPUTestConfig::kOsChromeOS},
@@ -267,6 +269,7 @@
       case kConfigMacSierra:
       case kConfigMacHighSierra:
       case kConfigMacMojave:
+      case kConfigMacCatalina:
       case kConfigMac:
       case kConfigLinux:
       case kConfigChromeOS:
@@ -329,6 +332,7 @@
       case kConfigMacSierra:
       case kConfigMacHighSierra:
       case kConfigMacMojave:
+      case kConfigMacCatalina:
       case kConfigMac:
       case kConfigLinux:
       case kConfigChromeOS:
@@ -454,6 +458,7 @@
     case kConfigMacSierra:
     case kConfigMacHighSierra:
     case kConfigMacMojave:
+    case kConfigMacCatalina:
     case kConfigMac:
     case kConfigLinux:
     case kConfigChromeOS:
@@ -568,4 +573,3 @@
 }
 
 }  // namespace gpu
-
diff --git a/gpu/config/gpu_test_expectations_parser_unittest.cc b/gpu/config/gpu_test_expectations_parser_unittest.cc
index d1000f6..f2d23dbf 100644
--- a/gpu/config/gpu_test_expectations_parser_unittest.cc
+++ b/gpu/config/gpu_test_expectations_parser_unittest.cc
@@ -39,6 +39,7 @@
     {{"SIERRA", GPUTestConfig::kOsMacSierra}, kOsFamilyMac},
     {{"HIGHSIERRA", GPUTestConfig::kOsMacHighSierra}, kOsFamilyMac},
     {{"MOJAVE", GPUTestConfig::kOsMacMojave}, kOsFamilyMac},
+    {{"CATALINA", GPUTestConfig::kOsMacCatalina}, kOsFamilyMac},
     {{"LINUX", GPUTestConfig::kOsLinux}, {"LINUX", GPUTestConfig::kOsLinux}},
     {{"CHROMEOS", GPUTestConfig::kOsChromeOS},
      {"CHROMEOS", GPUTestConfig::kOsChromeOS}},
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index dee56ee..647b622 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -1535,25 +1535,25 @@
     # Fuchsia bots.
     builders {
       name: "Fuchsia ARM64"
-      mixins: "linux-ci"
+      mixins: "linux-ci-goma-rbe-prod"
       mixins: "builderless"
     }
 
     builders {
       name: "fuchsia-arm64-cast"
-      mixins: "linux-ci"
+      mixins: "linux-ci-goma-rbe-prod"
       mixins: "builderless"
     }
 
     builders {
       name: "Fuchsia x64"
-      mixins: "linux-ci"
+      mixins: "linux-ci-goma-rbe-prod"
       mixins: "builderless"
     }
 
     builders {
       name: "fuchsia-x64-cast"
-      mixins: "linux-ci"
+      mixins: "linux-ci-goma-rbe-prod"
       mixins: "builderless"
     }
 
@@ -1562,6 +1562,7 @@
       mixins: "fyi-ci"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
 
     builders {
@@ -1569,6 +1570,7 @@
       mixins: "fyi-ci"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
 
     builders {
@@ -1576,6 +1578,7 @@
       mixins: "fyi-ci"
       mixins: "linux-xenial"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
 
     builders {
@@ -4269,35 +4272,42 @@
       mixins: "linux-try"
       name: "fuchsia_arm64"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       mixins: "builderless"
       mixins: "linux-try"
+      mixins: "goma-rbe-prod"
       name: "fuchsia-arm64-cast"
     }
     builders {
       mixins: "builderless"
       mixins: "linux-try"
+      mixins: "goma-rbe-prod"
       name: "fuchsia-fyi-arm64-rel"
     }
     builders {
       mixins: "builderless"
       mixins: "linux-try"
+      mixins: "goma-rbe-prod"
       name: "fuchsia-fyi-x64-dbg"
     }
     builders {
       mixins: "builderless"
       mixins: "linux-try"
+      mixins: "goma-rbe-prod"
       name: "fuchsia-fyi-x64-rel"
     }
     builders {
       mixins: "linux-try"
       name: "fuchsia_x64"
       mixins: "builderless"
+      mixins: "goma-rbe-prod"
     }
     builders {
       mixins: "builderless"
       mixins: "linux-try"
+      mixins: "goma-rbe-prod"
       name: "fuchsia-x64-cast"
     }
     builders {
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index 7d62bfa..4cdd960 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -3213,6 +3213,39 @@
     name: "buildbucket/luci.chromium.ci/Cast Linux"
     category: "week10|linux"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Fuchsia ARM64"
+    category: "week11|fuchsia|arm64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/fuchsia-arm64-cast"
+    category: "week11|fuchsia|arm64"
+    short_name: "cast"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Fuchsia x64"
+    category: "week11|fuchsia|x64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/fuchsia-x64-cast"
+    category: "week11|fuchsia|x64"
+    short_name: "cast"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/fuchsia-fyi-arm64-rel"
+    category: "week11|fuchsia|fyi"
+    short_name: "arm64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/fuchsia-fyi-x64-dbg"
+    category: "week11|fuchsia|fyi"
+    short_name: "x64 dbg"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/fuchsia-fyi-x64-rel"
+    category: "week11|fuchsia|fyi"
+    short_name: "x64 rel"
+  }
 }
 
 consoles {
diff --git a/ios/showcase/OWNERS b/ios/showcase/OWNERS
index f72b1e5..3066684 100644
--- a/ios/showcase/OWNERS
+++ b/ios/showcase/OWNERS
@@ -2,3 +2,4 @@
 
 # TEAM: ios-directory-owners@chromium.org
 # OS: iOS
+# COMPONENT: UI>Browser>Mobile
diff --git a/jingle/OWNERS b/jingle/OWNERS
index d728905..3526efa 100644
--- a/jingle/OWNERS
+++ b/jingle/OWNERS
@@ -1,2 +1,4 @@
 sergeyu@chromium.org
 zea@chromium.org
+# COMPONENT: Blink>WebRTC
+# TEAM: net-dev@chromium.org
diff --git a/jingle/notifier/OWNERS b/jingle/notifier/OWNERS
new file mode 100644
index 0000000..b1b8445d4
--- /dev/null
+++ b/jingle/notifier/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: Internals>Printing
diff --git a/media/audio/fuchsia/OWNERS b/media/audio/fuchsia/OWNERS
index e7034ea..c1b5845 100644
--- a/media/audio/fuchsia/OWNERS
+++ b/media/audio/fuchsia/OWNERS
@@ -1 +1,4 @@
 file://build/fuchsia/OWNERS
+# COMPONENT: Fuchsia
+# OS: Fuchsia
+# TEAM: cr-fuchsia@chromium.org
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index 5b4a2f7..4baba10 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -16,6 +16,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/bits.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
@@ -33,6 +34,7 @@
 #include "media/gpu/gpu_video_encode_accelerator_helpers.h"
 #include "media/gpu/image_processor_factory.h"
 #include "media/gpu/macros.h"
+#include "media/video/h264_level_limits.h"
 #include "media/video/h264_parser.h"
 
 #define NOTIFY_ERROR(x)                      \
@@ -1498,13 +1500,42 @@
     ctrls.push_back(ctrl);
 
     // Set H.264 output level from config. Use Level 4.0 as fallback default.
-    int32_t level_value = V4L2Device::H264LevelIdcToV4L2H264Level(
-        config.h264_output_level.value_or(
-            VideoEncodeAccelerator::kDefaultH264Level));
-    if (level_value < 0) {
-      NOTIFY_ERROR(kInvalidArgumentError);
-      return false;
+    uint8_t h264_level =
+        config.h264_output_level.value_or(H264SPS::kLevelIDC4p0);
+    constexpr size_t kH264MacroblockSizeInPixels = 16;
+    const uint32_t framerate = config.initial_framerate.value_or(
+        VideoEncodeAccelerator::kDefaultFramerate);
+    const uint32_t mb_width =
+        base::bits::Align(config.input_visible_size.width(),
+                          kH264MacroblockSizeInPixels) /
+        kH264MacroblockSizeInPixels;
+    const uint32_t mb_height =
+        base::bits::Align(config.input_visible_size.height(),
+                          kH264MacroblockSizeInPixels) /
+        kH264MacroblockSizeInPixels;
+    const uint32_t framesize_in_mbs = mb_width * mb_height;
+
+    // Check whether the h264 level is valid.
+    if (!CheckH264LevelLimits(config.output_profile, h264_level,
+                              config.initial_bitrate, framerate,
+                              framesize_in_mbs)) {
+      base::Optional<uint8_t> valid_level =
+          FindValidH264Level(config.output_profile, config.initial_bitrate,
+                             framerate, framesize_in_mbs);
+      if (!valid_level) {
+        VLOGF(1) << "Could not find a valid h264 level for"
+                 << " profile=" << config.output_profile
+                 << " bitrate=" << config.initial_bitrate
+                 << " framerate=" << framerate
+                 << " size=" << config.input_visible_size.ToString();
+        NOTIFY_ERROR(kInvalidArgumentError);
+        return false;
+      }
+
+      h264_level = *valid_level;
     }
+
+    int32_t level_value = V4L2Device::H264LevelIdcToV4L2H264Level(h264_level);
     memset(&ctrl, 0, sizeof(ctrl));
     ctrl.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL;
     ctrl.value = level_value;
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn
index ecaa728..2d19903 100644
--- a/media/gpu/vaapi/BUILD.gn
+++ b/media/gpu/vaapi/BUILD.gn
@@ -166,6 +166,7 @@
 source_set("unit_test") {
   testonly = true
   sources = [
+    "h264_encoder_unittest.cc",
     "vaapi_image_decode_accelerator_worker_unittest.cc",
     "vaapi_video_decode_accelerator_unittest.cc",
   ]
diff --git a/media/gpu/vaapi/h264_encoder.cc b/media/gpu/vaapi/h264_encoder.cc
index ddde6ee..8e15bc5 100644
--- a/media/gpu/vaapi/h264_encoder.cc
+++ b/media/gpu/vaapi/h264_encoder.cc
@@ -93,13 +93,28 @@
   mb_height_ = coded_size_.height() / kH264MacroblockSizeInPixels;
 
   profile_ = config.output_profile;
-  level_ = config.h264_output_level.value_or(
-      VideoEncodeAccelerator::kDefaultH264Level);
+  level_ = config.h264_output_level.value_or(H264SPS::kLevelIDC4p0);
   uint32_t initial_framerate = config.initial_framerate.value_or(
       VideoEncodeAccelerator::kDefaultFramerate);
+
+  // Checks if |level_| is valid. If it is invalid, set |level_| to a minimum
+  // level that comforts Table A-1 in H.264 spec with specified bitrate,
+  // framerate and dimension.
   if (!CheckH264LevelLimits(profile_, level_, config.initial_bitrate,
-                            initial_framerate, mb_width_ * mb_height_))
-    return false;
+                            initial_framerate, mb_width_ * mb_height_)) {
+    base::Optional<uint8_t> valid_level =
+        FindValidH264Level(profile_, config.initial_bitrate, initial_framerate,
+                           mb_width_ * mb_height_);
+    if (!valid_level) {
+      VLOGF(1) << "Could not find a valid h264 level for"
+               << " profile=" << profile_
+               << " bitrate=" << config.initial_bitrate
+               << " framerate=" << initial_framerate
+               << " size=" << config.input_visible_size.ToString();
+      return false;
+    }
+    level_ = *valid_level;
+  }
 
   curr_params_.max_ref_pic_list0_size =
       std::min(kMaxRefIdxL0Size, ave_config.max_num_ref_frames & 0xffff);
diff --git a/media/gpu/vaapi/h264_encoder.h b/media/gpu/vaapi/h264_encoder.h
index 3b6c855..ea65c7c9 100644
--- a/media/gpu/vaapi/h264_encoder.h
+++ b/media/gpu/vaapi/h264_encoder.h
@@ -113,6 +113,8 @@
   bool PrepareEncodeJob(EncodeJob* encode_job) override;
 
  private:
+  friend class H264EncoderTest;
+
   // Fill current_sps_ and current_pps_ with current encoding state parameters.
   void UpdateSPS();
   void UpdatePPS();
diff --git a/media/gpu/vaapi/h264_encoder_unittest.cc b/media/gpu/vaapi/h264_encoder_unittest.cc
new file mode 100644
index 0000000..b0c0f4c
--- /dev/null
+++ b/media/gpu/vaapi/h264_encoder_unittest.cc
@@ -0,0 +1,95 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/vaapi/h264_encoder.h"
+
+#include <memory>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+
+namespace media {
+namespace {
+
+AcceleratedVideoEncoder::Config kDefaultAVEConfig{10};
+
+VideoEncodeAccelerator::Config kDefaultVEAConfig(
+    PIXEL_FORMAT_I420,
+    gfx::Size(1280, 720),
+    H264PROFILE_BASELINE,
+    14000000 /* = maximum bitrate in bits per second for level 3.1 */,
+    VideoEncodeAccelerator::kDefaultFramerate,
+    base::nullopt /* gop_length */,
+    base::nullopt /* h264 output level*/,
+    VideoEncodeAccelerator::Config::StorageType::kShmem,
+    VideoEncodeAccelerator::Config::ContentType::kCamera);
+
+class MockH264Accelerator : public H264Encoder::Accelerator {
+ public:
+  MockH264Accelerator() = default;
+  MOCK_METHOD1(
+      GetPicture,
+      scoped_refptr<H264Picture>(AcceleratedVideoEncoder::EncodeJob* job));
+  MOCK_METHOD3(SubmitPackedHeaders,
+               bool(AcceleratedVideoEncoder::EncodeJob*,
+                    scoped_refptr<H264BitstreamBuffer>,
+                    scoped_refptr<H264BitstreamBuffer>));
+  MOCK_METHOD7(SubmitFrameParameters,
+               bool(AcceleratedVideoEncoder::EncodeJob*,
+                    const H264Encoder::EncodeParams&,
+                    const H264SPS&,
+                    const H264PPS&,
+                    scoped_refptr<H264Picture>,
+                    const std::list<scoped_refptr<H264Picture>>&,
+                    const std::list<scoped_refptr<H264Picture>>&));
+};
+}  // namespace
+
+class H264EncoderTest : public ::testing::Test {
+ public:
+  H264EncoderTest() = default;
+  void SetUp() override;
+
+  void ExpectLevel(uint8_t level) { EXPECT_EQ(encoder_->level_, level); }
+
+ protected:
+  std::unique_ptr<H264Encoder> encoder_;
+  MockH264Accelerator* accelerator_;
+};
+
+void H264EncoderTest::SetUp() {
+  auto mock_accelerator = std::make_unique<MockH264Accelerator>();
+  accelerator_ = mock_accelerator.get();
+  encoder_ = std::make_unique<H264Encoder>(std::move(mock_accelerator));
+
+  // Set default behaviors for mock methods for convenience.
+  ON_CALL(*accelerator_, GetPicture(_))
+      .WillByDefault(Invoke([](AcceleratedVideoEncoder::EncodeJob*) {
+        return new H264Picture();
+      }));
+  ON_CALL(*accelerator_, SubmitPackedHeaders(_, _, _))
+      .WillByDefault(Return(true));
+  ON_CALL(*accelerator_, SubmitFrameParameters(_, _, _, _, _, _, _))
+      .WillByDefault(Return(true));
+}
+
+TEST_F(H264EncoderTest, Initialize) {
+  VideoEncodeAccelerator::Config vea_config = kDefaultVEAConfig;
+  AcceleratedVideoEncoder::Config ave_config = kDefaultAVEConfig;
+  EXPECT_TRUE(encoder_->Initialize(vea_config, ave_config));
+  // Profile is unspecified, H264Encoder will select the default level, 4.0.
+  // 4.0 will be proper with |vea_config|'s values.
+  ExpectLevel(H264SPS::kLevelIDC4p0);
+
+  // Initialize with 4k size. The level will be adjusted to 5.1 by H264Encoder.
+  vea_config.input_visible_size.SetSize(3840, 2160);
+  EXPECT_TRUE(encoder_->Initialize(vea_config, ave_config));
+  ExpectLevel(H264SPS::kLevelIDC5p1);
+}
+
+}  // namespace media
diff --git a/media/gpu/vaapi/vp8_encoder.cc b/media/gpu/vaapi/vp8_encoder.cc
index fa1212f5..c2b4f03 100644
--- a/media/gpu/vaapi/vp8_encoder.cc
+++ b/media/gpu/vaapi/vp8_encoder.cc
@@ -162,6 +162,12 @@
   // TODO(sprang): Make this dynamic. Value based on reference implementation
   // in libyami (https://github.com/intel/libyami).
   current_frame_hdr_.loopfilter_hdr.level = 19;
+
+  // b/138840822: Set mb_no_skip_coeff and loop_filter_adj_enable to 1 as a
+  // workaround of color artifacts issue with a kepler device hw decoder and
+  // ffmpeg sw decoder.
+  current_frame_hdr_.mb_no_skip_coeff = 1;
+  current_frame_hdr_.loopfilter_hdr.loop_filter_adj_enable = 1;
 }
 
 void VP8Encoder::UpdateFrameHeader(bool keyframe) {
diff --git a/media/video/h264_level_limits.cc b/media/video/h264_level_limits.cc
index 5437e7b..a51d34a 100644
--- a/media/video/h264_level_limits.cc
+++ b/media/video/h264_level_limits.cc
@@ -5,6 +5,7 @@
 #include "media/video/h264_level_limits.h"
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "media/video/h264_parser.h"
 
 namespace media {
@@ -141,4 +142,27 @@
   return true;
 }
 
+base::Optional<uint8_t> FindValidH264Level(VideoCodecProfile profile,
+                                           uint32_t bitrate,
+                                           uint32_t framerate,
+                                           uint32_t framesize_in_mbs) {
+  constexpr uint8_t kH264Levels[] = {
+      H264SPS::kLevelIDC1p0, H264SPS::kLevelIDC1B,  H264SPS::kLevelIDC1p1,
+      H264SPS::kLevelIDC1p2, H264SPS::kLevelIDC1p3, H264SPS::kLevelIDC2p0,
+      H264SPS::kLevelIDC2p1, H264SPS::kLevelIDC2p2, H264SPS::kLevelIDC3p0,
+      H264SPS::kLevelIDC3p1, H264SPS::kLevelIDC3p2, H264SPS::kLevelIDC4p0,
+      H264SPS::kLevelIDC4p1, H264SPS::kLevelIDC4p2, H264SPS::kLevelIDC5p0,
+      H264SPS::kLevelIDC5p1, H264SPS::kLevelIDC5p2, H264SPS::kLevelIDC6p0,
+      H264SPS::kLevelIDC6p1, H264SPS::kLevelIDC6p2,
+  };
+
+  for (const uint8_t level : kH264Levels) {
+    if (CheckH264LevelLimits(profile, level, bitrate, framerate,
+                             framesize_in_mbs)) {
+      return level;
+    }
+  }
+  return base::nullopt;
+}
+
 }  // namespace media
diff --git a/media/video/h264_level_limits.h b/media/video/h264_level_limits.h
index 0772dd2..fcc8eb8c8b 100644
--- a/media/video/h264_level_limits.h
+++ b/media/video/h264_level_limits.h
@@ -7,6 +7,7 @@
 
 #include <stddef.h>
 
+#include "base/optional.h"
 #include "media/base/media_export.h"
 #include "media/base/video_codecs.h"
 
@@ -37,6 +38,14 @@
                                        uint32_t framerate,
                                        uint32_t framesize_in_mbs);
 
+// Return a minimum level that comforts Table A-1 in spec with |profile|,
+// |bitrate|, |framerate| and |framesize_in_mbs|. If there is no proper level,
+// returns base::nullopt.
+base::Optional<uint8_t> MEDIA_EXPORT
+FindValidH264Level(VideoCodecProfile profile,
+                   uint32_t bitrate,
+                   uint32_t framerate,
+                   uint32_t framesize_in_mbs);
 }  // namespace media
 
 #endif  // MEDIA_VIDEO_H264_LEVEL_LIMITS_H_
diff --git a/media/video/video_encode_accelerator.cc b/media/video/video_encode_accelerator.cc
index 67d6cedc..3e61991 100644
--- a/media/video/video_encode_accelerator.cc
+++ b/media/video/video_encode_accelerator.cc
@@ -52,8 +52,7 @@
       initial_framerate(initial_framerate.value_or(
           VideoEncodeAccelerator::kDefaultFramerate)),
       gop_length(gop_length),
-      h264_output_level(h264_output_level.value_or(
-          VideoEncodeAccelerator::kDefaultH264Level)),
+      h264_output_level(h264_output_level),
       storage_type(storage_type),
       content_type(content_type) {}
 
diff --git a/media/video/video_encode_accelerator.h b/media/video/video_encode_accelerator.h
index 90ca6a1..b394f4b 100644
--- a/media/video/video_encode_accelerator.h
+++ b/media/video/video_encode_accelerator.h
@@ -98,11 +98,8 @@
     kErrorMax = kPlatformFailureError
   };
 
-  // Unified default values for all VEA implementations.
-  enum {
-    kDefaultFramerate = 30,
-    kDefaultH264Level = H264SPS::kLevelIDC4p0,
-  };
+  // A default framerate for all VEA implementations.
+  enum { kDefaultFramerate = 30 };
 
   // Parameters required for VEA initialization.
   struct MEDIA_EXPORT Config {
@@ -154,11 +151,9 @@
     base::Optional<uint32_t> gop_length;
 
     // Codec level of encoded output stream for H264 only. This value should
-    // be aligned to the H264 standard definition of SPS.level_idc. The only
-    // exception is in Main and Baseline profile we still use
-    // |h264_output_level|=9 for Level 1b, which should set level_idc to 11 and
-    // constraint_set3_flag to 1 (Spec A.3.1 and A.3.2). This is optional and
-    // use |kDefaultH264Level| if not given.
+    // be aligned to the H264 standard definition of SPS.level_idc.
+    // If this is not given, VideoEncodeAccelerator selects one of proper H.264
+    // levels for |input_visible_size| and |initial_framerate|.
     base::Optional<uint8_t> h264_output_level;
 
     // The storage type of video frame provided on Encode().
diff --git a/mojo/public/cpp/base/big_buffer.cc b/mojo/public/cpp/base/big_buffer.cc
index 6c4eabfa..72da4c8c 100644
--- a/mojo/public/cpp/base/big_buffer.cc
+++ b/mojo/public/cpp/base/big_buffer.cc
@@ -44,6 +44,38 @@
 
 }  // namespace internal
 
+namespace {
+
+void TryCreateSharedMemory(
+    size_t size,
+    BigBuffer::StorageType* storage_type,
+    base::Optional<internal::BigBufferSharedMemoryRegion>* shared_memory) {
+  if (size > BigBuffer::kMaxInlineBytes) {
+    auto buffer = mojo::SharedBufferHandle::Create(size);
+    if (buffer.is_valid()) {
+      internal::BigBufferSharedMemoryRegion shm_region(std::move(buffer), size);
+      if (shm_region.memory()) {
+        *storage_type = BigBuffer::StorageType::kSharedMemory;
+        shared_memory->emplace(std::move(shm_region));
+        return;
+      }
+    }
+
+    if (size > kMaxFallbackInlineBytes) {
+      // The data is too large to even bother with inline fallback, so we
+      // instead produce an invalid buffer. This will always fail validation on
+      // the receiving end.
+      *storage_type = BigBuffer::StorageType::kInvalidBuffer;
+      return;
+    }
+  }
+
+  // We can use inline memory.
+  *storage_type = BigBuffer::StorageType::kBytes;
+}
+
+}  // namespace
+
 // static
 constexpr size_t BigBuffer::kMaxInlineBytes;
 
@@ -62,6 +94,15 @@
     : storage_type_(StorageType::kSharedMemory),
       shared_memory_(std::move(shared_memory)) {}
 
+BigBuffer::BigBuffer(size_t size) {
+  TryCreateSharedMemory(size, &storage_type_, &shared_memory_);
+  if (storage_type_ == BigBuffer::StorageType::kBytes) {
+    // Either |size| is small enough or shared memory allocation failed, and
+    // fallback to inline allocation is feasible.
+    bytes_ = std::vector<uint8_t>(size);
+  }
+}
+
 BigBuffer::~BigBuffer() = default;
 
 BigBuffer& BigBuffer::operator=(BigBuffer&& other) = default;
@@ -107,31 +148,18 @@
 BigBufferView::BigBufferView(BigBufferView&& other) = default;
 
 BigBufferView::BigBufferView(base::span<const uint8_t> bytes) {
-  if (bytes.size() > BigBuffer::kMaxInlineBytes) {
-    auto buffer = mojo::SharedBufferHandle::Create(bytes.size());
-    if (buffer.is_valid()) {
-      storage_type_ = BigBuffer::StorageType::kSharedMemory;
-      shared_memory_.emplace(std::move(buffer), bytes.size());
-      if (shared_memory_->buffer_mapping_) {
-        std::copy(bytes.begin(), bytes.end(),
-                  static_cast<uint8_t*>(shared_memory_->buffer_mapping_.get()));
-        return;
-      }
-    }
-
-    if (bytes.size() > kMaxFallbackInlineBytes) {
-      // The data is too large to even bother with inline fallback, so we
-      // instead produce an invalid buffer. This will always fail validation on
-      // the receiving end.
-      storage_type_ = BigBuffer::StorageType::kInvalidBuffer;
-      return;
-    }
+  TryCreateSharedMemory(bytes.size(), &storage_type_, &shared_memory_);
+  if (storage_type_ == BigBuffer::StorageType::kSharedMemory) {
+    DCHECK(shared_memory_->memory());
+    std::copy(bytes.begin(), bytes.end(),
+              static_cast<uint8_t*>(shared_memory_->memory()));
+    return;
   }
-
-  // Either the data is small enough or shared memory allocation failed. Either
-  // way we fall back to directly referencing the input bytes.
-  storage_type_ = BigBuffer::StorageType::kBytes;
-  bytes_ = bytes;
+  if (storage_type_ == BigBuffer::StorageType::kBytes) {
+    // Either the data is small enough or shared memory allocation failed.
+    // Either way we fall back to directly referencing the input bytes.
+    bytes_ = bytes;
+  }
 }
 
 BigBufferView::~BigBufferView() = default;
diff --git a/mojo/public/cpp/base/big_buffer.h b/mojo/public/cpp/base/big_buffer.h
index 03b5a4e..579cf358 100644
--- a/mojo/public/cpp/base/big_buffer.h
+++ b/mojo/public/cpp/base/big_buffer.h
@@ -88,6 +88,11 @@
   // for general-purpose use.
   explicit BigBuffer(internal::BigBufferSharedMemoryRegion shared_memory);
 
+  // Constructs a BigBuffer with the given size. The contents of buffer memory
+  // are uninitialized. Buffers constructed this way must be filled completely
+  // before transfer to avoid leaking information to less privileged processes.
+  explicit BigBuffer(size_t size);
+
   ~BigBuffer();
 
   BigBuffer& operator=(BigBuffer&& other);
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index c9d296a0..149fdf6 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -66,6 +66,7 @@
 #include "net/ssl/ssl_info.h"
 #include "net/ssl/ssl_private_key.h"
 #include "url/gurl.h"
+#include "url/scheme_host_port.h"
 #include "url/url_canon.h"
 
 #if BUILDFLAG(ENABLE_REPORTING)
@@ -324,7 +325,8 @@
   if (target == HttpAuth::AUTH_SERVER &&
       auth_controllers_[target]->NeedsHTTP11()) {
     session_->http_server_properties()->SetHTTP11Required(
-        HostPortPair::FromURL(request_->url));
+        HttpServerProperties::GetNormalizedSchemeHostPort(request_->url),
+        request_->network_isolation_key);
   }
 
   bool keep_alive = false;
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 9d70bd7a..34169b5 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -7674,6 +7674,225 @@
   EXPECT_TRUE(data1.AllWriteDataConsumed());
 }
 
+#if BUILDFLAG(ENABLE_WEBSOCKETS)
+
+// Variant of above test using WebSockets.
+TEST_F(HttpNetworkTransactionTest, NTLMOverHttp2WithWebsockets) {
+  const GURL kInitialUrl("https://server/");
+  const GURL kWebSocketUrl("wss://server/");
+  HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(
+      MockGetMSTime, MockGenerateRandom, MockGetHostName);
+
+  // Initial request establishes an H2 connection, which will then be reused for
+  // WebSockets. This is needed since WebSockets will reuse H2 connections, but
+  // it won't create a new one.
+  spdy::SpdyHeaderBlock initial_request_headers(
+      spdy_util_.ConstructGetHeaderBlock(kInitialUrl.spec()));
+  spdy::SpdySerializedFrame initial_request(spdy_util_.ConstructSpdyHeaders(
+      1, std::move(initial_request_headers), DEFAULT_PRIORITY, true));
+  spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
+
+  // Settings frame, indicating WebSockets is supported.
+  spdy::SettingsMap settings;
+  settings[spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
+  spdy::SpdySerializedFrame settings_frame(
+      spdy_util_.ConstructSpdySettings(settings));
+
+  // Response headers for first request. Body is never received, but that
+  // shouldn't matter for the purposes of this test.
+  spdy::SpdySerializedFrame initial_response(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+
+  // First WebSocket request, which has no credentials.
+  spdy::SpdyHeaderBlock websocket_request_headers;
+  websocket_request_headers[spdy::kHttp2MethodHeader] = "CONNECT";
+  websocket_request_headers[spdy::kHttp2AuthorityHeader] = "server";
+  websocket_request_headers[spdy::kHttp2SchemeHeader] = "https";
+  websocket_request_headers[spdy::kHttp2PathHeader] = "/";
+  websocket_request_headers[spdy::kHttp2ProtocolHeader] = "websocket";
+  websocket_request_headers["origin"] = "http://server";
+  websocket_request_headers["sec-websocket-version"] = "13";
+  websocket_request_headers["sec-websocket-extensions"] =
+      "permessage-deflate; client_max_window_bits";
+  spdy::SpdySerializedFrame websocket_request(spdy_util_.ConstructSpdyHeaders(
+      3, std::move(websocket_request_headers), MEDIUM, false));
+
+  // Auth challenge to WebSocket request.
+  spdy::SpdyHeaderBlock auth_challenge_headers;
+  auth_challenge_headers[spdy::kHttp2StatusHeader] = "401";
+  auth_challenge_headers["www-authenticate"] = "NTLM";
+  spdy::SpdySerializedFrame websocket_auth_challenge(
+      spdy_util_.ConstructSpdyResponseHeaders(
+          3, std::move(auth_challenge_headers), true));
+
+  MockWrite writes0[] = {CreateMockWrite(initial_request, 0),
+                         CreateMockWrite(settings_ack, 2),
+                         CreateMockWrite(websocket_request, 4),
+                         MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 7)};
+  MockRead reads0[] = {CreateMockRead(settings_frame, 1),
+                       CreateMockRead(initial_response, 3),
+                       CreateMockRead(websocket_auth_challenge, 5),
+                       MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6)};
+
+  // Generate the NTLM messages based on known test data.
+  std::string negotiate_msg;
+  std::string challenge_msg;
+  std::string authenticate_msg;
+  base::Base64Encode(
+      base::StringPiece(
+          reinterpret_cast<const char*>(ntlm::test::kExpectedNegotiateMsg),
+          base::size(ntlm::test::kExpectedNegotiateMsg)),
+      &negotiate_msg);
+  base::Base64Encode(
+      base::StringPiece(
+          reinterpret_cast<const char*>(ntlm::test::kChallengeMsgFromSpecV2),
+          base::size(ntlm::test::kChallengeMsgFromSpecV2)),
+      &challenge_msg);
+  base::Base64Encode(
+      base::StringPiece(
+          reinterpret_cast<const char*>(
+              ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2),
+          base::size(
+              ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2)),
+      &authenticate_msg);
+
+  // Retry yet again using HTTP/1.1.
+  MockWrite writes1[] = {
+      // After restarting with a null identity, this is the
+      // request we should be issuing -- the final header line contains a Type
+      // 1 message.
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: server\r\n"
+                "Connection: Upgrade\r\n"
+                "Authorization: NTLM "),
+      MockWrite(negotiate_msg.c_str()),
+      MockWrite("\r\n"),
+      MockWrite("Origin: http://server\r\n"
+                "Sec-WebSocket-Version: 13\r\n"
+                "Upgrade: websocket\r\n"
+                "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
+                "Sec-WebSocket-Extensions: permessage-deflate; "
+                "client_max_window_bits\r\n\r\n"),
+
+      // After calling trans.RestartWithAuth(), we should send a Type 3 message
+      // (the credentials for the origin server).  The second request continues
+      // on the same connection.
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: server\r\n"
+                "Connection: Upgrade\r\n"
+                "Authorization: NTLM "),
+      MockWrite(authenticate_msg.c_str()),
+      MockWrite("\r\n"),
+      MockWrite("Origin: http://server\r\n"
+                "Sec-WebSocket-Version: 13\r\n"
+                "Upgrade: websocket\r\n"
+                "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
+                "Sec-WebSocket-Extensions: permessage-deflate; "
+                "client_max_window_bits\r\n\r\n"),
+  };
+
+  MockRead reads1[] = {
+      // The origin server responds with a Type 2 message.
+      MockRead("HTTP/1.1 401 Access Denied\r\n"),
+      MockRead("WWW-Authenticate: NTLM "),
+      MockRead(challenge_msg.c_str()),
+      MockRead("\r\n"),
+      MockRead("Content-Length: 42\r\n"),
+      MockRead("Content-Type: text/html\r\n\r\n"),
+      MockRead("You are not authorized to view this page\r\n"),
+
+      // Lastly we get the desired content.
+      MockRead("HTTP/1.1 101 Switching Protocols\r\n"
+               "Upgrade: websocket\r\n"
+               "Connection: Upgrade\r\n"
+               "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n"),
+  };
+  SequencedSocketData data0(reads0, writes0);
+  session_deps_.socket_factory->AddSocketDataProvider(&data0);
+  SSLSocketDataProvider ssl0(ASYNC, OK);
+  ssl0.next_proto = kProtoHTTP2;
+  ssl0.next_protos_expected_in_ssl_config =
+      NextProtoVector{kProtoHTTP2, kProtoHTTP11};
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl0);
+
+  StaticSocketDataProvider data1(reads1, writes1);
+  session_deps_.socket_factory->AddSocketDataProvider(&data1);
+  SSLSocketDataProvider ssl1(ASYNC, OK);
+  // When creating the second connection, only HTTP/1.1 should be allowed.
+  ssl1.next_protos_expected_in_ssl_config = NextProtoVector{};
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl1);
+
+  session_deps_.enable_websocket_over_http2 = true;
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  HttpRequestInfo initial_request_info;
+  initial_request_info.method = "GET";
+  initial_request_info.url = kInitialUrl;
+  initial_request_info.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  HttpNetworkTransaction initial_trans(DEFAULT_PRIORITY, session.get());
+  TestCompletionCallback initial_callback;
+  int rv = initial_trans.Start(&initial_request_info,
+                               initial_callback.callback(), NetLogWithSource());
+  EXPECT_THAT(initial_callback.GetResult(rv), IsOk());
+
+  EXPECT_FALSE(session->http_server_properties()->RequiresHTTP11(
+      url::SchemeHostPort(kInitialUrl), NetworkIsolationKey()));
+
+  HttpRequestInfo websocket_request_info;
+  websocket_request_info.method = "GET";
+  websocket_request_info.url = kWebSocketUrl;
+  websocket_request_info.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_TRUE(HostPortPair::FromURL(initial_request_info.url)
+                  .Equals(HostPortPair::FromURL(websocket_request_info.url)));
+  websocket_request_info.extra_headers.SetHeader("Origin", "http://server");
+  websocket_request_info.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
+  // The following two headers must be removed by WebSocketHttp2HandshakeStream.
+  websocket_request_info.extra_headers.SetHeader("Connection", "Upgrade");
+  websocket_request_info.extra_headers.SetHeader("Upgrade", "websocket");
+
+  TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
+
+  HttpNetworkTransaction websocket_trans(MEDIUM, session.get());
+  websocket_trans.SetWebSocketHandshakeStreamCreateHelper(
+      &websocket_stream_create_helper);
+
+  TestCompletionCallback websocket_callback;
+  rv = websocket_trans.Start(&websocket_request_info,
+                             websocket_callback.callback(), NetLogWithSource());
+  EXPECT_THAT(websocket_callback.GetResult(rv), IsOk());
+
+  EXPECT_FALSE(websocket_trans.IsReadyToRestartForAuth());
+
+  const HttpResponseInfo* response = websocket_trans.GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_TRUE(CheckNTLMServerAuth(response->auth_challenge));
+
+  rv = websocket_trans.RestartWithAuth(
+      AuthCredentials(ntlm::test::kDomainUserCombined, ntlm::test::kPassword),
+      websocket_callback.callback());
+  EXPECT_THAT(websocket_callback.GetResult(rv), IsOk());
+
+  EXPECT_TRUE(websocket_trans.IsReadyToRestartForAuth());
+
+  response = websocket_trans.GetResponseInfo();
+  ASSERT_TRUE(response);
+  EXPECT_FALSE(response->auth_challenge.has_value());
+
+  rv = websocket_trans.RestartWithAuth(AuthCredentials(),
+                                       websocket_callback.callback());
+  EXPECT_THAT(websocket_callback.GetResult(rv), IsOk());
+
+  // The server should have been marked as requiring HTTP/1.1. The important
+  // part here is that the scheme that requires HTTP/1.1 should be HTTPS, not
+  // WSS.
+  EXPECT_TRUE(session->http_server_properties()->RequiresHTTP11(
+      url::SchemeHostPort(kInitialUrl), NetworkIsolationKey()));
+}
+
+#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
+
 // Test that, if we have an NTLM proxy and the origin resets the connection, we
 // do no retry forever checking for TLS version interference. This is a
 // regression test for https://crbug.com/823387. The version interference probe
diff --git a/net/http/http_server_properties.cc b/net/http/http_server_properties.cc
index 70aaa214..e83e286 100644
--- a/net/http/http_server_properties.cc
+++ b/net/http/http_server_properties.cc
@@ -62,6 +62,7 @@
     : server(server),
       network_isolation_key(use_network_isolation_key ? network_isolation_key
                                                       : NetworkIsolationKey()) {
+  // TODO(mmenke):  DCHECK that |server|'s scheme is not ws/wss.
 }
 
 HttpServerProperties::ServerInfoMapKey::~ServerInfoMapKey() = default;
@@ -135,6 +136,20 @@
   }
 }
 
+url::SchemeHostPort HttpServerProperties::GetNormalizedSchemeHostPort(
+    const GURL& url) {
+  if (url.is_valid()) {
+    if (url.SchemeIs(url::kHttpsScheme) || url.SchemeIs(url::kWssScheme)) {
+      return url::SchemeHostPort(url::kHttpsScheme, url.host_piece(),
+                                 url.EffectiveIntPort());
+    } else if (url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kWsScheme)) {
+      return url::SchemeHostPort(url::kHttpScheme, url.host_piece(),
+                                 url.EffectiveIntPort());
+    }
+  }
+  return url::SchemeHostPort(url);
+}
+
 void HttpServerProperties::Clear(base::OnceClosure callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   server_info_map_.Clear();
@@ -188,10 +203,10 @@
   if (server.host().empty())
     return false;
 
-  auto spdy_info =
+  auto server_info =
       server_info_map_.Get(CreateServerInfoKey(server, network_isolation_key));
-  return spdy_info != server_info_map_.end() &&
-         spdy_info->second.supports_spdy.value_or(false);
+  return server_info != server_info_map_.end() &&
+         server_info->second.supports_spdy.value_or(false);
 }
 
 void HttpServerProperties::SetSupportsSpdy(
@@ -214,27 +229,43 @@
     MaybeQueueWriteProperties();
 }
 
-bool HttpServerProperties::RequiresHTTP11(const HostPortPair& host_port_pair) {
+bool HttpServerProperties::RequiresHTTP11(
+    const url::SchemeHostPort& server,
+    const net::NetworkIsolationKey& network_isolation_key) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (host_port_pair.host().empty())
+  DCHECK(server.scheme() != "ws");
+  DCHECK(server.scheme() != "wss");
+  if (server.host().empty())
     return false;
 
-  return (http11_servers_.find(host_port_pair) != http11_servers_.end());
+  auto spdy_info =
+      server_info_map_.Get(CreateServerInfoKey(server, network_isolation_key));
+  return spdy_info != server_info_map_.end() &&
+         spdy_info->second.requires_http11.value_or(false);
 }
 
 void HttpServerProperties::SetHTTP11Required(
-    const HostPortPair& host_port_pair) {
+    const url::SchemeHostPort& server,
+    const net::NetworkIsolationKey& network_isolation_key) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (host_port_pair.host().empty())
+  DCHECK(server.scheme() != "ws");
+  DCHECK(server.scheme() != "wss");
+  if (server.host().empty())
     return;
 
-  http11_servers_.insert(host_port_pair);
-  MaybeQueueWriteProperties();
+  server_info_map_.GetOrPut(CreateServerInfoKey(server, network_isolation_key))
+      ->second.requires_http11 = true;
+  // No need to call MaybeQueueWriteProperties(), as this information is not
+  // persisted to preferences.
 }
 
-void HttpServerProperties::MaybeForceHTTP11(const HostPortPair& server,
-                                            SSLConfig* ssl_config) {
-  if (RequiresHTTP11(server)) {
+void HttpServerProperties::MaybeForceHTTP11(
+    const url::SchemeHostPort& server,
+    const net::NetworkIsolationKey& network_isolation_key,
+    SSLConfig* ssl_config) {
+  DCHECK(server.scheme() != "ws");
+  DCHECK(server.scheme() != "wss");
+  if (RequiresHTTP11(server, network_isolation_key)) {
     ssl_config->alpn_protos.clear();
     ssl_config->alpn_protos.push_back(kProtoHTTP11);
   }
@@ -919,11 +950,15 @@
     // entries.
     if (!old_entry->second.supports_spdy.has_value())
       old_entry->second.supports_spdy = it->second.supports_spdy;
-    if (!old_entry->second.alternative_services.has_value()) {
+    if (!old_entry->second.alternative_services.has_value())
       old_entry->second.alternative_services = it->second.alternative_services;
-    }
     if (!old_entry->second.server_network_stats.has_value())
       old_entry->second.server_network_stats = it->second.server_network_stats;
+
+    // |requires_http11| isn't saved to prefs, so the loaded entry should not
+    // have it set. Unconditionally copy it from the new entry.
+    DCHECK(!old_entry->second.requires_http11.has_value());
+    old_entry->second.requires_http11 = it->second.requires_http11;
   }
 
   // Attempt to find canonical servers. Canonical suffix only apply to HTTPS.
diff --git a/net/http/http_server_properties.h b/net/http/http_server_properties.h
index 5d499b75..4346ada 100644
--- a/net/http/http_server_properties.h
+++ b/net/http/http_server_properties.h
@@ -133,7 +133,9 @@
     virtual void WaitForPrefLoad(base::OnceClosure pref_loaded_callback) = 0;
   };
 
-  // Contains metadata about a particular server.
+  // Contains metadata about a particular server. Note that all methods that
+  // take a "SchemeHostPort" expect schemes of ws and wss to be mapped to http
+  // and https, respectively. See GetNormalizedSchemeHostPort().
   struct NET_EXPORT ServerInfo {
     ServerInfo();
     ServerInfo(const ServerInfo& server_info);
@@ -158,11 +160,12 @@
     // priority over a not set value.
     base::Optional<bool> supports_spdy;
 
+    // True if the server has previously indicated it required HTTP/1.1. Unlike
+    // other fields, not persisted to disk.
+    base::Optional<bool> requires_http11;
+
     base::Optional<AlternativeServiceInfoVector> alternative_services;
     base::Optional<ServerNetworkStats> server_network_stats;
-
-    // TODO(mmenke):  Add other per-server data as well
-    // (Http11ServerHostPortSet, QUIC server info).
   };
 
   struct NET_EXPORT ServerInfoMapKey {
@@ -214,6 +217,10 @@
 
   ~HttpServerProperties() override;
 
+  // Returns the SchemeHostPort for |url|, with the exception that it replaces
+  // "wss" with "https" and "ws" with "http", so they're grouped together.
+  static url::SchemeHostPort GetNormalizedSchemeHostPort(const GURL& url);
+
   // Deletes all data. If |callback| is non-null, flushes data to disk
   // and invokes the callback asynchronously once changes have been written to
   // disk.
@@ -241,14 +248,20 @@
                        const net::NetworkIsolationKey& network_isolation_key,
                        bool supports_spdy);
 
-  // Returns true if |server| has required HTTP/1.1 via HTTP/2 error code.
-  bool RequiresHTTP11(const HostPortPair& server);
+  // Returns true if |server| has required HTTP/1.1 via HTTP/2 error code, in
+  // the context of |network_isolation_key|.
+  bool RequiresHTTP11(const url::SchemeHostPort& server,
+                      const net::NetworkIsolationKey& network_isolation_key);
 
-  // Require HTTP/1.1 on subsequent connections.  Not persisted.
-  void SetHTTP11Required(const HostPortPair& server);
+  // Require HTTP/1.1 on subsequent connections, in the context of
+  // |network_isolation_key|.  Not persisted.
+  void SetHTTP11Required(const url::SchemeHostPort& server,
+                         const net::NetworkIsolationKey& network_isolation_key);
 
   // Modify SSLConfig to force HTTP/1.1 if necessary.
-  void MaybeForceHTTP11(const HostPortPair& server, SSLConfig* ssl_config);
+  void MaybeForceHTTP11(const url::SchemeHostPort& server,
+                        const net::NetworkIsolationKey& network_isolation_key,
+                        SSLConfig* ssl_config);
 
   // Return all alternative services for |origin|, learned in the context of
   // |network_isolation_key|, including broken ones. Returned alternative
@@ -416,7 +429,6 @@
   typedef base::flat_map<HostPortPair, quic::QuicServerId>
       CanonicalServerInfoMap;
   typedef std::vector<std::string> CanonicalSuffixList;
-  typedef std::set<HostPortPair> Http11ServerHostPortSet;
 
   // Helper function to use the passed in parameters and
   // |use_network_isolation_key_| to create a ServerInfoMapKey.
@@ -511,8 +523,6 @@
 
   ServerInfoMap server_info_map_;
 
-  Http11ServerHostPortSet http11_servers_;
-
   BrokenAlternativeServices broken_alternative_services_;
 
   IPAddress last_quic_address_;
diff --git a/net/http/http_server_properties_manager_unittest.cc b/net/http/http_server_properties_manager_unittest.cc
index 10d0611..26dc69c 100644
--- a/net/http/http_server_properties_manager_unittest.cc
+++ b/net/http/http_server_properties_manager_unittest.cc
@@ -1971,6 +1971,87 @@
   EXPECT_EQ(4, pref_delegate_->GetAndClearNumPrefUpdates());
 }
 
+// Check the interaction of ForceHTTP11 with saving/restoring settings.
+// In particular, ForceHTTP11 is not saved, and it should not overwrite or be
+// overitten by loaded data.
+TEST_F(HttpServerPropertiesManagerTest, ForceHTTP11) {
+  const url::SchemeHostPort kServer1("https", "foo.test", 443);
+  const url::SchemeHostPort kServer2("https", "bar.test", 443);
+  const url::SchemeHostPort kServer3("https", "baz.test", 443);
+
+  // Create and initialize an HttpServerProperties with no state.
+  std::unique_ptr<MockPrefDelegate> pref_delegate =
+      std::make_unique<MockPrefDelegate>();
+  MockPrefDelegate* unowned_pref_delegate = pref_delegate.get();
+  std::unique_ptr<HttpServerProperties> properties =
+      std::make_unique<HttpServerProperties>(std::move(pref_delegate),
+                                             /*net_log=*/nullptr,
+                                             GetMockTickClock());
+  unowned_pref_delegate->InitializePrefs(base::DictionaryValue());
+
+  // Set kServer1 to support H2, but require HTTP/1.1.  Set kServer2 to only
+  // require HTTP/1.1.
+  EXPECT_FALSE(properties->GetSupportsSpdy(kServer1, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->RequiresHTTP11(kServer1, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->GetSupportsSpdy(kServer2, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->RequiresHTTP11(kServer2, NetworkIsolationKey()));
+  properties->SetSupportsSpdy(kServer1, NetworkIsolationKey(), true);
+  properties->SetHTTP11Required(kServer1, NetworkIsolationKey());
+  properties->SetHTTP11Required(kServer2, NetworkIsolationKey());
+  EXPECT_TRUE(properties->GetSupportsSpdy(kServer1, NetworkIsolationKey()));
+  EXPECT_TRUE(properties->RequiresHTTP11(kServer1, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->GetSupportsSpdy(kServer2, NetworkIsolationKey()));
+  EXPECT_TRUE(properties->RequiresHTTP11(kServer2, NetworkIsolationKey()));
+
+  // Wait until the data's been written to prefs, and then tear down the
+  // HttpServerProperties.
+  FastForwardBy(HttpServerProperties::GetUpdatePrefsDelayForTesting());
+  std::unique_ptr<base::DictionaryValue> saved_value =
+      unowned_pref_delegate->GetServerProperties()->CreateDeepCopy();
+  properties.reset();
+
+  // Only information on kServer1 should have been saved to prefs.
+  std::string preferences_json;
+  base::JSONWriter::Write(*saved_value, &preferences_json);
+  EXPECT_EQ(
+      "{\"servers\":["
+      "{\"isolation\":[],"
+      "\"server\":\"https://foo.test\","
+      "\"supports_spdy\":true}],"
+      "\"version\":5}",
+      preferences_json);
+
+  // Create a new HttpServerProperties using the value saved to prefs above.
+  pref_delegate = std::make_unique<MockPrefDelegate>();
+  unowned_pref_delegate = pref_delegate.get();
+  properties = std::make_unique<HttpServerProperties>(
+      std::move(pref_delegate), /*net_log=*/nullptr, GetMockTickClock());
+
+  // Before the data has loaded, set kServer1 and kServer3 as requiring
+  // HTTP/1.1.
+  EXPECT_FALSE(properties->GetSupportsSpdy(kServer1, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->RequiresHTTP11(kServer1, NetworkIsolationKey()));
+  properties->SetHTTP11Required(kServer1, NetworkIsolationKey());
+  properties->SetHTTP11Required(kServer3, NetworkIsolationKey());
+  EXPECT_FALSE(properties->GetSupportsSpdy(kServer1, NetworkIsolationKey()));
+  EXPECT_TRUE(properties->RequiresHTTP11(kServer1, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->GetSupportsSpdy(kServer2, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->RequiresHTTP11(kServer2, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->GetSupportsSpdy(kServer3, NetworkIsolationKey()));
+  EXPECT_TRUE(properties->RequiresHTTP11(kServer3, NetworkIsolationKey()));
+
+  // The data loads.
+  unowned_pref_delegate->InitializePrefs(*saved_value);
+
+  // The properties should contain a combination of the old and new data.
+  EXPECT_TRUE(properties->GetSupportsSpdy(kServer1, NetworkIsolationKey()));
+  EXPECT_TRUE(properties->RequiresHTTP11(kServer1, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->GetSupportsSpdy(kServer2, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->RequiresHTTP11(kServer2, NetworkIsolationKey()));
+  EXPECT_FALSE(properties->GetSupportsSpdy(kServer3, NetworkIsolationKey()));
+  EXPECT_TRUE(properties->RequiresHTTP11(kServer3, NetworkIsolationKey()));
+}
+
 TEST_F(HttpServerPropertiesManagerTest, NetworkIsolationKeyServerInfo) {
   const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
   const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
diff --git a/net/http/http_server_properties_unittest.cc b/net/http/http_server_properties_unittest.cc
index 37d7aa6..ff6ba8e 100644
--- a/net/http/http_server_properties_unittest.cc
+++ b/net/http/http_server_properties_unittest.cc
@@ -121,6 +121,53 @@
   HttpServerProperties impl_;
 };
 
+TEST_F(HttpServerPropertiesTest, GetNormalizedSchemeHostPort) {
+  url::SchemeHostPort server =
+      HttpServerProperties::GetNormalizedSchemeHostPort(
+          GURL("https://foo.test/"));
+  EXPECT_EQ("https", server.scheme());
+  EXPECT_EQ("foo.test", server.host());
+  EXPECT_EQ(443, server.port());
+
+  server = HttpServerProperties::GetNormalizedSchemeHostPort(
+      GURL("wss://foo.test/"));
+  EXPECT_EQ("https", server.scheme());
+  EXPECT_EQ("foo.test", server.host());
+  EXPECT_EQ(443, server.port());
+
+  server = HttpServerProperties::GetNormalizedSchemeHostPort(
+      GURL("wss://bar.test:7/"));
+  EXPECT_EQ("https", server.scheme());
+  EXPECT_EQ("bar.test", server.host());
+  EXPECT_EQ(7, server.port());
+
+  server = HttpServerProperties::GetNormalizedSchemeHostPort(
+      GURL("http://foo.test/"));
+  EXPECT_EQ("http", server.scheme());
+  EXPECT_EQ("foo.test", server.host());
+  EXPECT_EQ(80, server.port());
+
+  server =
+      HttpServerProperties::GetNormalizedSchemeHostPort(GURL("ws://foo.test/"));
+  EXPECT_EQ("http", server.scheme());
+  EXPECT_EQ("foo.test", server.host());
+  EXPECT_EQ(80, server.port());
+
+  server = HttpServerProperties::GetNormalizedSchemeHostPort(
+      GURL("ws://bar.test:7/"));
+  EXPECT_EQ("http", server.scheme());
+  EXPECT_EQ("bar.test", server.host());
+  EXPECT_EQ(7, server.port());
+
+  // Neither of these should happen, except possibly when loading a bad
+  // properties file, but neither should crash.
+  server = HttpServerProperties::GetNormalizedSchemeHostPort(GURL());
+  EXPECT_EQ("", server.host());
+  server = HttpServerProperties::GetNormalizedSchemeHostPort(
+      GURL("file:///foo/bar"));
+  EXPECT_EQ(server, url::SchemeHostPort(GURL("file:///foo/bar")));
+}
+
 TEST_F(HttpServerPropertiesTest, SetSupportsSpdy) {
   // Check spdy servers are correctly set with SchemeHostPort key.
   url::SchemeHostPort https_www_server("https", "www.google.com", 443);
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc
index eea10221..0fd1963 100644
--- a/net/http/http_stream_factory_job.cc
+++ b/net/http/http_stream_factory_job.cc
@@ -391,7 +391,9 @@
   DCHECK(!using_quic_);
 
   if (proxy_info_.is_direct() &&
-      session_->http_server_properties()->RequiresHTTP11(destination_)) {
+      session_->http_server_properties()->RequiresHTTP11(
+          HttpServerProperties::GetNormalizedSchemeHostPort(request_info_.url),
+          request_info_.network_isolation_key)) {
     return false;
   }
 
@@ -841,10 +843,16 @@
   HttpServerProperties* http_server_properties =
       session_->http_server_properties();
   if (http_server_properties) {
-    http_server_properties->MaybeForceHTTP11(destination_, &server_ssl_config_);
+    http_server_properties->MaybeForceHTTP11(
+        HttpServerProperties::GetNormalizedSchemeHostPort(request_info_.url),
+        request_info_.network_isolation_key, &server_ssl_config_);
     if (proxy_info_.is_https()) {
       http_server_properties->MaybeForceHTTP11(
-          proxy_info_.proxy_server().host_port_pair(), &proxy_ssl_config_);
+          url::SchemeHostPort(
+              url::kHttpsScheme,
+              proxy_info_.proxy_server().host_port_pair().host(),
+              proxy_info_.proxy_server().host_port_pair().port()),
+          request_info_.network_isolation_key, &proxy_ssl_config_);
     }
   }
 
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 4a5e21ef..c0763d2 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/test_file_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
@@ -22,7 +23,9 @@
 #include "net/base/chunked_upload_data_stream.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/elements_upload_data_stream.h"
+#include "net/base/features.h"
 #include "net/base/ip_endpoint.h"
+#include "net/base/network_isolation_key.h"
 #include "net/base/proxy_delegate.h"
 #include "net/base/proxy_server.h"
 #include "net/base/request_priority.h"
@@ -5385,13 +5388,15 @@
 
   HttpServerProperties* http_server_properties =
       helper.session()->spdy_session_pool()->http_server_properties();
-  EXPECT_FALSE(http_server_properties->RequiresHTTP11(host_port_pair_));
+  EXPECT_FALSE(http_server_properties->RequiresHTTP11(
+      url::SchemeHostPort(request_.url), NetworkIsolationKey()));
 
   helper.RunPreTestSetup();
   helper.StartDefaultTest();
   helper.FinishDefaultTestWithoutVerification();
   helper.VerifyDataConsumed();
-  EXPECT_TRUE(http_server_properties->RequiresHTTP11(host_port_pair_));
+  EXPECT_TRUE(http_server_properties->RequiresHTTP11(
+      url::SchemeHostPort(request_.url), NetworkIsolationKey()));
 
   const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
   ASSERT_TRUE(response);
@@ -5409,6 +5414,117 @@
   EXPECT_EQ("hello", response_data);
 }
 
+// Same as above test, but checks that NetworkIsolationKeys are respected.
+TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredRetryWithNetworkIsolationKey) {
+  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
+  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+
+  const NetworkIsolationKey kNetworkIsolationKeys[] = {
+      kNetworkIsolationKey1, kNetworkIsolationKey2, NetworkIsolationKey()};
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      // enabled_features
+      {features::kPartitionHttpServerPropertiesByNetworkIsolationKey,
+       // Need to partition connections by NetworkIsolationKey for
+       // SpdySessionKeys to include NetworkIsolationKeys.
+       features::kPartitionConnectionsByNetworkIsolationKey},
+      // disabled_features
+      {});
+
+  // Do not force SPDY so that sockets can negotiate HTTP/1.1.
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
+
+  // For each server, set up and tear down a QUIC session cleanly, and check
+  // that stats have been added to HttpServerProperties using the correct
+  // NetworkIsolationKey.
+  for (size_t i = 0; i < base::size(kNetworkIsolationKeys); ++i) {
+    SCOPED_TRACE(i);
+
+    request_.method = "GET";
+    request_.network_isolation_key = kNetworkIsolationKeys[i];
+
+    // First socket: HTTP/2 request rejected with HTTP_1_1_REQUIRED.
+    SpdyTestUtil spdy_util;
+    spdy::SpdyHeaderBlock headers(
+        spdy_util.ConstructGetHeaderBlock(kDefaultUrl));
+    spdy::SpdySerializedFrame req(
+        spdy_util.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
+    MockWrite writes0[] = {CreateMockWrite(req, 0)};
+    spdy::SpdySerializedFrame rst(spdy_util.ConstructSpdyRstStream(
+        1, spdy::ERROR_CODE_HTTP_1_1_REQUIRED));
+    MockRead reads0[] = {CreateMockRead(rst, 1)};
+    SequencedSocketData data0(reads0, writes0);
+
+    auto ssl_provider0 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+    // Expect HTTP/2 protocols too in SSLConfig.
+    ssl_provider0->next_protos_expected_in_ssl_config =
+        NextProtoVector{kProtoHTTP2, kProtoHTTP11};
+    // Force SPDY.
+    ssl_provider0->next_proto = kProtoHTTP2;
+    helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0));
+
+    // Second socket: falling back to HTTP/1.1.
+    MockWrite writes1[] = {MockWrite(ASYNC, 0,
+                                     "GET / HTTP/1.1\r\n"
+                                     "Host: www.example.org\r\n"
+                                     "Connection: keep-alive\r\n\r\n")};
+    MockRead reads1[] = {MockRead(ASYNC, 1,
+                                  "HTTP/1.1 200 OK\r\n"
+                                  "Content-Length: 5\r\n\r\n"
+                                  "hello")};
+    SequencedSocketData data1(reads1, writes1);
+
+    auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+    // Expect only HTTP/1.1 protocol in SSLConfig.
+    ssl_provider1->next_protos_expected_in_ssl_config =
+        NextProtoVector{kProtoHTTP11};
+    // Force HTTP/1.1.
+    ssl_provider1->next_proto = kProtoHTTP11;
+    helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
+
+    HttpServerProperties* http_server_properties =
+        helper.session()->spdy_session_pool()->http_server_properties();
+    EXPECT_FALSE(http_server_properties->RequiresHTTP11(
+        url::SchemeHostPort(request_.url), kNetworkIsolationKeys[i]));
+
+    HttpNetworkTransaction trans(DEFAULT_PRIORITY, helper.session());
+
+    TestCompletionCallback callback;
+    int rv = trans.Start(&request_, callback.callback(), log_);
+    EXPECT_THAT(callback.GetResult(rv), IsOk());
+
+    const HttpResponseInfo* response = trans.GetResponseInfo();
+    ASSERT_TRUE(response);
+    ASSERT_TRUE(response->headers);
+    EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+    EXPECT_FALSE(response->was_fetched_via_spdy);
+    EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1,
+              response->connection_info);
+    EXPECT_TRUE(response->was_alpn_negotiated);
+    EXPECT_TRUE(request_.url.SchemeIs("https"));
+    EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
+    EXPECT_EQ(443, response->remote_endpoint.port());
+    std::string response_data;
+    ASSERT_THAT(ReadTransaction(&trans, &response_data), IsOk());
+    EXPECT_EQ("hello", response_data);
+
+    for (size_t j = 0; j < base::size(kNetworkIsolationKeys); ++j) {
+      // NetworkIsolationKeys up to kNetworkIsolationKeys[j] are known to
+      // require HTTP/1.1, others are not.
+      if (j <= i) {
+        EXPECT_TRUE(http_server_properties->RequiresHTTP11(
+            url::SchemeHostPort(request_.url), kNetworkIsolationKeys[j]));
+      } else {
+        EXPECT_FALSE(http_server_properties->RequiresHTTP11(
+            url::SchemeHostPort(request_.url), kNetworkIsolationKeys[j]));
+      }
+    }
+  }
+}
+
 // Retry with HTTP/1.1 to the proxy when receiving HTTP_1_1_REQUIRED from the
 // proxy.  Note that no actual protocol negotiation happens, instead this test
 // forces protocols for both sockets.
@@ -5475,14 +5591,16 @@
 
   HttpServerProperties* http_server_properties =
       helper.session()->spdy_session_pool()->http_server_properties();
-  const HostPortPair proxy_host_port_pair = HostPortPair("myproxy", 70);
-  EXPECT_FALSE(http_server_properties->RequiresHTTP11(proxy_host_port_pair));
+  url::SchemeHostPort proxy_scheme_host_port(url::kHttpsScheme, "myproxy", 70);
+  EXPECT_FALSE(http_server_properties->RequiresHTTP11(proxy_scheme_host_port,
+                                                      NetworkIsolationKey()));
 
   helper.RunPreTestSetup();
   helper.StartDefaultTest();
   helper.FinishDefaultTestWithoutVerification();
   helper.VerifyDataConsumed();
-  EXPECT_TRUE(http_server_properties->RequiresHTTP11(proxy_host_port_pair));
+  EXPECT_TRUE(http_server_properties->RequiresHTTP11(proxy_scheme_host_port,
+                                                     NetworkIsolationKey()));
 
   const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
   ASSERT_TRUE(response);
@@ -5500,6 +5618,140 @@
   EXPECT_EQ("hello", response_data);
 }
 
+// Same as above, but also test that NetworkIsolationKeys are respected.
+TEST_F(SpdyNetworkTransactionTest,
+       HTTP11RequiredProxyRetryWithNetworkIsolationKey) {
+  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
+  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+
+  const NetworkIsolationKey kNetworkIsolationKeys[] = {
+      kNetworkIsolationKey1, kNetworkIsolationKey2, NetworkIsolationKey()};
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      // enabled_features
+      {features::kPartitionHttpServerPropertiesByNetworkIsolationKey,
+       // Need to partition connections by NetworkIsolationKey for
+       // SpdySessionKeys to include NetworkIsolationKeys.
+       features::kPartitionConnectionsByNetworkIsolationKey},
+      // disabled_features
+      {});
+
+  request_.method = "GET";
+  auto session_deps = std::make_unique<SpdySessionDependencies>(
+      ProxyResolutionService::CreateFixedFromPacResult(
+          "HTTPS myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
+  // Do not force SPDY so that second socket can negotiate HTTP/1.1.
+  NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
+                                     std::move(session_deps));
+  helper.RunPreTestSetup();
+
+  for (size_t i = 0; i < base::size(kNetworkIsolationKeys); ++i) {
+    // First socket: HTTP/2 CONNECT rejected with HTTP_1_1_REQUIRED.
+
+    SpdyTestUtil spdy_util;
+    spdy::SpdySerializedFrame req(spdy_util.ConstructSpdyConnect(
+        nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
+        HostPortPair("www.example.org", 443)));
+    MockWrite writes0[] = {CreateMockWrite(req, 0)};
+    spdy::SpdySerializedFrame rst(spdy_util.ConstructSpdyRstStream(
+        1, spdy::ERROR_CODE_HTTP_1_1_REQUIRED));
+    MockRead reads0[] = {CreateMockRead(rst, 1)};
+    SequencedSocketData data0(reads0, writes0);
+
+    auto ssl_provider0 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+    // Expect HTTP/2 protocols too in SSLConfig.
+    ssl_provider0->next_protos_expected_in_ssl_config =
+        NextProtoVector{kProtoHTTP2, kProtoHTTP11};
+    // Force SPDY.
+    ssl_provider0->next_proto = kProtoHTTP2;
+    helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0));
+
+    // Second socket: retry using HTTP/1.1.
+    MockWrite writes1[] = {
+        MockWrite(ASYNC, 0,
+                  "CONNECT www.example.org:443 HTTP/1.1\r\n"
+                  "Host: www.example.org:443\r\n"
+                  "Proxy-Connection: keep-alive\r\n\r\n"),
+        MockWrite(ASYNC, 2,
+                  "GET / HTTP/1.1\r\n"
+                  "Host: www.example.org\r\n"
+                  "Connection: keep-alive\r\n\r\n"),
+    };
+
+    MockRead reads1[] = {
+        MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n\r\n"),
+        MockRead(ASYNC, 3,
+                 "HTTP/1.1 200 OK\r\n"
+                 "Content-Length: 5\r\n\r\n"
+                 "hello"),
+    };
+    SequencedSocketData data1(reads1, writes1);
+
+    auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+    // Expect only HTTP/1.1 protocol in SSLConfig.
+    ssl_provider1->next_protos_expected_in_ssl_config =
+        NextProtoVector{kProtoHTTP11};
+    // Force HTTP/1.1.
+    ssl_provider1->next_proto = kProtoHTTP11;
+    helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
+
+    // A third socket is needed for the tunnelled connection.
+    auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+    helper.session_deps()->socket_factory->AddSSLSocketDataProvider(
+        ssl_provider2.get());
+
+    HttpServerProperties* http_server_properties =
+        helper.session()->spdy_session_pool()->http_server_properties();
+    url::SchemeHostPort proxy_scheme_host_port(url::kHttpsScheme, "myproxy",
+                                               70);
+    EXPECT_FALSE(http_server_properties->RequiresHTTP11(
+        proxy_scheme_host_port, kNetworkIsolationKeys[i]));
+
+    request_.network_isolation_key = kNetworkIsolationKeys[i];
+    HttpNetworkTransaction trans(DEFAULT_PRIORITY, helper.session());
+    TestCompletionCallback callback;
+    int rv = trans.Start(&request_, callback.callback(), log_);
+    EXPECT_THAT(callback.GetResult(rv), IsOk());
+    helper.VerifyDataConsumed();
+
+    const HttpResponseInfo* response = trans.GetResponseInfo();
+    ASSERT_TRUE(response);
+    ASSERT_TRUE(response->headers);
+    EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+    EXPECT_FALSE(response->was_fetched_via_spdy);
+    EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1,
+              response->connection_info);
+    EXPECT_FALSE(response->was_alpn_negotiated);
+    EXPECT_TRUE(request_.url.SchemeIs("https"));
+    EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
+    EXPECT_EQ(70, response->remote_endpoint.port());
+    std::string response_data;
+    ASSERT_THAT(ReadTransaction(&trans, &response_data), IsOk());
+    EXPECT_EQ("hello", response_data);
+
+    for (size_t j = 0; j < base::size(kNetworkIsolationKeys); ++j) {
+      // The proxy SchemeHostPort URL should not be marked as requiring HTTP/1.1
+      // using the current NetworkIsolationKey, and the state of others should
+      // be unchanged since the last loop iteration..
+      if (j <= i) {
+        EXPECT_TRUE(http_server_properties->RequiresHTTP11(
+            proxy_scheme_host_port, kNetworkIsolationKeys[j]));
+      } else {
+        EXPECT_FALSE(http_server_properties->RequiresHTTP11(
+            proxy_scheme_host_port, kNetworkIsolationKeys[j]));
+      }
+    }
+
+    // The destination SchemeHostPort should not be marked as requiring
+    // HTTP/1.1.
+    EXPECT_FALSE(http_server_properties->RequiresHTTP11(
+        url::SchemeHostPort(request_.url), kNetworkIsolationKeys[i]));
+  }
+}
+
 // Test to make sure we can correctly connect through a proxy.
 TEST_F(SpdyNetworkTransactionTest, ProxyConnect) {
   auto session_deps = std::make_unique<SpdySessionDependencies>(
@@ -8828,6 +9080,130 @@
   helper.VerifyDataConsumed();
 }
 
+TEST_F(SpdyNetworkTransactionTest, WebSocketHttp11Required) {
+  base::HistogramTester histogram_tester;
+  auto session_deps = std::make_unique<SpdySessionDependencies>();
+  session_deps->enable_websocket_over_http2 = true;
+  NormalSpdyTransactionHelper helper(request_, HIGHEST, log_,
+                                     std::move(session_deps));
+  helper.RunPreTestSetup();
+
+  spdy::SpdySerializedFrame req(
+      spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST));
+  spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
+
+  spdy::SpdyHeaderBlock websocket_request_headers;
+  websocket_request_headers[spdy::kHttp2MethodHeader] = "CONNECT";
+  websocket_request_headers[spdy::kHttp2AuthorityHeader] = "www.example.org";
+  websocket_request_headers[spdy::kHttp2SchemeHeader] = "https";
+  websocket_request_headers[spdy::kHttp2PathHeader] = "/";
+  websocket_request_headers[spdy::kHttp2ProtocolHeader] = "websocket";
+  websocket_request_headers["origin"] = "http://www.example.org";
+  websocket_request_headers["sec-websocket-version"] = "13";
+  websocket_request_headers["sec-websocket-extensions"] =
+      "permessage-deflate; client_max_window_bits";
+  spdy::SpdySerializedFrame websocket_request(spdy_util_.ConstructSpdyHeaders(
+      3, std::move(websocket_request_headers), MEDIUM, false));
+
+  spdy::SpdySerializedFrame priority1(
+      spdy_util_.ConstructSpdyPriority(3, 0, MEDIUM, true));
+  spdy::SpdySerializedFrame priority2(
+      spdy_util_.ConstructSpdyPriority(1, 3, LOWEST, true));
+
+  MockWrite writes1[] = {CreateMockWrite(req, 0),
+                         CreateMockWrite(settings_ack, 2),
+                         CreateMockWrite(websocket_request, 4)};
+
+  spdy::SettingsMap settings;
+  settings[spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
+  spdy::SpdySerializedFrame settings_frame(
+      spdy_util_.ConstructSpdySettings(settings));
+  spdy::SpdySerializedFrame resp1(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
+  spdy::SpdySerializedFrame websocket_response_http11_required(
+      spdy_util_.ConstructSpdyRstStream(3, spdy::ERROR_CODE_HTTP_1_1_REQUIRED));
+  MockRead reads1[] = {CreateMockRead(settings_frame, 1),
+                       CreateMockRead(resp1, 3),
+                       CreateMockRead(websocket_response_http11_required, 5)};
+
+  SequencedSocketData data1(reads1, writes1);
+  helper.AddData(&data1);
+
+  MockWrite writes2[] = {
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: Upgrade\r\n"
+                "Origin: http://www.example.org\r\n"
+                "Sec-WebSocket-Version: 13\r\n"
+                "Upgrade: websocket\r\n"
+                "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
+                "Sec-WebSocket-Extensions: permessage-deflate; "
+                "client_max_window_bits\r\n\r\n")};
+  MockRead reads2[] = {
+      MockRead("HTTP/1.1 101 Switching Protocols\r\n"
+               "Upgrade: websocket\r\n"
+               "Connection: Upgrade\r\n"
+               "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")};
+  StaticSocketDataProvider data2(reads2, writes2);
+  auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
+  // Test that request has empty |alpn_protos|, that is, HTTP/2 is disabled.
+  ssl_provider2->next_protos_expected_in_ssl_config = NextProtoVector{};
+  // Force socket to use HTTP/1.1, the default protocol without ALPN.
+  ssl_provider2->next_proto = kProtoHTTP11;
+  ssl_provider2->ssl_info.cert =
+      ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
+  helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
+
+  // Create HTTP/2 connection.
+  TestCompletionCallback callback1;
+  int rv = helper.trans()->Start(&request_, callback1.callback(), log_);
+  ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  // Create HTTP/2 connection.
+  base::RunLoop().RunUntilIdle();
+
+  SpdySessionKey key(HostPortPair::FromURL(request_.url), ProxyServer::Direct(),
+                     PRIVACY_MODE_DISABLED,
+                     SpdySessionKey::IsProxySession::kFalse, SocketTag());
+  base::WeakPtr<SpdySession> spdy_session =
+      helper.session()->spdy_session_pool()->FindAvailableSession(
+          key, /* enable_ip_based_pooling = */ true,
+          /* is_websocket = */ true, log_);
+  ASSERT_TRUE(spdy_session);
+  EXPECT_TRUE(spdy_session->support_websocket());
+
+  HttpRequestInfo request2;
+  request2.method = "GET";
+  request2.url = GURL("wss://www.example.org/");
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_TRUE(HostPortPair::FromURL(request_.url)
+                  .Equals(HostPortPair::FromURL(request2.url)));
+  request2.extra_headers.SetHeader("Origin", "http://www.example.org");
+  request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
+  // The following two headers must be removed by WebSocketHttp2HandshakeStream.
+  request2.extra_headers.SetHeader("Connection", "Upgrade");
+  request2.extra_headers.SetHeader("Upgrade", "websocket");
+
+  TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
+
+  HttpNetworkTransaction trans2(MEDIUM, helper.session());
+  trans2.SetWebSocketHandshakeStreamCreateHelper(
+      &websocket_stream_create_helper);
+
+  TestCompletionCallback callback2;
+  rv = trans2.Start(&request2, callback2.callback(), log_);
+  EXPECT_THAT(callback2.GetResult(rv), IsOk());
+
+  helper.VerifyDataConsumed();
+
+  // Server advertised WebSocket support.
+  histogram_tester.ExpectUniqueSample("Net.SpdySession.ServerSupportsWebSocket",
+                                      /* support_websocket = true */ 1,
+                                      /* expected_count = */ 1);
+}
+
 // Plaintext WebSocket over HTTP/2 is not implemented, see
 // https://crbug.com/684681.
 TEST_F(SpdyNetworkTransactionTest, PlaintextWebSocketOverHttp2Proxy) {
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 23fb742..b3eab6de 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -57,6 +57,7 @@
 #include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h"
 #include "net/third_party/quiche/src/spdy/core/spdy_frame_builder.h"
 #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
+#include "url/scheme_host_port.h"
 #include "url/url_constants.h"
 
 namespace net {
@@ -2869,7 +2870,10 @@
 
   // Mark host_port_pair requiring HTTP/1.1 for subsequent connections.
   if (err == ERR_HTTP_1_1_REQUIRED) {
-    http_server_properties_->SetHTTP11Required(host_port_pair());
+    http_server_properties_->SetHTTP11Required(
+        url::SchemeHostPort(url::kHttpsScheme, host_port_pair().host(),
+                            host_port_pair().port()),
+        spdy_session_key_.network_isolation_key());
   }
 
   // If |err| indicates an error occurred, inform the peer that we're closing
diff --git a/net/websockets/websocket_channel.cc b/net/websockets/websocket_channel.cc
index 2139f55..d415dfd 100644
--- a/net/websockets/websocket_channel.cc
+++ b/net/websockets/websocket_channel.cc
@@ -631,8 +631,7 @@
 }
 
 ChannelState WebSocketChannel::ReadFrames() {
-  // TODO(crbug.com/994000) Remove this CHECK.
-  CHECK(stream_);
+  DCHECK(stream_);
   DCHECK(state_ == CONNECTED || state_ == SEND_CLOSED || state_ == CLOSE_WAIT);
   DCHECK(read_frames_.empty());
   if (is_reading_) {
@@ -648,8 +647,7 @@
   }
 
   while (!event_interface_->HasPendingDataFrames()) {
-    // TODO(crbug.com/994000) Remove this CHECK.
-    CHECK(stream_);
+    DCHECK(stream_);
     // This use of base::Unretained is safe because this object owns the
     // WebSocketStream, and any pending reads will be cancelled when it is
     // destroyed.
@@ -665,8 +663,6 @@
       return CHANNEL_DELETED;
     }
     DCHECK_NE(CLOSED, state_);
-    // TODO(crbug.com/994000) Remove this CHECK.
-    CHECK_EQ(result, net::OK);
   }
   return CHANNEL_ALIVE;
 }
diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc
index f05926e1..5329892 100644
--- a/pdf/pdfium/pdfium_page.cc
+++ b/pdf/pdfium/pdfium_page.cc
@@ -24,6 +24,7 @@
 #include "printing/units.h"
 #include "third_party/pdfium/public/cpp/fpdf_scopers.h"
 #include "third_party/pdfium/public/fpdf_annot.h"
+#include "third_party/pdfium/public/fpdf_catalog.h"
 
 using printing::ConvertUnitDouble;
 using printing::kPointsPerInch;
@@ -251,6 +252,28 @@
   return text_page();
 }
 
+void PDFiumPage::CalculatePageObjectTextRunBreaks() {
+  if (calculated_page_object_text_run_breaks_)
+    return;
+
+  calculated_page_object_text_run_breaks_ = true;
+  int chars_count = FPDFText_CountChars(GetTextPage());
+  if (chars_count == 0)
+    return;
+
+  CalculateLinks();
+  for (const auto& link : links_) {
+    if (link.start_char_index >= 0 && link.start_char_index < chars_count) {
+      page_object_text_run_breaks_.insert(link.start_char_index);
+      int next_text_run_break_index = link.start_char_index + link.char_count;
+      // Don't insert a break if the link is at the end of the page text.
+      if (next_text_run_break_index < chars_count) {
+        page_object_text_run_breaks_.insert(next_text_run_break_index);
+      }
+    }
+  }
+}
+
 base::Optional<PP_PrivateAccessibilityTextRunInfo> PDFiumPage::GetTextRunInfo(
     int start_char_index) {
   FPDF_PAGE page = GetPage();
@@ -308,9 +331,21 @@
   // ending at the current last character center-point.
   double text_run_angle = 0;
 
+  CalculatePageObjectTextRunBreaks();
+  const auto breakpoint_iter =
+      std::lower_bound(page_object_text_run_breaks_.begin(),
+                       page_object_text_run_breaks_.end(), char_index);
+  int breakpoint_index = breakpoint_iter != page_object_text_run_breaks_.end()
+                             ? *breakpoint_iter
+                             : -1;
+
   // Continue adding characters until heuristics indicate we should end the text
   // run.
   while (char_index < chars_count) {
+    // Split a text run when it encounters a page object like links or images.
+    if (char_index == breakpoint_index)
+      break;
+
     unsigned int character = FPDFText_GetUnicode(text_page, char_index);
     pp::FloatRect char_rect =
         GetFloatCharRectInPixels(page, text_page, char_index);
@@ -684,6 +719,8 @@
   calculated_images_ = true;
   FPDF_PAGE page = GetPage();
   int page_object_count = FPDFPage_CountObjects(page);
+  MarkedContentIdToImageMap marked_content_id_image_map;
+  bool is_tagged = FPDFCatalog_IsTagged(engine_->doc());
   for (int i = 0; i < page_object_count; ++i) {
     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
     if (FPDFPageObj_GetType(page_object) != FPDF_PAGEOBJ_IMAGE)
@@ -698,8 +735,80 @@
     Image image;
     image.bounding_rect = PageToScreen(pp::Point(), 1.0, left, top, right,
                                        bottom, PageOrientation::kOriginal);
+
+    if (is_tagged) {
+      // Collect all marked content IDs for image objects so that they can
+      // later be used to retrieve alt text from struct tree for the page.
+      FPDF_IMAGEOBJ_METADATA image_metadata;
+      if (FPDFImageObj_GetImageMetadata(page_object, page, &image_metadata)) {
+        int marked_content_id = image_metadata.marked_content_id;
+        if (marked_content_id >= 0) {
+          // If |marked_content_id| is already present, ignore the one being
+          // inserted.
+          marked_content_id_image_map.insert(
+              {marked_content_id, images_.size()});
+        }
+      }
+    }
     images_.push_back(image);
   }
+
+  if (!marked_content_id_image_map.empty())
+    PopulateImageAltText(marked_content_id_image_map);
+}
+
+void PDFiumPage::PopulateImageAltText(
+    const MarkedContentIdToImageMap& marked_content_id_image_map) {
+  ScopedFPDFStructTree struct_tree(FPDF_StructTree_GetForPage(GetPage()));
+  if (!struct_tree)
+    return;
+
+  std::set<FPDF_STRUCTELEMENT> visited_elements;
+  int tree_children_count = FPDF_StructTree_CountChildren(struct_tree.get());
+  for (int i = 0; i < tree_children_count; ++i) {
+    FPDF_STRUCTELEMENT current_element =
+        FPDF_StructTree_GetChildAtIndex(struct_tree.get(), i);
+    PopulateImageAltTextForStructElement(marked_content_id_image_map,
+                                         current_element, &visited_elements);
+  }
+}
+
+void PDFiumPage::PopulateImageAltTextForStructElement(
+    const MarkedContentIdToImageMap& marked_content_id_image_map,
+    FPDF_STRUCTELEMENT current_element,
+    std::set<FPDF_STRUCTELEMENT>* visited_elements) {
+  if (!current_element)
+    return;
+
+  bool inserted = visited_elements->insert(current_element).second;
+  if (!inserted)
+    return;
+
+  int marked_content_id =
+      FPDF_StructElement_GetMarkedContentID(current_element);
+  if (marked_content_id >= 0) {
+    auto it = marked_content_id_image_map.find(marked_content_id);
+    if (it != marked_content_id_image_map.end() &&
+        images_[it->second].alt_text.empty()) {
+      size_t buffer_size =
+          FPDF_StructElement_GetAltText(current_element, nullptr, 0);
+      if (buffer_size > 0) {
+        base::string16 alt_text;
+        PDFiumAPIStringBufferSizeInBytesAdapter<base::string16>
+            api_string_adapter(&alt_text, buffer_size, true);
+        api_string_adapter.Close(FPDF_StructElement_GetAltText(
+            current_element, api_string_adapter.GetData(), buffer_size));
+        images_[it->second].alt_text = base::UTF16ToUTF8(alt_text);
+      }
+    }
+  }
+  int children_count = FPDF_StructElement_CountChildren(current_element);
+  for (int i = 0; i < children_count; ++i) {
+    FPDF_STRUCTELEMENT child =
+        FPDF_StructElement_GetChildAtIndex(current_element, i);
+    PopulateImageAltTextForStructElement(marked_content_id_image_map, child,
+                                         visited_elements);
+  }
 }
 
 bool PDFiumPage::GetUnderlyingTextRangeForRect(const pp::FloatRect& rect,
diff --git a/pdf/pdfium/pdfium_page.h b/pdf/pdfium/pdfium_page.h
index c7e347af..0816187 100644
--- a/pdf/pdfium/pdfium_page.h
+++ b/pdf/pdfium/pdfium_page.h
@@ -5,6 +5,8 @@
 #ifndef PDF_PDFIUM_PDFIUM_PAGE_H_
 #define PDF_PDFIUM_PDFIUM_PAGE_H_
 
+#include <map>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -155,6 +157,27 @@
   // Returns link type and fills target associated with a URI action. Returns
   // NONSELECTABLE_AREA if detection failed.
   Area GetURITarget(FPDF_ACTION uri_action, LinkTarget* target) const;
+  // Calculates the set of character indices on which text runs need to be
+  // broken for page objects such as links and images.
+  void CalculatePageObjectTextRunBreaks();
+
+  // Key    :  Marked content id for the image element as specified in the
+  //           struct tree.
+  // Value  :  Index of image in the |images_| vector.
+  using MarkedContentIdToImageMap = std::map<int, size_t>;
+  // Traverses the entire struct tree of the page recursively and extracts the
+  // alt text from struct tree elements corresponding to the marked content IDs
+  // present in |marked_content_id_image_map|.
+  void PopulateImageAltText(
+      const MarkedContentIdToImageMap& marked_content_id_image_map);
+  // Traverses a struct element and its sub-tree recursively and extracts the
+  // alt text from struct elements corresponding to the marked content IDs
+  // present in |marked_content_id_image_map|. Uses |visited_elements| to guard
+  // against malformed struct trees.
+  void PopulateImageAltTextForStructElement(
+      const MarkedContentIdToImageMap& marked_content_id_image_map,
+      FPDF_STRUCTELEMENT current_element,
+      std::set<FPDF_STRUCTELEMENT>* visited_elements);
 
   class ScopedUnloadPreventer {
    public:
@@ -188,6 +211,8 @@
     ~Image();
 
     pp::Rect bounding_rect;
+    // Alt text is available only for tagged PDFs.
+    std::string alt_text;
   };
 
   PDFiumEngine* engine_;
@@ -200,6 +225,10 @@
   std::vector<Link> links_;
   bool calculated_images_ = false;
   std::vector<Image> images_;
+  bool calculated_page_object_text_run_breaks_ = false;
+  // The set of character indices on which text runs need to be broken for page
+  // objects.
+  std::set<int> page_object_text_run_breaks_;
   bool available_;
   PDFEngine::PageFeatures page_features_;
 
diff --git a/pdf/pdfium/pdfium_page_unittest.cc b/pdf/pdfium/pdfium_page_unittest.cc
index b45d2ee1..d2887dd 100644
--- a/pdf/pdfium/pdfium_page_unittest.cc
+++ b/pdf/pdfium/pdfium_page_unittest.cc
@@ -17,10 +17,6 @@
 
 namespace {
 
-bool IsValidLinkForTesting(const std::string& url) {
-  return !url.empty();
-}
-
 TEST(PDFiumPageTest, ToPDFiumRotation) {
   EXPECT_EQ(ToPDFiumRotation(PageOrientation::kOriginal), 0);
   EXPECT_EQ(ToPDFiumRotation(PageOrientation::kClockwise90), 1);
@@ -41,13 +37,8 @@
 
 class PDFiumPageLinkTest : public PDFiumTestBase {
  public:
-  PDFiumPageLinkTest() {
-    PDFiumPage::SetIsValidLinkFunctionForTesting(&IsValidLinkForTesting);
-  }
-
-  ~PDFiumPageLinkTest() override {
-    PDFiumPage::SetIsValidLinkFunctionForTesting(nullptr);
-  }
+  PDFiumPageLinkTest() = default;
+  ~PDFiumPageLinkTest() override = default;
 
   const std::vector<PDFiumPage::Link>& GetLinks(PDFiumEngine* engine,
                                                 int page_index) {
@@ -70,7 +61,7 @@
   bool is_chromeos = IsRunningOnChromeOS();
 
   const std::vector<PDFiumPage::Link>& links = GetLinks(engine.get(), 0);
-  ASSERT_EQ(2u, links.size());
+  ASSERT_EQ(3u, links.size());
 
   const PDFiumPage::Link& link = links[0];
   EXPECT_EQ("http://yahoo.com", link.url);
@@ -93,6 +84,13 @@
   } else {
     CompareRect({131, 121, 138, 20}, second_link.bounding_rects[0]);
   }
+
+  const PDFiumPage::Link& third_link = links[2];
+  EXPECT_EQ("http://google.com", third_link.url);
+  EXPECT_EQ(92, third_link.start_char_index);
+  EXPECT_EQ(17, third_link.char_count);
+  ASSERT_EQ(1u, third_link.bounding_rects.size());
+  CompareRect({82, 67, 161, 21}, third_link.bounding_rects[0]);
 }
 
 using PDFiumPageImageTest = PDFiumTestBase;
@@ -109,8 +107,83 @@
   page->CalculateImages();
   ASSERT_EQ(3u, page->images_.size());
   CompareRect({380, 78, 67, 68}, page->images_[0].bounding_rect);
+  EXPECT_EQ("Image 1", page->images_[0].alt_text);
   CompareRect({380, 385, 27, 28}, page->images_[1].bounding_rect);
+  EXPECT_EQ("Image 2", page->images_[1].alt_text);
   CompareRect({380, 678, 1, 1}, page->images_[2].bounding_rect);
+  EXPECT_EQ("Image 3", page->images_[2].alt_text);
+}
+
+using PDFiumPageTextTest = PDFiumTestBase;
+
+TEST_F(PDFiumPageTextTest, GetTextRunInfo) {
+  TestClient client;
+  std::unique_ptr<PDFiumEngine> engine =
+      InitializeEngine(&client, FILE_PATH_LITERAL("weblinks.pdf"));
+  ASSERT_TRUE(engine);
+
+  int current_char_index = 0;
+
+  // The links span from [7, 22], [52, 66] and [92, 108] with 16, 15 and 17
+  // text run lengths respectively. There are text runs preceding and
+  // succeeding them.
+  PP_PrivateAccessibilityTextRunInfo expected_text_runs[] = {
+      {7, 12,
+       PP_MakeFloatRectFromXYWH(26.666666f, 189.333333f, 38.666672f,
+                                13.333344f),
+       PP_PrivateDirection::PP_PRIVATEDIRECTION_LTR},
+      {16, 12,
+       PP_MakeFloatRectFromXYWH(70.666664f, 189.333333f, 108.0f, 14.666672f),
+       PP_PrivateDirection::PP_PRIVATEDIRECTION_LTR},
+      {20, 12,
+       PP_MakeFloatRectFromXYWH(181.333333f, 189.333333f, 117.333333f,
+                                14.666672f),
+       PP_PrivateDirection::PP_PRIVATEDIRECTION_LTR},
+      {9, 16, PP_MakeFloatRectFromXYWH(28.0f, 117.33334f, 89.333328f, 20.0f),
+       PP_PrivateDirection::PP_PRIVATEDIRECTION_LTR},
+      {15, 16,
+       PP_MakeFloatRectFromXYWH(126.66666f, 117.33334f, 137.33334f, 20.0f),
+       PP_PrivateDirection::PP_PRIVATEDIRECTION_LTR},
+      {20, 16,
+       PP_MakeFloatRectFromXYWH(266.66666f, 118.66666f, 169.33334f, 18.666664f),
+       PP_PrivateDirection::PP_PRIVATEDIRECTION_LTR},
+      {5, 16, PP_MakeFloatRectFromXYWH(28.0f, 65.333336f, 40.0f, 18.666664f),
+       PP_PrivateDirection::PP_PRIVATEDIRECTION_LTR},
+      {17, 16, PP_MakeFloatRectFromXYWH(77.333336f, 64.0f, 160.0f, 20.0f),
+       PP_PrivateDirection::PP_PRIVATEDIRECTION_LTR}};
+
+  if (IsRunningOnChromeOS()) {
+    expected_text_runs[4].bounds =
+        PP_MakeFloatRectFromXYWH(126.66666f, 117.33334f, 137.33334f, 21.33334f);
+    expected_text_runs[5].bounds =
+        PP_MakeFloatRectFromXYWH(266.66666f, 118.66666f, 170.66666f, 20.0f);
+    expected_text_runs[7].bounds =
+        PP_MakeFloatRectFromXYWH(77.333336f, 64.0f, 160.0f, 21.33333f);
+  }
+
+  // Test negative char index returns nullopt
+  base::Optional<PP_PrivateAccessibilityTextRunInfo> text_run_info_result =
+      engine->GetTextRunInfo(0, -1);
+  ASSERT_FALSE(text_run_info_result.has_value());
+
+  // Test valid char index returns expected text run info
+  for (const auto& expected_text_run : expected_text_runs) {
+    text_run_info_result = engine->GetTextRunInfo(0, current_char_index);
+    ASSERT_TRUE(text_run_info_result.has_value());
+    const auto& text_run_info = text_run_info_result.value();
+    EXPECT_EQ(expected_text_run.len, text_run_info.len);
+    EXPECT_EQ(expected_text_run.font_size, text_run_info.font_size);
+    CompareRect(expected_text_run.bounds, text_run_info.bounds);
+    EXPECT_EQ(expected_text_run.direction, text_run_info.direction);
+    current_char_index += text_run_info.len;
+  }
+
+  // Test char index outside char range returns nullopt
+  PDFiumPage* page = GetPDFiumPageForTest(engine.get(), 0);
+  ASSERT_TRUE(page);
+  EXPECT_EQ(page->GetCharCount(), current_char_index);
+  text_run_info_result = engine->GetTextRunInfo(0, current_char_index);
+  ASSERT_FALSE(text_run_info_result.has_value());
 }
 
 }  // namespace chrome_pdf
diff --git a/pdf/pdfium/pdfium_test_base.cc b/pdf/pdfium/pdfium_test_base.cc
index 71fad7d..c9a67e0 100644
--- a/pdf/pdfium/pdfium_test_base.cc
+++ b/pdf/pdfium/pdfium_test_base.cc
@@ -26,6 +26,10 @@
   return std::make_unique<TestDocumentLoader>(client, g_test_pdf_name);
 }
 
+bool IsValidLinkForTesting(const std::string& url) {
+  return !url.empty();
+}
+
 }  // namespace
 
 PDFiumTestBase::PDFiumTestBase() = default;
@@ -43,10 +47,12 @@
 
 void PDFiumTestBase::SetUp() {
   InitializePDFium();
+  PDFiumPage::SetIsValidLinkFunctionForTesting(&IsValidLinkForTesting);
 }
 
 void PDFiumTestBase::TearDown() {
   PDFiumEngine::SetCreateDocumentLoaderFunctionForTesting(nullptr);
+  PDFiumPage::SetIsValidLinkFunctionForTesting(nullptr);
   g_test_pdf_name = nullptr;
   FPDF_DestroyLibrary();
 }
diff --git a/pdf/test/data/weblinks.in b/pdf/test/data/weblinks.in
index 7783dc41..4015980 100644
--- a/pdf/test/data/weblinks.in
+++ b/pdf/test/data/weblinks.in
@@ -46,6 +46,9 @@
 0 50 Td
 /F2 16 Tf
 (Goodbye, http://bing.com! nice meeting you) Tj
+0 40 Td
+/F2 16 Tf
+(Bye, http://google.com) Tj
 ET
 endstream
 endobj
diff --git a/pdf/test/data/weblinks.pdf b/pdf/test/data/weblinks.pdf
index 3dc1701b..f6317ac 100644
--- a/pdf/test/data/weblinks.pdf
+++ b/pdf/test/data/weblinks.pdf
@@ -37,7 +37,7 @@
 >>
 endobj
 6 0 obj <<
- /Length 138
+  /Length 184
 >>
 stream
 BT
@@ -47,6 +47,9 @@
 0 50 Td
 /F2 16 Tf
 (Goodbye, http://bing.com! nice meeting you) Tj
+0 40 Td
+/F2 16 Tf
+(Bye, http://google.com) Tj
 ET
 endstream
 endobj
@@ -64,5 +67,5 @@
   /Size 7
 >>
 startxref
-642
+689
 %%EOF
diff --git a/pdf/test/test_utils.cc b/pdf/test/test_utils.cc
index fab6c64b..4057169 100644
--- a/pdf/test/test_utils.cc
+++ b/pdf/test/test_utils.cc
@@ -16,6 +16,14 @@
   EXPECT_EQ(expected_rect.height(), given_rect.height());
 }
 
+void CompareRect(const pp::FloatRect& expected_rect,
+                 const pp::FloatRect& given_rect) {
+  EXPECT_FLOAT_EQ(expected_rect.x(), given_rect.x());
+  EXPECT_FLOAT_EQ(expected_rect.y(), given_rect.y());
+  EXPECT_FLOAT_EQ(expected_rect.width(), given_rect.width());
+  EXPECT_FLOAT_EQ(expected_rect.height(), given_rect.height());
+}
+
 void CompareSize(const pp::Size& expected_size, const pp::Size& given_size) {
   EXPECT_EQ(expected_size.width(), given_size.width());
   EXPECT_EQ(expected_size.height(), given_size.height());
diff --git a/pdf/test/test_utils.h b/pdf/test/test_utils.h
index fd56039..fb05d06ad 100644
--- a/pdf/test/test_utils.h
+++ b/pdf/test/test_utils.h
@@ -6,6 +6,7 @@
 #define PDF_TEST_TEST_UTILS_H_
 
 namespace pp {
+class FloatRect;
 class Rect;
 class Size;
 }  // namespace pp
@@ -13,6 +14,8 @@
 namespace chrome_pdf {
 
 void CompareRect(const pp::Rect& expected_rect, const pp::Rect& given_rect);
+void CompareRect(const pp::FloatRect& expected_rect,
+                 const pp::FloatRect& given_rect);
 void CompareSize(const pp::Size& expected_size, const pp::Size& given_size);
 
 }  // namespace chrome_pdf
diff --git a/remoting/proto/ftl/v1/ftl_messages.proto b/remoting/proto/ftl/v1/ftl_messages.proto
index ea2c4dfe..efd25bba 100644
--- a/remoting/proto/ftl/v1/ftl_messages.proto
+++ b/remoting/proto/ftl/v1/ftl_messages.proto
@@ -143,7 +143,10 @@
     CHROMOTING_MESSAGE = 29;
   }
 
-  enum MessageClass { USER = 0; }
+  enum MessageClass {
+    USER = 0;    // USER both persists the message and sends a GCM tickle
+    STATUS = 3;  // STATUS persists the message and doesn't send GCM tickle
+  }
 
   string message_id = 1;
   MessageType message_type = 2;
diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd
index 1c5a3de..396cd89 100644
--- a/remoting/resources/remoting_strings.grd
+++ b/remoting/resources/remoting_strings.grd
@@ -1315,20 +1315,20 @@
         <message name="IDS_HOST_AUTHENTICATION_PROMPT" desc="The prompt to be displayed in the authentication dialog when making changes to the system.  The system will add a sentence asking for an administrator's name and password.  Mac-only.">
           <ph name="PRODUCT_NAME">$1<ex>Chrome Remote Desktop</ex></ph> wants to make changes.
         </message>
-        <message name="IDS_ACCESSIBILITY_PERMISSION_DIALOG_TITLE" desc="The title of the prompt used to ask the user to enable the Chrome Remote Desktop service in the accessibility section of the privacy and security preferences pane.  Mac-only.">
-          Allow remote users to control this machine
+        <message name="IDS_ACCESSIBILITY_PERMISSION_DIALOG_TITLE" desc="The title of the prompt that asks the user to turn on the Chrome Remote Desktop service. This setting lives in the 'Accessibility' section of the 'Privacy and Security' preferences pane. Mac-only.">
+          Allow remote users to control this Mac
         </message>
-        <message name="IDS_ACCESSIBILITY_PERMISSION_DIALOG_BODY_TEXT" desc="The body text of the prompt used to ask the user to enable the Chrome Remote Desktop service in the accessibility section of the privacy and security preferences pane.  Mac-only.">
-An additional configuration step is required before you can control this machine remotely using <ph name="PRODUCT_NAME">$1<ex>Chrome Remote Desktop</ex></ph>.
+        <message name="IDS_ACCESSIBILITY_PERMISSION_DIALOG_BODY_TEXT" desc="The body text of the prompt that asks the user to turn on the Chrome Remote Desktop service. This setting lives in the 'Accessibility' section of the 'Privacy and Security' preferences pane. Mac-only.">
+To use <ph name="PRODUCT_NAME">$1<ex>Chrome Remote Desktop</ex></ph> on this Mac, follow these steps:
 
-To enable this functionality, select '<ph name="BUTTON_NAME">$2<ex>Open Preferences</ex></ph>' to display the Security and Privacy preferences pane, then check the box next to '<ph name="SERVICE_SCRIPT_NAME">$3<ex>org.chromium.chromoting.me2me.sh</ex></ph>'.
+Select '<ph name="BUTTON_NAME">$2<ex>Open Preferences</ex></ph>' below. In your Security and Privacy preferences, check the box next to '<ph name="SERVICE_SCRIPT_NAME">$3<ex>org.chromium.chromoting.me2me.sh</ex></ph>'.
 
-If '<ph name="SERVICE_SCRIPT_NAME">$3<ex>org.chromium.chromoting.me2me.sh</ex></ph>' is already present and checked, you may need to uncheck and then recheck it.
+If '<ph name="SERVICE_SCRIPT_NAME">$3<ex>org.chromium.chromoting.me2me.sh</ex></ph>' is already checked, uncheck it and then check it again.
         </message>
-        <message name="IDS_ACCESSIBILITY_PERMISSION_DIALOG_OPEN_BUTTON" desc="The button text of the prompt used to ask the user to enable the Chrome Remote Desktop service in the accessibility section of the privacy and security preferences pane.  Selecting this button will open the security and preferences pane.  Mac-only.">
+        <message name="IDS_ACCESSIBILITY_PERMISSION_DIALOG_OPEN_BUTTON" desc="The button text of the prompt that asks the user to turn on the Chrome Remote Desktop service. This setting lives in the 'Accessibility' section of the 'Privacy and Security' preferences pane. Selecting this button will open the 'Privacy and Security' preferences pane. Mac-only.">
           Open Preferences
         </message>
-        <message name="IDS_ACCESSIBILITY_PERMISSION_DIALOG_NOT_NOW_BUTTON" desc="The button text of the prompt used to ask the user to enable the Chrome Remote Desktop service in the accessibility section of the privacy and security preferences pane.  Selecting this button will close the dialog.  Mac-only.">
+        <message name="IDS_ACCESSIBILITY_PERMISSION_DIALOG_NOT_NOW_BUTTON" desc="The button text of the prompt that asks the user to turn on the Chrome Remote Desktop service. This setting lives in the 'Accessibility' section of the 'Privacy and Security' preferences pane. When the user selects this button, they decline to turn on Chrome Remote Desktop and this dialog will close. Mac-only.">
           Not Now
         </message>
       </if>
diff --git a/remoting/signaling/ftl_messaging_client.cc b/remoting/signaling/ftl_messaging_client.cc
index b84fed1..61e690b 100644
--- a/remoting/signaling/ftl_messaging_client.cc
+++ b/remoting/signaling/ftl_messaging_client.cc
@@ -103,7 +103,7 @@
   request.mutable_message()->set_message_type(
       ftl::InboxMessage_MessageType_CHROMOTING_MESSAGE);
   request.mutable_message()->set_message_class(
-      ftl::InboxMessage_MessageClass_USER);
+      ftl::InboxMessage_MessageClass_STATUS);
   if (!destination_registration_id.empty()) {
     request.add_dest_registration_ids(destination_registration_id);
   }
diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc
index b39170c..7c7c533 100644
--- a/sandbox/win/src/broker_services.cc
+++ b/sandbox/win/src/broker_services.cc
@@ -282,15 +282,27 @@
     return SBOX_ERROR_BAD_PARAMS;
 
   // Even though the resources touched by SpawnTarget can be accessed in
-  // multiple threads, the method itself cannot be called from more than
-  // 1 thread. This is to protect the global variables used while setting up
-  // the child process.
+  // multiple threads, the method itself cannot be called from more than one
+  // thread. This is to protect the global variables used while setting up the
+  // child process, and to make sure launcher thread mitigations are applied
+  // correctly.
   static DWORD thread_id = ::GetCurrentThreadId();
   DCHECK(thread_id == ::GetCurrentThreadId());
   *last_warning = SBOX_ALL_OK;
 
   AutoLock lock(&lock_);
 
+  // Launcher thread only needs to be opted out of ACG once. Do this on the
+  // first child process being spawned.
+  static bool launcher_thread_opted_out = false;
+
+  if (!launcher_thread_opted_out) {
+    // Soft fail this call. It will fail if ACG is not enabled for this process.
+    sandbox::ApplyMitigationsToCurrentThread(
+        sandbox::MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD);
+    launcher_thread_opted_out = true;
+  }
+
   // This downcast is safe as long as we control CreatePolicy()
   scoped_refptr<PolicyBase> policy_base(static_cast<PolicyBase*>(policy.get()));
 
diff --git a/services/device/device_service.cc b/services/device/device_service.cc
index 4054179..9e938ed 100644
--- a/services/device/device_service.cc
+++ b/services/device/device_service.cc
@@ -353,7 +353,7 @@
   if (!usb_device_manager_)
     usb_device_manager_ = std::make_unique<usb::DeviceManagerImpl>();
 
-  usb_device_manager_->AddBinding(std::move(request));
+  usb_device_manager_->AddReceiver(std::move(request));
 }
 
 void DeviceService::BindUsbDeviceManagerTestRequest(
diff --git a/services/device/fingerprint/OWNERS b/services/device/fingerprint/OWNERS
index 6c5faeda6..e8d012e 100644
--- a/services/device/fingerprint/OWNERS
+++ b/services/device/fingerprint/OWNERS
@@ -1,2 +1,3 @@
 xiaoyinh@chromium.org
 sammiequon@chromium.org
+# COMPONENT: UI>Shell>LockScreen
diff --git a/services/device/generic_sensor/platform_sensor_reader_winrt.cc b/services/device/generic_sensor/platform_sensor_reader_winrt.cc
index 4b98561..80150f8 100644
--- a/services/device/generic_sensor/platform_sensor_reader_winrt.cc
+++ b/services/device/generic_sensor/platform_sensor_reader_winrt.cc
@@ -4,11 +4,11 @@
 
 #include "services/device/generic_sensor/platform_sensor_reader_winrt.h"
 
+#include <cmath>
+
 #include "base/win/core_winrt_util.h"
 #include "services/device/generic_sensor/generic_sensor_consts.h"
-#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
 #include "services/device/public/mojom/sensor.mojom.h"
-#include "ui/gfx/geometry/angle_conversions.h"
 
 namespace device {
 
@@ -54,6 +54,16 @@
 using ABI::Windows::Foundation::ITypedEventHandler;
 using Microsoft::WRL::Callback;
 using Microsoft::WRL::ComPtr;
+
+double GetAngleBetweenOrientationSamples(SensorReading reading1,
+                                         SensorReading reading2) {
+  auto dot_product = reading1.orientation_quat.x * reading2.orientation_quat.x +
+                     reading1.orientation_quat.y * reading2.orientation_quat.y +
+                     reading1.orientation_quat.z * reading2.orientation_quat.z +
+                     reading1.orientation_quat.w * reading2.orientation_quat.w;
+
+  return 2.0 * acos(dot_product);
+}
 }  // namespace
 
 std::unique_ptr<PlatformSensorReaderWinBase>
@@ -332,6 +342,9 @@
   return nullptr;
 }
 
+PlatformSensorReaderWinrtLightSensor::PlatformSensorReaderWinrtLightSensor() =
+    default;
+
 HRESULT PlatformSensorReaderWinrtLightSensor::OnReadingChangedCallback(
     ILightSensor* light_sensor,
     ILightSensorReadingChangedEventArgs* reading_changed_args) {
@@ -361,10 +374,17 @@
     return S_OK;
   }
 
-  SensorReading reading;
-  reading.als.value = lux;
-  reading.als.timestamp = timestamp_delta.InSecondsF();
-  client_->OnReadingUpdated(reading);
+  if (!has_received_first_sample_ ||
+      (abs(lux - last_reported_lux_) >=
+       (last_reported_lux_ * kLuxPercentThreshold))) {
+    SensorReading reading;
+    reading.als.value = lux;
+    reading.als.timestamp = timestamp_delta.InSecondsF();
+    client_->OnReadingUpdated(reading);
+
+    last_reported_lux_ = lux;
+    has_received_first_sample_ = true;
+  }
 
   return S_OK;
 }
@@ -380,6 +400,9 @@
   return nullptr;
 }
 
+PlatformSensorReaderWinrtAccelerometer::
+    PlatformSensorReaderWinrtAccelerometer() = default;
+
 HRESULT PlatformSensorReaderWinrtAccelerometer::OnReadingChangedCallback(
     IAccelerometer* accelerometer,
     IAccelerometerReadingChangedEventArgs* reading_changed_args) {
@@ -423,16 +446,26 @@
     return S_OK;
   }
 
-  // Windows.Devices.Sensors.Accelerometer exposes acceleration as
-  // proportional and in the same direction as the force of gravity.
-  // The generic sensor interface exposes acceleration simply as
-  // m/s^2, so the data must be converted.
-  SensorReading reading;
-  reading.accel.x = -x * kMeanGravity;
-  reading.accel.y = -y * kMeanGravity;
-  reading.accel.z = -z * kMeanGravity;
-  reading.accel.timestamp = timestamp_delta.InSecondsF();
-  client_->OnReadingUpdated(reading);
+  if (!has_received_first_sample_ ||
+      (abs(x - last_reported_x_) >= kAxisThreshold) ||
+      (abs(y - last_reported_y_) >= kAxisThreshold) ||
+      (abs(z - last_reported_z_) >= kAxisThreshold)) {
+    // Windows.Devices.Sensors.Accelerometer exposes acceleration as
+    // proportional and in the same direction as the force of gravity.
+    // The generic sensor interface exposes acceleration simply as
+    // m/s^2, so the data must be converted.
+    SensorReading reading;
+    reading.accel.x = -x * kMeanGravity;
+    reading.accel.y = -y * kMeanGravity;
+    reading.accel.z = -z * kMeanGravity;
+    reading.accel.timestamp = timestamp_delta.InSecondsF();
+    client_->OnReadingUpdated(reading);
+
+    last_reported_x_ = x;
+    last_reported_y_ = y;
+    last_reported_z_ = z;
+    has_received_first_sample_ = true;
+  }
 
   return S_OK;
 }
@@ -447,6 +480,9 @@
   return nullptr;
 }
 
+PlatformSensorReaderWinrtGyrometer::PlatformSensorReaderWinrtGyrometer() =
+    default;
+
 HRESULT PlatformSensorReaderWinrtGyrometer::OnReadingChangedCallback(
     IGyrometer* gyrometer,
     IGyrometerReadingChangedEventArgs* reading_changed_args) {
@@ -490,15 +526,25 @@
     return S_OK;
   }
 
-  // Windows.Devices.Sensors.Gyrometer exposes angular velocity as degrees,
-  // but the generic sensor interface uses radians so the data must be
-  // converted.
-  SensorReading reading;
-  reading.gyro.x = gfx::DegToRad(x);
-  reading.gyro.y = gfx::DegToRad(y);
-  reading.gyro.z = gfx::DegToRad(z);
-  reading.gyro.timestamp = timestamp_delta.InSecondsF();
-  client_->OnReadingUpdated(reading);
+  if (!has_received_first_sample_ ||
+      (abs(x - last_reported_x_) >= kDegreeThreshold) ||
+      (abs(y - last_reported_y_) >= kDegreeThreshold) ||
+      (abs(z - last_reported_z_) >= kDegreeThreshold)) {
+    // Windows.Devices.Sensors.Gyrometer exposes angular velocity as degrees,
+    // but the generic sensor interface uses radians so the data must be
+    // converted.
+    SensorReading reading;
+    reading.gyro.x = gfx::DegToRad(x);
+    reading.gyro.y = gfx::DegToRad(y);
+    reading.gyro.z = gfx::DegToRad(z);
+    reading.gyro.timestamp = timestamp_delta.InSecondsF();
+    client_->OnReadingUpdated(reading);
+
+    last_reported_x_ = x;
+    last_reported_y_ = y;
+    last_reported_z_ = z;
+    has_received_first_sample_ = true;
+  }
 
   return S_OK;
 }
@@ -513,6 +559,9 @@
   return nullptr;
 }
 
+PlatformSensorReaderWinrtMagnetometer::PlatformSensorReaderWinrtMagnetometer() =
+    default;
+
 HRESULT PlatformSensorReaderWinrtMagnetometer::OnReadingChangedCallback(
     IMagnetometer* magnetometer,
     IMagnetometerReadingChangedEventArgs* reading_changed_args) {
@@ -556,12 +605,22 @@
     return S_OK;
   }
 
-  SensorReading reading;
-  reading.magn.x = x;
-  reading.magn.y = y;
-  reading.magn.z = z;
-  reading.magn.timestamp = timestamp_delta.InSecondsF();
-  client_->OnReadingUpdated(reading);
+  if (!has_received_first_sample_ ||
+      (abs(x - last_reported_x_) >= kMicroteslaThreshold) ||
+      (abs(y - last_reported_y_) >= kMicroteslaThreshold) ||
+      (abs(z - last_reported_z_) >= kMicroteslaThreshold)) {
+    SensorReading reading;
+    reading.magn.x = x;
+    reading.magn.y = y;
+    reading.magn.z = z;
+    reading.magn.timestamp = timestamp_delta.InSecondsF();
+    client_->OnReadingUpdated(reading);
+
+    last_reported_x_ = x;
+    last_reported_y_ = y;
+    last_reported_z_ = z;
+    has_received_first_sample_ = true;
+  }
 
   return S_OK;
 }
@@ -577,6 +636,9 @@
   return nullptr;
 }
 
+PlatformSensorReaderWinrtAbsOrientationEulerAngles::
+    PlatformSensorReaderWinrtAbsOrientationEulerAngles() = default;
+
 HRESULT
 PlatformSensorReaderWinrtAbsOrientationEulerAngles::OnReadingChangedCallback(
     IInclinometer* inclinometer,
@@ -621,12 +683,22 @@
     return S_OK;
   }
 
-  SensorReading reading;
-  reading.orientation_euler.x = x;
-  reading.orientation_euler.y = y;
-  reading.orientation_euler.z = z;
-  reading.orientation_euler.timestamp = timestamp_delta.InSecondsF();
-  client_->OnReadingUpdated(reading);
+  if (!has_received_first_sample_ ||
+      (abs(x - last_reported_x_) >= kDegreeThreshold) ||
+      (abs(y - last_reported_y_) >= kDegreeThreshold) ||
+      (abs(z - last_reported_z_) >= kDegreeThreshold)) {
+    SensorReading reading;
+    reading.orientation_euler.x = x;
+    reading.orientation_euler.y = y;
+    reading.orientation_euler.z = z;
+    reading.orientation_euler.timestamp = timestamp_delta.InSecondsF();
+    client_->OnReadingUpdated(reading);
+
+    last_reported_x_ = x;
+    last_reported_y_ = y;
+    last_reported_z_ = z;
+    has_received_first_sample_ = true;
+  }
 
   return S_OK;
 }
@@ -642,6 +714,12 @@
   return nullptr;
 }
 
+PlatformSensorReaderWinrtAbsOrientationQuaternion::
+    PlatformSensorReaderWinrtAbsOrientationQuaternion() = default;
+
+PlatformSensorReaderWinrtAbsOrientationQuaternion::
+    ~PlatformSensorReaderWinrtAbsOrientationQuaternion() = default;
+
 HRESULT
 PlatformSensorReaderWinrtAbsOrientationQuaternion::OnReadingChangedCallback(
     IOrientationSensor* orientation_sensor,
@@ -709,7 +787,19 @@
   reading.orientation_quat.y = y;
   reading.orientation_quat.z = z;
   reading.orientation_quat.timestamp = timestamp_delta.InSecondsF();
-  client_->OnReadingUpdated(reading);
+
+  // As per
+  // https://docs.microsoft.com/en-us/windows-hardware/drivers/sensors/orientation-sensor-thresholds,
+  // thresholding should be done on angle between two quaternions:
+  // 2 * cos-1(dot_product(q1, q2))
+  auto angle =
+      abs(GetAngleBetweenOrientationSamples(reading, last_reported_sample));
+  if (!has_received_first_sample_ || (angle >= kRadianThreshold)) {
+    client_->OnReadingUpdated(reading);
+
+    last_reported_sample = reading;
+    has_received_first_sample_ = true;
+  }
 
   return S_OK;
 }
diff --git a/services/device/generic_sensor/platform_sensor_reader_winrt.h b/services/device/generic_sensor/platform_sensor_reader_winrt.h
index 19f8844..7736a3d 100644
--- a/services/device/generic_sensor/platform_sensor_reader_winrt.h
+++ b/services/device/generic_sensor/platform_sensor_reader_winrt.h
@@ -16,6 +16,8 @@
 #include "base/optional.h"
 #include "base/synchronization/lock.h"
 #include "services/device/generic_sensor/platform_sensor_reader_win_base.h"
+#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
+#include "ui/gfx/geometry/angle_conversions.h"
 
 namespace device {
 
@@ -45,6 +47,8 @@
 // interfaces should be passed in. The owner of this class must guarantee
 // construction and destruction occur on the same thread and that no
 // other thread is accessing it during destruction.
+// TODO(crbug.com/995594): Change Windows.Devices.Sensors based
+//   implementation of W3C sensor API to use hardware thresholding.
 template <wchar_t const* runtime_class_id,
           class ISensorWinrtStatics,
           class ISensorWinrtClass,
@@ -102,6 +106,9 @@
   // Null if there is no client to notify, non-null otherwise.
   Client* client_;
 
+  // Always report the first sample received after starting the sensor.
+  bool has_received_first_sample_ = false;
+
  private:
   base::TimeDelta GetMinimumReportIntervalFromSensor();
 
@@ -128,9 +135,13 @@
               Microsoft::WRL::FtmBase>,
           ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs> {
  public:
+  // Lux scales exponentially with perceived brightness so use a relative
+  // threshold instead of an absolute one.
+  static constexpr float kLuxPercentThreshold = 0.2f;  // 20%
+
   static std::unique_ptr<PlatformSensorReaderWinBase> Create();
 
-  PlatformSensorReaderWinrtLightSensor() = default;
+  PlatformSensorReaderWinrtLightSensor();
   ~PlatformSensorReaderWinrtLightSensor() override = default;
 
  protected:
@@ -140,6 +151,8 @@
           reading_changed_args) override;
 
  private:
+  float last_reported_lux_ = 0.0f;
+
   PlatformSensorReaderWinrtLightSensor(
       const PlatformSensorReaderWinrtLightSensor&) = delete;
   PlatformSensorReaderWinrtLightSensor& operator=(
@@ -161,9 +174,11 @@
           ABI::Windows::Devices::Sensors::
               IAccelerometerReadingChangedEventArgs> {
  public:
+  static constexpr double kAxisThreshold = 0.1f;
+
   static std::unique_ptr<PlatformSensorReaderWinBase> Create();
 
-  PlatformSensorReaderWinrtAccelerometer() = default;
+  PlatformSensorReaderWinrtAccelerometer();
   ~PlatformSensorReaderWinrtAccelerometer() override = default;
 
  protected:
@@ -173,6 +188,10 @@
           reading_changed_args) override;
 
  private:
+  double last_reported_x_ = 0.0;
+  double last_reported_y_ = 0.0;
+  double last_reported_z_ = 0.0;
+
   PlatformSensorReaderWinrtAccelerometer(
       const PlatformSensorReaderWinrtAccelerometer&) = delete;
   PlatformSensorReaderWinrtAccelerometer& operator=(
@@ -193,9 +212,11 @@
               Microsoft::WRL::FtmBase>,
           ABI::Windows::Devices::Sensors::IGyrometerReadingChangedEventArgs> {
  public:
+  static constexpr double kDegreeThreshold = 5.0;
+
   static std::unique_ptr<PlatformSensorReaderWinBase> Create();
 
-  PlatformSensorReaderWinrtGyrometer() = default;
+  PlatformSensorReaderWinrtGyrometer();
   ~PlatformSensorReaderWinrtGyrometer() override = default;
 
  protected:
@@ -205,6 +226,10 @@
           reading_changed_args) override;
 
  private:
+  double last_reported_x_ = 0.0;
+  double last_reported_y_ = 0.0;
+  double last_reported_z_ = 0.0;
+
   PlatformSensorReaderWinrtGyrometer(
       const PlatformSensorReaderWinrtGyrometer&) = delete;
   PlatformSensorReaderWinrtGyrometer& operator=(
@@ -226,9 +251,11 @@
           ABI::Windows::Devices::Sensors::
               IMagnetometerReadingChangedEventArgs> {
  public:
+  static constexpr double kMicroteslaThreshold = 5.0f;
+
   static std::unique_ptr<PlatformSensorReaderWinBase> Create();
 
-  PlatformSensorReaderWinrtMagnetometer() = default;
+  PlatformSensorReaderWinrtMagnetometer();
   ~PlatformSensorReaderWinrtMagnetometer() override = default;
 
  protected:
@@ -238,6 +265,10 @@
           reading_changed_args) override;
 
  private:
+  double last_reported_x_ = 0.0;
+  double last_reported_y_ = 0.0;
+  double last_reported_z_ = 0.0;
+
   PlatformSensorReaderWinrtMagnetometer(
       const PlatformSensorReaderWinrtMagnetometer&) = delete;
   PlatformSensorReaderWinrtMagnetometer& operator=(
@@ -259,9 +290,11 @@
           ABI::Windows::Devices::Sensors::
               IInclinometerReadingChangedEventArgs> {
  public:
+  static constexpr double kDegreeThreshold = 5.0f;
+
   static std::unique_ptr<PlatformSensorReaderWinBase> Create();
 
-  PlatformSensorReaderWinrtAbsOrientationEulerAngles() = default;
+  PlatformSensorReaderWinrtAbsOrientationEulerAngles();
   ~PlatformSensorReaderWinrtAbsOrientationEulerAngles() override = default;
 
  protected:
@@ -271,6 +304,10 @@
           reading_changed_args) override;
 
  private:
+  double last_reported_x_ = 0.0;
+  double last_reported_y_ = 0.0;
+  double last_reported_z_ = 0.0;
+
   PlatformSensorReaderWinrtAbsOrientationEulerAngles(
       const PlatformSensorReaderWinrtAbsOrientationEulerAngles&) = delete;
   PlatformSensorReaderWinrtAbsOrientationEulerAngles& operator=(
@@ -292,10 +329,12 @@
           ABI::Windows::Devices::Sensors::
               IOrientationSensorReadingChangedEventArgs> {
  public:
+  static constexpr double kRadianThreshold = gfx::DegToRad(5.0);
+
   static std::unique_ptr<PlatformSensorReaderWinBase> Create();
 
-  PlatformSensorReaderWinrtAbsOrientationQuaternion() = default;
-  ~PlatformSensorReaderWinrtAbsOrientationQuaternion() override = default;
+  PlatformSensorReaderWinrtAbsOrientationQuaternion();
+  ~PlatformSensorReaderWinrtAbsOrientationQuaternion() override;
 
  protected:
   HRESULT OnReadingChangedCallback(
@@ -304,6 +343,8 @@
           reading_changed_args) override;
 
  private:
+  SensorReading last_reported_sample{};
+
   PlatformSensorReaderWinrtAbsOrientationQuaternion(
       const PlatformSensorReaderWinrtAbsOrientationQuaternion&) = delete;
   PlatformSensorReaderWinrtAbsOrientationQuaternion& operator=(
diff --git a/services/device/generic_sensor/platform_sensor_reader_winrt_unittests.cc b/services/device/generic_sensor/platform_sensor_reader_winrt_unittests.cc
index 5f84366..3117c2e 100644
--- a/services/device/generic_sensor/platform_sensor_reader_winrt_unittests.cc
+++ b/services/device/generic_sensor/platform_sensor_reader_winrt_unittests.cc
@@ -15,7 +15,8 @@
 #include "services/device/public/cpp/generic_sensor/sensor_reading.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/angle_conversions.h"
+#include "ui/gfx/geometry/quaternion.h"
+#include "ui/gfx/geometry/vector3d_f.h"
 
 namespace device {
 
@@ -1299,4 +1300,420 @@
   fake_sensor->TriggerFakeSensorReading(reading);
 }
 
-}  // namespace device
+TEST_F(PlatformSensorReaderTestWinrt, LightSensorThresholding) {
+  auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
+      ABI::Windows::Devices::Sensors::ILightSensorStatics,
+      ABI::Windows::Devices::Sensors::ILightSensor,
+      ABI::Windows::Devices::Sensors::LightSensor,
+      ABI::Windows::Devices::Sensors::ILightSensorReading,
+      ABI::Windows::Devices::Sensors::ILightSensorReadingChangedEventArgs,
+      ABI::Windows::Devices::Sensors::LightSensorReadingChangedEventArgs>>();
+  auto fake_sensor = fake_sensor_factory->fake_sensor_;
+
+  auto sensor = std::make_unique<PlatformSensorReaderWinrtLightSensor>();
+  sensor->InitForTests(base::BindLambdaForTesting(
+      [&](ABI::Windows::Devices::Sensors::ILightSensorStatics** sensor_factory)
+          -> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
+  EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
+
+  auto mock_client = std::make_unique<testing::NiceMock<MockClient>>();
+
+  bool expected_callback = false;
+  EXPECT_CALL(*mock_client, OnReadingUpdated(::testing::_))
+      .WillRepeatedly(testing::Invoke(
+          [&](const SensorReading&) { EXPECT_TRUE(expected_callback); }));
+
+  sensor->SetClient(mock_client.get());
+  PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
+  EXPECT_TRUE(sensor->StartSensor(sensor_config));
+
+  float last_sent_lux = 1.0f;
+  auto threshold_helper = [&](bool expect_callback) {
+    expected_callback = expect_callback;
+    auto reading = Microsoft::WRL::Make<FakeLightSensorReadingWinrt>(
+        ABI::Windows::Foundation::DateTime{}, last_sent_lux);
+    fake_sensor->TriggerFakeSensorReading(reading);
+  };
+
+  // Expect callback, first sample
+  threshold_helper(true);
+
+  // No callback, threshold has not been met
+  last_sent_lux +=
+      PlatformSensorReaderWinrtLightSensor::kLuxPercentThreshold * 0.5f;
+  threshold_helper(false);
+
+  // Expect callback, threshold has been met since last reported sample
+  last_sent_lux +=
+      PlatformSensorReaderWinrtLightSensor::kLuxPercentThreshold * 0.6f;
+  threshold_helper(true);
+
+  // Expect callback, threshold has been met exactly
+  last_sent_lux += PlatformSensorReaderWinrtLightSensor::kLuxPercentThreshold;
+  threshold_helper(true);
+
+  sensor->StopSensor();
+}
+
+TEST_F(PlatformSensorReaderTestWinrt, AccelerometerThresholding) {
+  auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
+      ABI::Windows::Devices::Sensors::IAccelerometerStatics,
+      ABI::Windows::Devices::Sensors::IAccelerometer,
+      ABI::Windows::Devices::Sensors::Accelerometer,
+      ABI::Windows::Devices::Sensors::IAccelerometerReading,
+      ABI::Windows::Devices::Sensors::IAccelerometerReadingChangedEventArgs,
+      ABI::Windows::Devices::Sensors::AccelerometerReadingChangedEventArgs>>(
+      Microsoft::WRL::Make<FakeAccelerometerSensorWinrt>());
+  auto fake_sensor = fake_sensor_factory->fake_sensor_;
+
+  auto sensor = std::make_unique<PlatformSensorReaderWinrtAccelerometer>();
+  sensor->InitForTests(base::BindLambdaForTesting(
+      [&](ABI::Windows::Devices::Sensors::IAccelerometerStatics**
+              sensor_factory) -> HRESULT {
+        return fake_sensor_factory.CopyTo(sensor_factory);
+      }));
+  EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
+
+  auto mock_client = std::make_unique<testing::NiceMock<MockClient>>();
+
+  bool expected_callback = false;
+  EXPECT_CALL(*mock_client, OnReadingUpdated(::testing::_))
+      .WillRepeatedly(testing::Invoke(
+          [&](const SensorReading&) { EXPECT_TRUE(expected_callback); }));
+
+  sensor->SetClient(mock_client.get());
+  PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
+  EXPECT_TRUE(sensor->StartSensor(sensor_config));
+
+  double last_sent_x = 1.0f;
+  double last_sent_y = 2.0f;
+  double last_sent_z = 3.0f;
+  auto threshold_helper = [&](bool expect_callback) {
+    expected_callback = expect_callback;
+    auto reading = Microsoft::WRL::Make<FakeAccelerometerReadingWinrt>(
+        ABI::Windows::Foundation::DateTime{}, last_sent_x, last_sent_y,
+        last_sent_z);
+    fake_sensor->TriggerFakeSensorReading(reading);
+  };
+
+  // Expect callback, first sample
+  threshold_helper(true);
+
+  // No callback, threshold has not been met
+  last_sent_x += PlatformSensorReaderWinrtAccelerometer::kAxisThreshold * 0.5f;
+  threshold_helper(false);
+  last_sent_y += PlatformSensorReaderWinrtAccelerometer::kAxisThreshold * 0.5f;
+  threshold_helper(false);
+  last_sent_z += PlatformSensorReaderWinrtAccelerometer::kAxisThreshold * 0.5f;
+  threshold_helper(false);
+
+  // Expect callback, threshold has been met since last reported sample
+  last_sent_x += PlatformSensorReaderWinrtAccelerometer::kAxisThreshold * 0.6f;
+  threshold_helper(true);
+  last_sent_y += PlatformSensorReaderWinrtAccelerometer::kAxisThreshold * 0.6f;
+  threshold_helper(true);
+  last_sent_z += PlatformSensorReaderWinrtAccelerometer::kAxisThreshold * 0.6f;
+  threshold_helper(true);
+
+  // Expect callback, threshold has been met exactly
+  last_sent_x += PlatformSensorReaderWinrtAccelerometer::kAxisThreshold;
+  threshold_helper(true);
+  last_sent_y += PlatformSensorReaderWinrtAccelerometer::kAxisThreshold;
+  threshold_helper(true);
+  last_sent_z += PlatformSensorReaderWinrtAccelerometer::kAxisThreshold;
+  threshold_helper(true);
+
+  sensor->StopSensor();
+}
+
+TEST_F(PlatformSensorReaderTestWinrt, GyrometerThresholding) {
+  auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
+      ABI::Windows::Devices::Sensors::IGyrometerStatics,
+      ABI::Windows::Devices::Sensors::IGyrometer,
+      ABI::Windows::Devices::Sensors::Gyrometer,
+      ABI::Windows::Devices::Sensors::IGyrometerReading,
+      ABI::Windows::Devices::Sensors::IGyrometerReadingChangedEventArgs,
+      ABI::Windows::Devices::Sensors::GyrometerReadingChangedEventArgs>>();
+  auto fake_sensor = fake_sensor_factory->fake_sensor_;
+
+  auto sensor = std::make_unique<PlatformSensorReaderWinrtGyrometer>();
+  sensor->InitForTests(base::BindLambdaForTesting(
+      [&](ABI::Windows::Devices::Sensors::IGyrometerStatics** sensor_factory)
+          -> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
+  EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
+
+  auto mock_client = std::make_unique<testing::NiceMock<MockClient>>();
+
+  bool expected_callback = false;
+  EXPECT_CALL(*mock_client, OnReadingUpdated(::testing::_))
+      .WillRepeatedly(testing::Invoke(
+          [&](const SensorReading&) { EXPECT_TRUE(expected_callback); }));
+
+  sensor->SetClient(mock_client.get());
+  PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
+  EXPECT_TRUE(sensor->StartSensor(sensor_config));
+
+  double last_sent_x = 3.0f;
+  double last_sent_y = 4.0f;
+  double last_sent_z = 5.0f;
+  auto threshold_helper = [&](bool expect_callback) {
+    expected_callback = expect_callback;
+    auto reading = Microsoft::WRL::Make<FakeGyrometerReadingWinrt>(
+        ABI::Windows::Foundation::DateTime{}, last_sent_x, last_sent_y,
+        last_sent_z);
+    fake_sensor->TriggerFakeSensorReading(reading);
+  };
+
+  // Expect callback, first sample
+  threshold_helper(true);
+
+  // No callback, threshold has not been met
+  last_sent_x += PlatformSensorReaderWinrtGyrometer::kDegreeThreshold * 0.5f;
+  threshold_helper(false);
+  last_sent_y += PlatformSensorReaderWinrtGyrometer::kDegreeThreshold * 0.5f;
+  threshold_helper(false);
+  last_sent_z += PlatformSensorReaderWinrtGyrometer::kDegreeThreshold * 0.5f;
+  threshold_helper(false);
+
+  // Expect callback, threshold has been met since last reported sample
+  last_sent_x += PlatformSensorReaderWinrtGyrometer::kDegreeThreshold * 0.6f;
+  threshold_helper(true);
+  last_sent_y += PlatformSensorReaderWinrtGyrometer::kDegreeThreshold * 0.6f;
+  threshold_helper(true);
+  last_sent_z += PlatformSensorReaderWinrtGyrometer::kDegreeThreshold * 0.6f;
+  threshold_helper(true);
+
+  // Expect callback, threshold has been met exactly
+  last_sent_x += PlatformSensorReaderWinrtGyrometer::kDegreeThreshold;
+  threshold_helper(true);
+  last_sent_y += PlatformSensorReaderWinrtGyrometer::kDegreeThreshold;
+  threshold_helper(true);
+  last_sent_z += PlatformSensorReaderWinrtGyrometer::kDegreeThreshold;
+  threshold_helper(true);
+
+  sensor->StopSensor();
+}
+
+TEST_F(PlatformSensorReaderTestWinrt, MagnetometerThresholding) {
+  auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
+      ABI::Windows::Devices::Sensors::IMagnetometerStatics,
+      ABI::Windows::Devices::Sensors::IMagnetometer,
+      ABI::Windows::Devices::Sensors::Magnetometer,
+      ABI::Windows::Devices::Sensors::IMagnetometerReading,
+      ABI::Windows::Devices::Sensors::IMagnetometerReadingChangedEventArgs,
+      ABI::Windows::Devices::Sensors::MagnetometerReadingChangedEventArgs>>();
+  auto fake_sensor = fake_sensor_factory->fake_sensor_;
+
+  auto sensor = std::make_unique<PlatformSensorReaderWinrtMagnetometer>();
+  sensor->InitForTests(base::BindLambdaForTesting(
+      [&](ABI::Windows::Devices::Sensors::IMagnetometerStatics** sensor_factory)
+          -> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
+  EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
+
+  auto mock_client = std::make_unique<testing::NiceMock<MockClient>>();
+
+  bool expected_callback = false;
+  EXPECT_CALL(*mock_client, OnReadingUpdated(::testing::_))
+      .WillRepeatedly(testing::Invoke(
+          [&](const SensorReading&) { EXPECT_TRUE(expected_callback); }));
+
+  sensor->SetClient(mock_client.get());
+  PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
+  EXPECT_TRUE(sensor->StartSensor(sensor_config));
+
+  double last_sent_x = 3.0f;
+  double last_sent_y = 4.0f;
+  double last_sent_z = 5.0f;
+  auto threshold_helper = [&](bool expect_callback) {
+    expected_callback = expect_callback;
+    auto reading = Microsoft::WRL::Make<FakeMagnetometerReadingWinrt>(
+        ABI::Windows::Foundation::DateTime{}, last_sent_x, last_sent_y,
+        last_sent_z);
+    fake_sensor->TriggerFakeSensorReading(reading);
+  };
+
+  // Expect callback, first sample
+  threshold_helper(true);
+
+  // No callback, threshold has not been met
+  last_sent_x +=
+      PlatformSensorReaderWinrtMagnetometer::kMicroteslaThreshold * 0.5f;
+  threshold_helper(false);
+  last_sent_y +=
+      PlatformSensorReaderWinrtMagnetometer::kMicroteslaThreshold * 0.5f;
+  threshold_helper(false);
+  last_sent_z +=
+      PlatformSensorReaderWinrtMagnetometer::kMicroteslaThreshold * 0.5f;
+  threshold_helper(false);
+
+  // Expect callback, threshold has been met since last reported sample
+  last_sent_x +=
+      PlatformSensorReaderWinrtMagnetometer::kMicroteslaThreshold * 0.6f;
+  threshold_helper(true);
+  last_sent_y +=
+      PlatformSensorReaderWinrtMagnetometer::kMicroteslaThreshold * 0.6f;
+  threshold_helper(true);
+  last_sent_z +=
+      PlatformSensorReaderWinrtMagnetometer::kMicroteslaThreshold * 0.6f;
+  threshold_helper(true);
+
+  // Expect callback, threshold has been met exactly
+  last_sent_x += PlatformSensorReaderWinrtMagnetometer::kMicroteslaThreshold;
+  threshold_helper(true);
+  last_sent_y += PlatformSensorReaderWinrtMagnetometer::kMicroteslaThreshold;
+  threshold_helper(true);
+  last_sent_z += PlatformSensorReaderWinrtMagnetometer::kMicroteslaThreshold;
+  threshold_helper(true);
+
+  sensor->StopSensor();
+}
+
+TEST_F(PlatformSensorReaderTestWinrt, AbsOrientationEulerThresholding) {
+  auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
+      ABI::Windows::Devices::Sensors::IInclinometerStatics,
+      ABI::Windows::Devices::Sensors::IInclinometer,
+      ABI::Windows::Devices::Sensors::Inclinometer,
+      ABI::Windows::Devices::Sensors::IInclinometerReading,
+      ABI::Windows::Devices::Sensors::IInclinometerReadingChangedEventArgs,
+      ABI::Windows::Devices::Sensors::InclinometerReadingChangedEventArgs>>();
+  auto fake_sensor = fake_sensor_factory->fake_sensor_;
+
+  auto sensor =
+      std::make_unique<PlatformSensorReaderWinrtAbsOrientationEulerAngles>();
+  sensor->InitForTests(base::BindLambdaForTesting(
+      [&](ABI::Windows::Devices::Sensors::IInclinometerStatics** sensor_factory)
+          -> HRESULT { return fake_sensor_factory.CopyTo(sensor_factory); }));
+  EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
+
+  auto mock_client = std::make_unique<testing::NiceMock<MockClient>>();
+
+  bool expected_callback = false;
+  EXPECT_CALL(*mock_client, OnReadingUpdated(::testing::_))
+      .WillRepeatedly(testing::Invoke(
+          [&](const SensorReading&) { EXPECT_TRUE(expected_callback); }));
+
+  sensor->SetClient(mock_client.get());
+  PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
+  EXPECT_TRUE(sensor->StartSensor(sensor_config));
+
+  double last_sent_x = 3.0f;
+  double last_sent_y = 4.0f;
+  double last_sent_z = 5.0f;
+  auto threshold_helper = [&](bool expect_callback) {
+    expected_callback = expect_callback;
+    auto reading = Microsoft::WRL::Make<FakeInclinometerReadingWinrt>(
+        ABI::Windows::Foundation::DateTime{}, last_sent_x, last_sent_y,
+        last_sent_z);
+    fake_sensor->TriggerFakeSensorReading(reading);
+  };
+
+  // Expect callback, first sample
+  threshold_helper(true);
+
+  // No callback, threshold has not been met
+  last_sent_x +=
+      PlatformSensorReaderWinrtAbsOrientationEulerAngles::kDegreeThreshold *
+      0.5f;
+  threshold_helper(false);
+  last_sent_y +=
+      PlatformSensorReaderWinrtAbsOrientationEulerAngles::kDegreeThreshold *
+      0.5f;
+  threshold_helper(false);
+  last_sent_z +=
+      PlatformSensorReaderWinrtAbsOrientationEulerAngles::kDegreeThreshold *
+      0.5f;
+  threshold_helper(false);
+
+  // Expect callback, threshold has been met since last reported sample
+  last_sent_x +=
+      PlatformSensorReaderWinrtAbsOrientationEulerAngles::kDegreeThreshold *
+      0.6f;
+  threshold_helper(true);
+  last_sent_y +=
+      PlatformSensorReaderWinrtAbsOrientationEulerAngles::kDegreeThreshold *
+      0.6f;
+  threshold_helper(true);
+  last_sent_z +=
+      PlatformSensorReaderWinrtAbsOrientationEulerAngles::kDegreeThreshold *
+      0.6f;
+  threshold_helper(true);
+
+  // Expect callback, threshold has been met exactly
+  last_sent_x +=
+      PlatformSensorReaderWinrtAbsOrientationEulerAngles::kDegreeThreshold;
+  threshold_helper(true);
+  last_sent_y +=
+      PlatformSensorReaderWinrtAbsOrientationEulerAngles::kDegreeThreshold;
+  threshold_helper(true);
+  last_sent_z +=
+      PlatformSensorReaderWinrtAbsOrientationEulerAngles::kDegreeThreshold;
+  threshold_helper(true);
+
+  sensor->StopSensor();
+}
+
+TEST_F(PlatformSensorReaderTestWinrt, AbsOrientationQuatThresholding) {
+  auto fake_sensor_factory = Microsoft::WRL::Make<FakeSensorFactoryWinrt<
+      ABI::Windows::Devices::Sensors::IOrientationSensorStatics,
+      ABI::Windows::Devices::Sensors::IOrientationSensor,
+      ABI::Windows::Devices::Sensors::OrientationSensor,
+      ABI::Windows::Devices::Sensors::IOrientationSensorReading,
+      ABI::Windows::Devices::Sensors::IOrientationSensorReadingChangedEventArgs,
+      ABI::Windows::Devices::Sensors::
+          OrientationSensorReadingChangedEventArgs>>();
+  auto fake_sensor = fake_sensor_factory->fake_sensor_;
+
+  auto sensor =
+      std::make_unique<PlatformSensorReaderWinrtAbsOrientationQuaternion>();
+  sensor->InitForTests(base::BindLambdaForTesting(
+      [&](ABI::Windows::Devices::Sensors::IOrientationSensorStatics**
+              sensor_factory) -> HRESULT {
+        return fake_sensor_factory.CopyTo(sensor_factory);
+      }));
+  EXPECT_EQ(sensor->Initialize(), SensorWinrtCreateFailure::kOk);
+
+  auto mock_client = std::make_unique<testing::NiceMock<MockClient>>();
+
+  bool expected_callback = false;
+  EXPECT_CALL(*mock_client, OnReadingUpdated(::testing::_))
+      .WillRepeatedly(testing::Invoke(
+          [&](const SensorReading&) { EXPECT_TRUE(expected_callback); }));
+
+  sensor->SetClient(mock_client.get());
+  PlatformSensorConfiguration sensor_config(kExpectedReportFrequencySet);
+  EXPECT_TRUE(sensor->StartSensor(sensor_config));
+
+  double last_sent_rad = 1.0;
+  auto threshold_helper = [&](bool expect_callback) {
+    expected_callback = expect_callback;
+    auto quat = gfx::Quaternion(gfx::Vector3dF(1.0, 0, 0), last_sent_rad);
+    auto reading = Microsoft::WRL::Make<FakeOrientationSensorReadingWinrt>(
+        ABI::Windows::Foundation::DateTime{}, quat.w(), quat.x(), quat.y(),
+        quat.z());
+    fake_sensor->TriggerFakeSensorReading(reading);
+  };
+
+  // Expect callback, first sample
+  threshold_helper(true);
+
+  // No callback, threshold has not been met
+  last_sent_rad +=
+      PlatformSensorReaderWinrtAbsOrientationQuaternion::kRadianThreshold *
+      0.5f;
+  threshold_helper(false);
+
+  // Expect callback, threshold has been met since last reported sample
+  last_sent_rad +=
+      PlatformSensorReaderWinrtAbsOrientationQuaternion::kRadianThreshold *
+      0.6f;
+  threshold_helper(true);
+
+  // Expect callback, threshold has been met exactly
+  last_sent_rad +=
+      PlatformSensorReaderWinrtAbsOrientationQuaternion::kRadianThreshold;
+  threshold_helper(true);
+
+  sensor->StopSensor();
+}
+
+}  // namespace device
\ No newline at end of file
diff --git a/services/device/nfc/android/BUILD.gn b/services/device/nfc/android/BUILD.gn
index 7448b78..c47608c 100644
--- a/services/device/nfc/android/BUILD.gn
+++ b/services/device/nfc/android/BUILD.gn
@@ -12,11 +12,11 @@
   ]
   java_files = [
     "java/src/org/chromium/device/nfc/InvalidNdefMessageException.java",
-    "java/src/org/chromium/device/nfc/NfcProviderImpl.java",
-    "java/src/org/chromium/device/nfc/NfcImpl.java",
+    "java/src/org/chromium/device/nfc/NdefMessageUtils.java",
     "java/src/org/chromium/device/nfc/NdefMessageValidator.java",
+    "java/src/org/chromium/device/nfc/NfcImpl.java",
+    "java/src/org/chromium/device/nfc/NfcProviderImpl.java",
     "java/src/org/chromium/device/nfc/NfcTagHandler.java",
-    "java/src/org/chromium/device/nfc/NfcTypeConverter.java",
   ]
 
   deps = [
diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTypeConverter.java b/services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageUtils.java
similarity index 89%
rename from services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTypeConverter.java
rename to services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageUtils.java
index 7ed7202..d19ad24 100644
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTypeConverter.java
+++ b/services/device/nfc/android/java/src/org/chromium/device/nfc/NdefMessageUtils.java
@@ -22,11 +22,10 @@
  * Utility class that provides convesion between Android NdefMessage
  * and mojo NdefMessage data structures.
  */
-public final class NfcTypeConverter {
-    private static final String TAG = "NfcTypeConverter";
-    private static final String DOMAIN = "w3.org";
-    private static final String TYPE = "webnfc";
-    private static final String WEBNFC_URN = DOMAIN + ":" + TYPE;
+public final class NdefMessageUtils {
+    private static final String TAG = "NdefMessageUtils";
+    private static final String AUTHOR_RECORD_DOMAIN = "w3.org";
+    private static final String AUTHOR_RECORD_TYPE = "A";
     private static final String TEXT_MIME = "text/plain";
     private static final String JSON_MIME = "application/json";
     private static final String CHARSET_UTF8 = ";charset=UTF-8";
@@ -42,8 +41,10 @@
             for (int i = 0; i < message.data.length; ++i) {
                 records.add(toNdefRecord(message.data[i]));
             }
-            records.add(android.nfc.NdefRecord.createExternal(
-                    DOMAIN, TYPE, ApiCompatibilityUtils.getBytesUtf8(message.url)));
+            // NdefRecord.createExternal() will internally convert both the domain and type to
+            // lower-case. Details: https://github.com/w3c/web-nfc/issues/308
+            records.add(android.nfc.NdefRecord.createExternal(AUTHOR_RECORD_DOMAIN,
+                    AUTHOR_RECORD_TYPE, ApiCompatibilityUtils.getBytesUtf8(message.url)));
             android.nfc.NdefRecord[] ndefRecords = new android.nfc.NdefRecord[records.size()];
             records.toArray(ndefRecords);
             return new android.nfc.NdefMessage(ndefRecords);
@@ -63,9 +64,13 @@
         List<NdefRecord> nfcRecords = new ArrayList<NdefRecord>();
 
         for (int i = 0; i < ndefRecords.length; i++) {
+            // NFC Forum requires that the domain and type used in an external record are treated as
+            // case insensitive, so we compare while ignoring the case.
             if ((ndefRecords[i].getTnf() == android.nfc.NdefRecord.TNF_EXTERNAL_TYPE)
-                    && (Arrays.equals(ndefRecords[i].getType(),
-                            ApiCompatibilityUtils.getBytesUtf8(WEBNFC_URN)))) {
+                    && new String(ndefRecords[i].getType(), "UTF-8")
+                                    .compareToIgnoreCase(
+                                            AUTHOR_RECORD_DOMAIN + ":" + AUTHOR_RECORD_TYPE)
+                            == 0) {
                 webNdefMessage.url = new String(ndefRecords[i].getPayload(), "UTF-8");
                 continue;
             }
diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
index f0f8d09..4c78cdd4 100644
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
+++ b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
@@ -525,7 +525,7 @@
 
         try {
             mTagHandler.connect();
-            mTagHandler.write(NfcTypeConverter.toNdefMessage(mPendingPushOperation.ndefMessage));
+            mTagHandler.write(NdefMessageUtils.toNdefMessage(mPendingPushOperation.ndefMessage));
             pendingPushOperationCompleted(null);
         } catch (InvalidNdefMessageException e) {
             Log.w(TAG, "Cannot write data to NFC tag. Invalid NdefMessage.");
@@ -582,7 +582,7 @@
      */
     private void notifyMatchingWatchers(android.nfc.NdefMessage message, int compatibility) {
         try {
-            NdefMessage ndefMessage = NfcTypeConverter.toNdefMessage(message);
+            NdefMessage ndefMessage = NdefMessageUtils.toNdefMessage(message);
             List<Integer> watchIds = new ArrayList<Integer>();
             for (int i = 0; i < mWatchers.size(); i++) {
                 NfcReaderOptions options = mWatchers.valueAt(i);
diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java
index f481691e..9c600b0 100644
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java
+++ b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcTagHandler.java
@@ -109,7 +109,7 @@
 
         @Override
         public NdefMessage read() throws FormatException {
-            return NfcTypeConverter.emptyNdefMessage();
+            return NdefMessageUtils.emptyNdefMessage();
         }
     }
 
diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/OWNERS b/services/device/nfc/android/java/src/org/chromium/device/nfc/OWNERS
deleted file mode 100644
index a0a2749..0000000
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *TypeConverter*.*=set noparent
-per-file *TypeConverter*.*=file://ipc/SECURITY_OWNERS
diff --git a/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java b/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java
index d62b75f6..e998a66 100644
--- a/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java
+++ b/services/device/nfc/android/junit/src/org/chromium/device/nfc/NFCTest.java
@@ -65,7 +65,7 @@
 import java.io.UnsupportedEncodingException;
 
 /**
- * Unit tests for NfcImpl and NfcTypeConverter classes.
+ * Unit tests for NfcImpl and NdefMessageUtils classes.
  */
 @RunWith(LocalRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
@@ -93,8 +93,8 @@
     private static final String TEST_TEXT = "test";
     private static final String TEST_URL = "https://google.com";
     private static final String TEST_JSON = "{\"key1\":\"value1\",\"key2\":2}";
-    private static final String DOMAIN = "w3.org";
-    private static final String TYPE = "webnfc";
+    private static final String AUTHOR_RECORD_DOMAIN = "w3.org";
+    private static final String AUTHOR_RECORD_TYPE = "A";
     private static final String TEXT_MIME = "text/plain";
     private static final String JSON_MIME = "application/json";
     private static final String CHARSET_UTF8 = ";charset=UTF-8";
@@ -216,7 +216,7 @@
         // Test EMPTY record conversion.
         android.nfc.NdefMessage emptyNdefMessage = new android.nfc.NdefMessage(
                 new android.nfc.NdefRecord(android.nfc.NdefRecord.TNF_EMPTY, null, null, null));
-        NdefMessage emptyMojoNdefMessage = NfcTypeConverter.toNdefMessage(emptyNdefMessage);
+        NdefMessage emptyMojoNdefMessage = NdefMessageUtils.toNdefMessage(emptyNdefMessage);
         assertNull(emptyMojoNdefMessage.url);
         assertEquals(1, emptyMojoNdefMessage.data.length);
         assertEquals(NdefRecordType.EMPTY, emptyMojoNdefMessage.data[0].recordType);
@@ -226,7 +226,7 @@
         // Test URL record conversion.
         android.nfc.NdefMessage urlNdefMessage =
                 new android.nfc.NdefMessage(android.nfc.NdefRecord.createUri(TEST_URL));
-        NdefMessage urlMojoNdefMessage = NfcTypeConverter.toNdefMessage(urlNdefMessage);
+        NdefMessage urlMojoNdefMessage = NdefMessageUtils.toNdefMessage(urlNdefMessage);
         assertNull(urlMojoNdefMessage.url);
         assertEquals(1, urlMojoNdefMessage.data.length);
         assertEquals(NdefRecordType.URL, urlMojoNdefMessage.data[0].recordType);
@@ -236,7 +236,7 @@
         // Test TEXT record conversion.
         android.nfc.NdefMessage textNdefMessage = new android.nfc.NdefMessage(
                 android.nfc.NdefRecord.createTextRecord(LANG_EN_US, TEST_TEXT));
-        NdefMessage textMojoNdefMessage = NfcTypeConverter.toNdefMessage(textNdefMessage);
+        NdefMessage textMojoNdefMessage = NdefMessageUtils.toNdefMessage(textNdefMessage);
         assertNull(textMojoNdefMessage.url);
         assertEquals(1, textMojoNdefMessage.data.length);
         assertEquals(NdefRecordType.TEXT, textMojoNdefMessage.data[0].recordType);
@@ -247,7 +247,7 @@
         android.nfc.NdefMessage mimeNdefMessage =
                 new android.nfc.NdefMessage(android.nfc.NdefRecord.createMime(
                         TEXT_MIME, ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT)));
-        NdefMessage mimeMojoNdefMessage = NfcTypeConverter.toNdefMessage(mimeNdefMessage);
+        NdefMessage mimeMojoNdefMessage = NdefMessageUtils.toNdefMessage(mimeNdefMessage);
         assertNull(mimeMojoNdefMessage.url);
         assertEquals(1, mimeMojoNdefMessage.data.length);
         assertEquals(NdefRecordType.OPAQUE_RECORD, mimeMojoNdefMessage.data[0].recordType);
@@ -258,7 +258,7 @@
         android.nfc.NdefMessage jsonNdefMessage =
                 new android.nfc.NdefMessage(android.nfc.NdefRecord.createMime(
                         JSON_MIME, ApiCompatibilityUtils.getBytesUtf8(TEST_JSON)));
-        NdefMessage jsonMojoNdefMessage = NfcTypeConverter.toNdefMessage(jsonNdefMessage);
+        NdefMessage jsonMojoNdefMessage = NdefMessageUtils.toNdefMessage(jsonNdefMessage);
         assertNull(jsonMojoNdefMessage.url);
         assertEquals(1, jsonMojoNdefMessage.data.length);
         assertEquals(NdefRecordType.JSON, jsonMojoNdefMessage.data[0].recordType);
@@ -268,11 +268,12 @@
         // Test NdefMessage with WebNFC external type.
         android.nfc.NdefRecord jsonNdefRecord = android.nfc.NdefRecord.createMime(
                 JSON_MIME, ApiCompatibilityUtils.getBytesUtf8(TEST_JSON));
-        android.nfc.NdefRecord extNdefRecord = android.nfc.NdefRecord.createExternal(
-                DOMAIN, TYPE, ApiCompatibilityUtils.getBytesUtf8(TEST_URL));
+        android.nfc.NdefRecord extNdefRecord =
+                android.nfc.NdefRecord.createExternal(AUTHOR_RECORD_DOMAIN, AUTHOR_RECORD_TYPE,
+                        ApiCompatibilityUtils.getBytesUtf8(TEST_URL));
         android.nfc.NdefMessage webNdefMessage =
                 new android.nfc.NdefMessage(jsonNdefRecord, extNdefRecord);
-        NdefMessage webMojoNdefMessage = NfcTypeConverter.toNdefMessage(webNdefMessage);
+        NdefMessage webMojoNdefMessage = NdefMessageUtils.toNdefMessage(webNdefMessage);
         assertEquals(TEST_URL, webMojoNdefMessage.url);
         assertEquals(1, webMojoNdefMessage.data.length);
         assertEquals(NdefRecordType.JSON, webMojoNdefMessage.data[0].recordType);
@@ -294,7 +295,9 @@
         assertEquals(TEST_URL, urlNdefMessage.getRecords()[0].toUri().toString());
         assertEquals(
                 android.nfc.NdefRecord.TNF_EXTERNAL_TYPE, urlNdefMessage.getRecords()[1].getTnf());
-        assertEquals(DOMAIN + ":" + TYPE, new String(urlNdefMessage.getRecords()[1].getType()));
+        assertEquals(0,
+                new String(urlNdefMessage.getRecords()[1].getType())
+                        .compareToIgnoreCase(AUTHOR_RECORD_DOMAIN + ":" + AUTHOR_RECORD_TYPE));
 
         // Test TEXT record conversion.
         NdefRecord textMojoNdefRecord = new NdefRecord();
@@ -303,7 +306,7 @@
         textMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT);
         NdefMessage textMojoNdefMessage = createMojoNdefMessage(TEST_URL, textMojoNdefRecord);
         android.nfc.NdefMessage textNdefMessage =
-                NfcTypeConverter.toNdefMessage(textMojoNdefMessage);
+                NdefMessageUtils.toNdefMessage(textMojoNdefMessage);
         assertEquals(2, textNdefMessage.getRecords().length);
         short tnf = textNdefMessage.getRecords()[0].getTnf();
         boolean isWellKnownOrMime = tnf == android.nfc.NdefRecord.TNF_WELL_KNOWN
@@ -319,7 +322,7 @@
         mimeMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_TEXT);
         NdefMessage mimeMojoNdefMessage = createMojoNdefMessage(TEST_URL, mimeMojoNdefRecord);
         android.nfc.NdefMessage mimeNdefMessage =
-                NfcTypeConverter.toNdefMessage(mimeMojoNdefMessage);
+                NdefMessageUtils.toNdefMessage(mimeMojoNdefMessage);
         assertEquals(2, mimeNdefMessage.getRecords().length);
         assertEquals(
                 android.nfc.NdefRecord.TNF_MIME_MEDIA, mimeNdefMessage.getRecords()[0].getTnf());
@@ -335,7 +338,7 @@
         jsonMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_JSON);
         NdefMessage jsonMojoNdefMessage = createMojoNdefMessage(TEST_URL, jsonMojoNdefRecord);
         android.nfc.NdefMessage jsonNdefMessage =
-                NfcTypeConverter.toNdefMessage(jsonMojoNdefMessage);
+                NdefMessageUtils.toNdefMessage(jsonMojoNdefMessage);
         assertEquals(2, jsonNdefMessage.getRecords().length);
         assertEquals(
                 android.nfc.NdefRecord.TNF_MIME_MEDIA, jsonNdefMessage.getRecords()[0].getTnf());
@@ -349,7 +352,7 @@
         emptyMojoNdefRecord.recordType = NdefRecordType.EMPTY;
         NdefMessage emptyMojoNdefMessage = createMojoNdefMessage(TEST_URL, emptyMojoNdefRecord);
         android.nfc.NdefMessage emptyNdefMessage =
-                NfcTypeConverter.toNdefMessage(emptyMojoNdefMessage);
+                NdefMessageUtils.toNdefMessage(emptyMojoNdefMessage);
         assertEquals(2, emptyNdefMessage.getRecords().length);
         assertEquals(android.nfc.NdefRecord.TNF_EMPTY, emptyNdefMessage.getRecords()[0].getTnf());
         assertEquals(android.nfc.NdefRecord.TNF_EXTERNAL_TYPE,
@@ -594,7 +597,7 @@
         // Should not match
         NfcReaderOptions options4 = createNfcReaderOptions();
         options4.compatibility = NdefCompatibility.NFC_FORUM;
-        options4.url = DOMAIN;
+        options4.url = AUTHOR_RECORD_DOMAIN;
         int watchId4 = mNextWatchId++;
         WatchResponse mockWatchCallback4 = mock(WatchResponse.class);
         nfc.watch(options4, watchId4, mockWatchCallback4);
@@ -1326,7 +1329,7 @@
         urlMojoNdefRecord.data = ApiCompatibilityUtils.getBytesUtf8(TEST_URL);
         NdefMessage urlNdefMessage = createMojoNdefMessage(webNfcId, urlMojoNdefRecord);
         try {
-            return NfcTypeConverter.toNdefMessage(urlNdefMessage);
+            return NdefMessageUtils.toNdefMessage(urlNdefMessage);
         } catch (InvalidNdefMessageException e) {
             return null;
         }
diff --git a/services/device/public/cpp/test/fake_usb_device_manager.cc b/services/device/public/cpp/test/fake_usb_device_manager.cc
index 1edd3e1a..ba495da 100644
--- a/services/device/public/cpp/test/fake_usb_device_manager.cc
+++ b/services/device/public/cpp/test/fake_usb_device_manager.cc
@@ -95,8 +95,9 @@
   clients_.AddPtr(std::move(client_ptr));
 }
 
-void FakeUsbDeviceManager::AddBinding(mojom::UsbDeviceManagerRequest request) {
-  bindings_.AddBinding(this, std::move(request));
+void FakeUsbDeviceManager::AddReceiver(
+    mojo::PendingReceiver<mojom::UsbDeviceManager> receiver) {
+  receivers_.Add(this, std::move(receiver));
 }
 
 mojom::UsbDeviceInfoPtr FakeUsbDeviceManager::AddDevice(
diff --git a/services/device/public/cpp/test/fake_usb_device_manager.h b/services/device/public/cpp/test/fake_usb_device_manager.h
index b9563bd..d957ced1 100644
--- a/services/device/public/cpp/test/fake_usb_device_manager.h
+++ b/services/device/public/cpp/test/fake_usb_device_manager.h
@@ -12,8 +12,9 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
 #include "build/build_config.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "services/device/public/cpp/test/fake_usb_device_info.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
@@ -32,7 +33,7 @@
   FakeUsbDeviceManager();
   ~FakeUsbDeviceManager() override;
 
-  void AddBinding(mojom::UsbDeviceManagerRequest request);
+  void AddReceiver(mojo::PendingReceiver<mojom::UsbDeviceManager> receiver);
 
   // Create a device and add it to added_devices_.
   template <typename... Args>
@@ -51,9 +52,9 @@
   bool SetMockForDevice(const std::string& guid,
                         MockUsbMojoDevice* mock_device);
 
-  bool IsBound() { return !bindings_.empty(); }
+  bool IsBound() { return !receivers_.empty(); }
 
-  void CloseAllBindings() { bindings_.CloseAllBindings(); }
+  void CloseAllBindings() { receivers_.Clear(); }
 
   void RemoveAllDevices();
 
@@ -87,7 +88,7 @@
   void SetClient(
       mojom::UsbDeviceManagerClientAssociatedPtrInfo client) override;
 
-  mojo::BindingSet<mojom::UsbDeviceManager> bindings_;
+  mojo::ReceiverSet<mojom::UsbDeviceManager> receivers_;
   mojo::AssociatedInterfacePtrSet<mojom::UsbDeviceManagerClient> clients_;
 
   DeviceMap devices_;
diff --git a/services/device/usb/mojo/device_manager_impl.cc b/services/device/usb/mojo/device_manager_impl.cc
index 035429c..c238edba 100644
--- a/services/device/usb/mojo/device_manager_impl.cc
+++ b/services/device/usb/mojo/device_manager_impl.cc
@@ -40,9 +40,10 @@
 
 DeviceManagerImpl::~DeviceManagerImpl() = default;
 
-void DeviceManagerImpl::AddBinding(mojom::UsbDeviceManagerRequest request) {
+void DeviceManagerImpl::AddReceiver(
+    mojo::PendingReceiver<mojom::UsbDeviceManager> receiver) {
   if (usb_service_)
-    bindings_.AddBinding(this, std::move(request));
+    receivers_.Add(this, std::move(receiver));
 }
 
 void DeviceManagerImpl::EnumerateDevicesAndSetClient(
@@ -201,7 +202,7 @@
   usb_service_ = nullptr;
 
   // Close all the connections.
-  bindings_.CloseAllBindings();
+  receivers_.Clear();
   clients_.CloseAll();
 }
 
diff --git a/services/device/usb/mojo/device_manager_impl.h b/services/device/usb/mojo/device_manager_impl.h
index 349296e..6d02e47 100644
--- a/services/device/usb/mojo/device_manager_impl.h
+++ b/services/device/usb/mojo/device_manager_impl.h
@@ -17,8 +17,9 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
 #include "build/build_config.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "services/device/public/mojom/usb_manager.mojom.h"
 #include "services/device/usb/usb_service.h"
 
@@ -38,7 +39,7 @@
   explicit DeviceManagerImpl(std::unique_ptr<UsbService> usb_service);
   ~DeviceManagerImpl() override;
 
-  void AddBinding(mojom::UsbDeviceManagerRequest request);
+  void AddReceiver(mojo::PendingReceiver<mojom::UsbDeviceManager> receiver);
 
   UsbService* GetUsbService() const { return usb_service_.get(); }
 
@@ -95,7 +96,7 @@
   std::unique_ptr<UsbService> usb_service_;
   ScopedObserver<UsbService, UsbService::Observer> observer_;
 
-  mojo::BindingSet<mojom::UsbDeviceManager> bindings_;
+  mojo::ReceiverSet<mojom::UsbDeviceManager> receivers_;
   mojo::AssociatedInterfacePtrSet<mojom::UsbDeviceManagerClient> clients_;
 
   base::WeakPtrFactory<DeviceManagerImpl> weak_factory_{this};
diff --git a/services/device/usb/mojo/device_manager_impl_unittest.cc b/services/device/usb/mojo/device_manager_impl_unittest.cc
index 3dcea39..468eeb4 100644
--- a/services/device/usb/mojo/device_manager_impl_unittest.cc
+++ b/services/device/usb/mojo/device_manager_impl_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/usb_enumeration_options.mojom.h"
 #include "services/device/public/mojom/usb_manager_client.mojom.h"
 #include "services/device/usb/mock_usb_device.h"
@@ -33,8 +34,8 @@
 namespace device {
 
 using mojom::UsbDeviceInfoPtr;
+using mojom::UsbDeviceManager;
 using mojom::UsbDeviceManagerClientPtr;
-using mojom::UsbDeviceManagerPtr;
 using mojom::UsbDevicePtr;
 using mojom::UsbEnumerationOptionsPtr;
 
@@ -117,8 +118,9 @@
   mock_usb_service_->AddDevice(device1);
   mock_usb_service_->AddDevice(device2);
 
-  UsbDeviceManagerPtr device_manager;
-  device_manager_instance_->AddBinding(mojo::MakeRequest(&device_manager));
+  mojo::Remote<UsbDeviceManager> device_manager;
+  device_manager_instance_->AddReceiver(
+      device_manager.BindNewPipeAndPassReceiver());
 
   auto filter = mojom::UsbDeviceFilter::New();
   filter->has_vendor_id = true;
@@ -145,8 +147,9 @@
 
   mock_usb_service_->AddDevice(mock_device);
 
-  UsbDeviceManagerPtr device_manager;
-  device_manager_instance_->AddBinding(mojo::MakeRequest(&device_manager));
+  mojo::Remote<UsbDeviceManager> device_manager;
+  device_manager_instance_->AddReceiver(
+      device_manager.BindNewPipeAndPassReceiver());
 
   {
     base::RunLoop loop;
@@ -183,8 +186,9 @@
 
   mock_usb_service_->AddDevice(device0);
 
-  UsbDeviceManagerPtr device_manager;
-  device_manager_instance_->AddBinding(mojo::MakeRequest(&device_manager));
+  mojo::Remote<UsbDeviceManager> device_manager;
+  device_manager_instance_->AddReceiver(
+      device_manager.BindNewPipeAndPassReceiver());
 
   MockDeviceManagerClient mock_client;
   device_manager->SetClient(mock_client.CreateInterfacePtrAndBind());
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc
index 3d370e6..9b8d5c2f 100644
--- a/services/network/cors/cors_url_loader_factory.cc
+++ b/services/network/cors/cors_url_loader_factory.cc
@@ -217,8 +217,9 @@
     case InitiatorLockCompatibility::kNoInitiator:
       // Requests from the renderer need to always specify an initiator.
       DCHECK_NE(process_id_, mojom::kBrowserProcessId);
-      // TODO(lukasza): Report this as a bad message.
-      break;
+      mojo::ReportBadMessage(
+          "CorsURLLoaderFactory: no initiator in a renderer request");
+      return false;
 
     case InitiatorLockCompatibility::kIncorrectLock:
       // Requests from the renderer need to always specify a correct initiator.
diff --git a/services/network/initiator_lock_compatibility.cc b/services/network/initiator_lock_compatibility.cc
index b5bb985..5c2d18be 100644
--- a/services/network/initiator_lock_compatibility.cc
+++ b/services/network/initiator_lock_compatibility.cc
@@ -38,22 +38,8 @@
     return InitiatorLockCompatibility::kNoLock;
   const url::Origin& lock = request_initiator_site_lock.value();
 
-  if (!request_initiator.has_value()) {
-    // SECURITY CHECK: Renderer processes should always provide a
-    // |request_initiator|.  Similarly, browser-side features acting on
-    // behalf of a renderer process (like AppCache), should always provide a
-    // |request_initiator|.
-    //
-    // Callers associated with features (e.g. Sec-Fetch-Site) that may handle
-    // browser-initiated requests (e.g. navigations and/or SafeBrowsing
-    // traffic) need to take extra care to avoid calling
-    // VerifyRequestInitiatorLock (and/or GetTrustworthyInitiator) with no
-    // |request_initiator|.  Such features should return early when handling
-    // request with no |request_initiator| but only when the request comes
-    // through a URLLoaderFactory associated with kBrowserProcessId.
-    CHECK(false);
+  if (!request_initiator.has_value())
     return InitiatorLockCompatibility::kNoInitiator;
-  }
   const url::Origin& initiator = request_initiator.value();
 
   // TODO(lukasza, nasko): Also consider equality of precursor origins (e.g. if
diff --git a/styleguide/web/es.md b/styleguide/web/es.md
index 75ad869..e1ef98a 100644
--- a/styleguide/web/es.md
+++ b/styleguide/web/es.md
@@ -563,6 +563,31 @@
 
 ---
 
+### Modules
+
+Support for exporting/importing values from/to modules without global
+namespace pollution.
+
+**Usage Example:**
+
+```js
+// lib/rect.js
+export function getArea() {...};
+export {width, height, unimportant};
+
+// app.js
+import {getArea, width, height} from './lib/rect.js';
+```
+
+**Documentation:** [link](https://developers.google.com/web/fundamentals/primers/modules)
+
+**Discussion Notes / Link to Thread:**
+Dynamic Import [link](https://v8.dev/features/dynamic-import) are not allowed
+yet, see separate entry in the [Features To Be Discussed](##es2015-support-in-chromium-features-to-be-discussed)
+section.
+
+---
+
 ## Banned Features
 
 The following features are banned for Chromium development.
@@ -730,12 +755,10 @@
 
 **Discussion Notes / Link to Thread:**
 
----
+### Dynamic Import
 
-### Modules
-
-Support for exporting/importing values from/to modules without global
-namespace pollution.
+Dynamic import() introduces a new function-like form of import that returns a
+promise for the module namespace object of the requested module.
 
 **Usage Example:**
 
@@ -745,10 +768,14 @@
 export {width, height, unimportant};
 
 // app.js
-import {getArea, width, height} from './lib/rect.js';
+if (calculateArea) {
+  import('./lib/rect.js').then(rect => {
+    rect.getArea(...);
+  });
+}
 ```
 
-**Documentation:** [link](https://developers.google.com/web/fundamentals/primers/modules)
+**Documentation:** [link](https://v8.dev/features/dynamic-import)
 
 **Discussion Notes / Link to Thread:**
 
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index af28265c..36c4dcec 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -608,6 +608,51 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
+            "android_webview_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "NRD90M",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
+        },
+        "test": "android_webview_unittests"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
             "system_webview_shell_layout_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -796,6 +841,54 @@
       },
       {
         "args": [
+          "--disable-field-trials",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.webview_instrumentation_tests_nougat.filter"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "webview_instrumentation_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "NRD90M",
+              "device_os_flavor": "google",
+              "device_os_type": "userdebug",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ],
+          "shards": 12
+        },
+        "test": "webview_instrumentation_test_apk"
+      },
+      {
+        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 8337d9b..2b4febd 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -132,6 +132,7 @@
   testonly = true
 
   data = [
+    "//testing/buildbot/filters/android.webview_instrumentation_tests_nougat.filter",
     "//testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter",
   ]
 }
diff --git a/testing/buildbot/filters/android.webview_instrumentation_tests_nougat.filter b/testing/buildbot/filters/android.webview_instrumentation_tests_nougat.filter
new file mode 100644
index 0000000..1fd2e54
--- /dev/null
+++ b/testing/buildbot/filters/android.webview_instrumentation_tests_nougat.filter
@@ -0,0 +1,2 @@
+-org.chromium.android_webview.test.AwNetworkConfigurationTest#testSHA1LocalAnchorsAllowed
+-org.chromium.android_webview.test.SslPreferencesTest#testSslErrorNotCalledForOkCert
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index a684c628..6d8c31d 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2091,6 +2091,12 @@
         # either passing or there is more capacity.
         'experiment_percentage': 0,
       },
+      'Android WebView N (dbg)': {
+        #TODO(crbug.com/998926): Remove test filters once bug is resolved.
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/android.webview_instrumentation_tests_nougat.filter',
+        ],
+      },
     },
   },
   'xr_browser_tests': {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index d5c8a5bc..5f18692 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -232,7 +232,7 @@
       },
       'Android WebView N (dbg)': {
         'test_suites': {
-          'gtest_tests': 'webview_bot_system_gtests',
+          'gtest_tests': 'webview_bot_all_gtests',
         },
         'swarming': {
           'dimension_sets': [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 4fd399a2..97d5464 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4419,6 +4419,21 @@
             ]
         }
     ],
+    "PlatformFontSkiaOnWindows": [
+        {
+            "platforms": [
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "PlatformFontSkiaOnWindows"
+                    ]
+                }
+            ]
+        }
+    ],
     "PolicyAtomicGroup": [
         {
             "platforms": [
diff --git a/third_party/apache-win32/OWNERS b/third_party/apache-win32/OWNERS
new file mode 100644
index 0000000..7ccb9fa
--- /dev/null
+++ b/third_party/apache-win32/OWNERS
@@ -0,0 +1,2 @@
+# COMPONENT: Blink>Infra
+# TEAM: blink-infra@chromium.org
diff --git a/third_party/blink/common/browser_interface_broker_proxy.cc b/third_party/blink/common/browser_interface_broker_proxy.cc
index ae552ba..9f258bc6c 100644
--- a/third_party/blink/common/browser_interface_broker_proxy.cc
+++ b/third_party/blink/common/browser_interface_broker_proxy.cc
@@ -37,10 +37,15 @@
   GetInterface(mojo::GenericPendingReceiver(name, std::move(pipe)));
 }
 
-void BrowserInterfaceBrokerProxy::SetBinderForTesting(
+bool BrowserInterfaceBrokerProxy::SetBinderForTesting(
     const std::string& name,
     base::RepeatingCallback<void(mojo::ScopedMessagePipeHandle)> binder) {
-  binder_map_for_testing_[name] = std::move(binder);
-}
+  if (!binder) {
+    binder_map_for_testing_.erase(name);
+    return true;
+  }
 
+  auto result = binder_map_for_testing_.emplace(name, std::move(binder));
+  return result.second;
+}
 }  // namespace blink
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 2511df4..14355f48 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -105,7 +105,7 @@
 #endif
 };
 
-// Start service workers on the IO thread.
+// Start service workers on a background thread.
 // https://crbug.com/692909
 const base::Feature kOffMainThreadServiceWorkerStartup{
     "OffMainThreadServiceWorkerStartup", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc b/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc
index 964ab6dd..234cfe4 100644
--- a/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc
+++ b/third_party/blink/common/font_unique_name_lookup/font_table_persistence.cc
@@ -14,8 +14,7 @@
 
 bool LoadFromFile(base::FilePath file_path,
                   base::MappedReadOnlyRegion* name_table_region) {
-  if (file_path.empty())
-    return false;
+  DCHECK(!file_path.empty());
 
   // Reset to empty to ensure IsValid() is false if reading fails.
   *name_table_region = base::MappedReadOnlyRegion();
@@ -75,11 +74,8 @@
 
 bool PersistToFile(const base::MappedReadOnlyRegion& name_table_region,
                    base::FilePath file_path) {
-  DCHECK(name_table_region.mapping.IsValid());
-  DCHECK(name_table_region.mapping.size());
-
-  if (file_path.empty())
-    return false;
+  DCHECK(name_table_region.mapping.IsValid() &&
+         name_table_region.mapping.size() && !file_path.empty());
 
   base::File table_cache_file(file_path, base::File::FLAG_CREATE_ALWAYS |
                                              base::File::Flags::FLAG_WRITE);
diff --git a/third_party/blink/public/common/browser_interface_broker_proxy.h b/third_party/blink/public/common/browser_interface_broker_proxy.h
index cd9b457..1c59e570 100644
--- a/third_party/blink/public/common/browser_interface_broker_proxy.h
+++ b/third_party/blink/public/common/browser_interface_broker_proxy.h
@@ -20,13 +20,24 @@
   BrowserInterfaceBrokerProxy() = default;
   void Bind(mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>);
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> Reset();
+
+  // Asks the browser to bind the given receiver. If a non-null testing override
+  // was set by |SetBinderForTesting()|, the request will be intercepted by that
+  // binder instead of going to the browser.
   void GetInterface(mojo::GenericPendingReceiver) const;
 
   // TODO(crbug.com/718652): Add a presubmit check for C++ call sites
   void GetInterface(const std::string& name,
                     mojo::ScopedMessagePipeHandle pipe) const;
 
-  void SetBinderForTesting(
+  // Overrides how the named interface is bound, rather than sending its
+  // receivers to the browser. If |binder| is null, any registered override
+  // for the interface is cancelled.
+  //
+  // Returns |true| if the new binder was successfully set, or |false| if the
+  // binder was non-null and an existing binder was already registered for the
+  // named interface.
+  bool SetBinderForTesting(
       const std::string& name,
       base::RepeatingCallback<void(mojo::ScopedMessagePipeHandle)> binder);
 
diff --git a/third_party/blink/renderer/controller/oom_intervention_impl.cc b/third_party/blink/renderer/controller/oom_intervention_impl.cc
index 5bf294cd..5cffaf7 100644
--- a/third_party/blink/renderer/controller/oom_intervention_impl.cc
+++ b/third_party/blink/renderer/controller/oom_intervention_impl.cc
@@ -4,12 +4,14 @@
 
 #include "third_party/blink/renderer/controller/oom_intervention_impl.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/debug/crash_logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.h"
 #include "third_party/blink/renderer/controller/crash_memory_metrics_reporter_impl.h"
@@ -61,9 +63,10 @@
 }  // namespace
 
 // static
-void OomInterventionImpl::Create(mojom::blink::OomInterventionRequest request) {
-  mojo::MakeStrongBinding(std::make_unique<OomInterventionImpl>(),
-                          std::move(request));
+void OomInterventionImpl::Create(
+    mojo::PendingReceiver<mojom::blink::OomIntervention> receiver) {
+  mojo::MakeSelfOwnedReceiver(std::make_unique<OomInterventionImpl>(),
+                              std::move(receiver));
 }
 
 OomInterventionImpl::OomInterventionImpl()
diff --git a/third_party/blink/renderer/controller/oom_intervention_impl.h b/third_party/blink/renderer/controller/oom_intervention_impl.h
index 43a89cd8..a35e642c 100644
--- a/third_party/blink/renderer/controller/oom_intervention_impl.h
+++ b/third_party/blink/renderer/controller/oom_intervention_impl.h
@@ -5,6 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CONTROLLER_OOM_INTERVENTION_IMPL_H_
 #define THIRD_PARTY_BLINK_RENDERER_CONTROLLER_OOM_INTERVENTION_IMPL_H_
 
+#include <memory>
+
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/oom_intervention/oom_intervention_types.h"
 #include "third_party/blink/public/mojom/oom_intervention/oom_intervention.mojom-blink.h"
@@ -23,7 +27,8 @@
     : public mojom::blink::OomIntervention,
       public MemoryUsageMonitor::Observer {
  public:
-  static void Create(mojom::blink::OomInterventionRequest);
+  static void Create(
+      mojo::PendingReceiver<mojom::blink::OomIntervention> receiver);
 
   OomInterventionImpl();
   ~OomInterventionImpl() override;
diff --git a/third_party/blink/renderer/controller/oom_intervention_impl_test.cc b/third_party/blink/renderer/controller/oom_intervention_impl_test.cc
index cf22ae4e..20cd097 100644
--- a/third_party/blink/renderer/controller/oom_intervention_impl_test.cc
+++ b/third_party/blink/renderer/controller/oom_intervention_impl_test.cc
@@ -6,7 +6,11 @@
 
 #include <unistd.h>
 
+#include <utility>
+
 #include "base/files/file_util.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -35,8 +39,9 @@
 
 class MockOomInterventionHost : public mojom::blink::OomInterventionHost {
  public:
-  MockOomInterventionHost(mojom::blink::OomInterventionHostRequest request)
-      : receiver_(this, std::move(request)) {}
+  MockOomInterventionHost(
+      mojo::PendingReceiver<mojom::blink::OomInterventionHost> receiver)
+      : receiver_(this, std::move(receiver)) {}
   ~MockOomInterventionHost() override = default;
 
   void OnHighMemoryUsage() override {}
diff --git a/third_party/blink/renderer/core/mojo/test/mojo_interface_interceptor.cc b/third_party/blink/renderer/core/mojo/test/mojo_interface_interceptor.cc
index f3800382..1aca30b6 100644
--- a/third_party/blink/renderer/core/mojo/test/mojo_interface_interceptor.cc
+++ b/third_party/blink/renderer/core/mojo/test/mojo_interface_interceptor.cc
@@ -85,13 +85,18 @@
 
     BrowserInterfaceBrokerProxy* proxy =
         context->GetBrowserInterfaceBrokerProxy();
-    CHECK(proxy);
+    DCHECK(proxy);
 
     started_ = true;
-    proxy->SetBinderForTesting(
-        interface_name,
-        WTF::BindRepeating(&MojoInterfaceInterceptor::OnInterfaceRequest,
-                           WrapWeakPersistent(this)));
+    if (!proxy->SetBinderForTesting(
+            interface_name,
+            WTF::BindRepeating(&MojoInterfaceInterceptor::OnInterfaceRequest,
+                               WrapWeakPersistent(this)))) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kInvalidModificationError,
+          "Interface " + interface_name_ +
+              " is already intercepted by another MojoInterfaceInterceptor.");
+    }
     return;
   }
 
@@ -113,7 +118,7 @@
 }
 
 void MojoInterfaceInterceptor::stop() {
-  if (!started_ || use_browser_interface_broker_)
+  if (!started_)
     return;
 
   started_ = false;
@@ -125,6 +130,19 @@
     return;
   }
 
+  if (use_browser_interface_broker_) {
+    ExecutionContext* context = GetExecutionContext();
+    DCHECK(context);
+
+    BrowserInterfaceBrokerProxy* proxy =
+        context->GetBrowserInterfaceBrokerProxy();
+    DCHECK(proxy);
+
+    proxy->SetBinderForTesting(interface_name, {});
+    return;
+  }
+
+  // TODO(crbug.com/994843): remove when no longer used.
   // GetInterfaceProvider() is guaranteed not to return nullptr because this
   // method is called when the context is destroyed.
   service_manager::InterfaceProvider::TestApi test_api(GetInterfaceProvider());
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs
index 93878ac9..907e2cd6 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs
+++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs
@@ -76,6 +76,10 @@
     }
   }
 
+  formResetCallback() {
+    this.on = this.defaultOn;
+  }
+
   #initializeDOM = () => {
     const factory = this.ownerDocument;
     const root = this.attachShadow({mode: 'closed'});
@@ -170,3 +174,4 @@
 delete StdSwitchElement.observedAttributes;
 delete StdSwitchElement.prototype.attributeChangedCallback;
 delete StdSwitchElement.prototype.connectedCallback;
+delete StdSwitchElement.prototype.formResetCallback;
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index c2a4abe..08e1d7e 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -35,11 +35,13 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
+#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/string_or_performance_measure_options.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/document_timing.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -795,6 +797,14 @@
   DCHECK(RuntimeEnabledFeatures::ExperimentalJSProfilerEnabled(
       ExecutionContext::From(script_state)));
 
+  // The JS Self-Profiling origin trial currently requires site isolation.
+  if (!Platform::Current()->IsLockedToSite()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotSupportedError,
+        "performance.profile() requires site-per-process (crbug.com/956688)");
+    return ScriptPromise();
+  }
+
   auto* profiler_group = ProfilerGroup::From(script_state->GetIsolate());
   DCHECK(profiler_group);
 
diff --git a/third_party/blink/renderer/core/timing/profiler_group.cc b/third_party/blink/renderer/core/timing/profiler_group.cc
index c3a4ea5..5f02a0a 100644
--- a/third_party/blink/renderer/core/timing/profiler_group.cc
+++ b/third_party/blink/renderer/core/timing/profiler_group.cc
@@ -55,13 +55,14 @@
       cpu_profiler_(nullptr),
       next_profiler_id_(0),
       num_active_profilers_(0) {
-  DCHECK(RuntimeEnabledFeatures::ExperimentalJSProfilerEnabled());
 }
 
 Profiler* ProfilerGroup::CreateProfiler(ScriptState* script_state,
                                         const ProfilerInitOptions& init_options,
                                         base::TimeTicks time_origin,
                                         ExceptionState& exception_state) {
+  DCHECK(RuntimeEnabledFeatures::ExperimentalJSProfilerEnabled(
+      ExecutionContext::From(script_state)));
   DCHECK_EQ(script_state->GetIsolate(), isolate_);
   DCHECK(init_options.hasSampleInterval());
 
diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc
index 391a9d1..06b529f 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread.cc
@@ -165,7 +165,10 @@
       CrossThreadBindOnce(&WorkerThread::InitializeSchedulerOnWorkerThread,
                           CrossThreadUnretained(this),
                           CrossThreadUnretained(&waitable_event)));
-  waitable_event.Wait();
+  {
+    base::ScopedAllowBaseSyncPrimitives allow_wait;
+    waitable_event.Wait();
+  }
 
   inspector_task_runner_ =
       InspectorTaskRunner::Create(GetTaskRunner(TaskType::kInternalInspector));
@@ -401,6 +404,7 @@
 
 void WorkerThread::WaitForShutdownForTesting() {
   DCHECK_CALLED_ON_VALID_THREAD(parent_thread_checker_);
+  base::ScopedAllowBaseSyncPrimitives allow_wait;
   shutdown_event_->Wait();
 }
 
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.cc b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.cc
index 3c52a2f..7a8961a 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.cc
@@ -196,7 +196,7 @@
 #if defined(OS_ANDROID)
   DCHECK(!monitor_.is_bound());
   Platform::Current()->GetBrowserInterfaceBrokerProxy()->GetInterface(
-      mojo::MakeRequest(&monitor_));
+      monitor_.BindNewPipeAndPassReceiver());
   monitor_->IsAutoRotateEnabledByUser(WTF::Bind(
       &MediaControlsOrientationLockDelegate::GotIsAutoRotateEnabledByUser,
       WrapPersistent(this)));
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.h b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.h
index 1e3f18b..ea7b238 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.h
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_MEDIA_CONTROLS_ORIENTATION_LOCK_DELEGATE_H_
 
 #include "base/optional.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/device/public/mojom/screen_orientation.mojom-blink.h"
 #include "third_party/blink/public/common/screen_orientation/web_screen_orientation_lock_type.h"
 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
@@ -133,7 +134,7 @@
 
   TaskHandle lock_to_any_task_;
 
-  device::mojom::blink::ScreenOrientationListenerPtr monitor_;
+  mojo::Remote<device::mojom::blink::ScreenOrientationListener> monitor_;
 
   base::Optional<bool> is_auto_rotate_enabled_by_user_override_for_testing_;
 
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc
index d708992..4359bbc 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_bottom_container_element.h"
 
+#include "third_party/blink/renderer/core/css/properties/css_property.h"
+#include "third_party/blink/renderer/core/css_value_keywords.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
 #include "third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_time_display_element.h"
 #include "third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_timeline_element.h"
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_element.cc b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_element.cc
index 335a60d..063c303 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_element.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/media_controls/touchless/elements/media_controls_touchless_element.h"
 
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_token_list.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
 #include "third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h"
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
index e43d651..7adcbc6 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.cc
@@ -9,6 +9,8 @@
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_screen_info.h"
+#include "third_party/blink/renderer/core/css/properties/css_property.h"
+#include "third_party/blink/renderer/core/css_value_keywords.h"
 #include "third_party/blink/renderer/core/dom/dom_token_list.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
@@ -242,10 +244,10 @@
 void MediaControlsTouchlessImpl::EnsureMediaControlsMenuHost() {
   if (!media_controls_host_) {
     GetDocument().GetFrame()->GetInterfaceProvider().GetInterface(
-        mojo::MakeRequest(&media_controls_host_,
-                          GetExecutionContext()->GetTaskRunner(
-                              blink::TaskType::kMediaElementEvent)));
-    media_controls_host_.set_connection_error_handler(WTF::Bind(
+        media_controls_host_.BindNewPipeAndPassReceiver(
+            GetExecutionContext()->GetTaskRunner(
+                blink::TaskType::kMediaElementEvent)));
+    media_controls_host_.set_disconnect_handler(WTF::Bind(
         &MediaControlsTouchlessImpl::OnMediaControlsMenuHostConnectionError,
         WrapWeakPersistent(this)));
   }
@@ -528,8 +530,8 @@
 }
 
 void MediaControlsTouchlessImpl::SetMediaControlsMenuHostForTesting(
-    mojom::blink::MediaControlsMenuHostPtr menu_host) {
-  media_controls_host_ = std::move(menu_host);
+    mojo::PendingRemote<mojom::blink::MediaControlsMenuHost> menu_host) {
+  media_controls_host_.Bind(std::move(menu_host));
 }
 
 void MediaControlsTouchlessImpl::MenuHostFlushForTesting() {
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h
index 6601987..21d32a91 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_TOUCHLESS_MEDIA_CONTROLS_TOUCHLESS_IMPL_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_TOUCHLESS_MEDIA_CONTROLS_TOUCHLESS_IMPL_H_
 
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/screen_orientation/web_screen_orientation_type.h"
 #include "third_party/blink/public/mojom/media_controls/touchless/media_controls.mojom-blink.h"
 #include "third_party/blink/renderer/core/html/html_div_element.h"
@@ -72,7 +73,7 @@
 
   // Test functions
   void SetMediaControlsMenuHostForTesting(
-      mojom::blink::MediaControlsMenuHostPtr);
+      mojo::PendingRemote<mojom::blink::MediaControlsMenuHost>);
   void MenuHostFlushForTesting();
 
   void Trace(blink::Visitor*) override;
@@ -119,7 +120,7 @@
   Member<MediaControlsTextTrackManager> text_track_manager_;
   Member<MediaControlsOrientationLockDelegate> orientation_lock_delegate_;
 
-  mojom::blink::MediaControlsMenuHostPtr media_controls_host_;
+  mojo::Remote<mojom::blink::MediaControlsMenuHost> media_controls_host_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaControlsTouchlessImpl);
 };
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
index c33d923e..b18531c66 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_impl_test.cc
@@ -111,7 +111,7 @@
     test_media_controls_host_ = std::make_unique<TestMediaControlsMenuHost>();
 
     media_controls_->SetMediaControlsMenuHostForTesting(
-        test_media_controls_host_->CreateMediaControlsMenuHostPtr());
+        test_media_controls_host_->CreateMediaControlsMenuHostRemote());
 
     // Scripts are disabled by default which forces controls to be on.
     GetFrame().GetSettings()->SetScriptEnabled(true);
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_media_event_listener.cc b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_media_event_listener.cc
index 17fc5f1..9077df3 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_media_event_listener.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_media_event_listener.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/media_controls/touchless/media_controls_touchless_media_event_listener.h"
 
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/test_media_controls_menu_host.cc b/third_party/blink/renderer/modules/media_controls/touchless/test_media_controls_menu_host.cc
index d878077..25b68b98 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/test_media_controls_menu_host.cc
+++ b/third_party/blink/renderer/modules/media_controls/touchless/test_media_controls_menu_host.cc
@@ -6,16 +6,14 @@
 
 namespace blink {
 
-mojom::blink::MediaControlsMenuHostPtr
-TestMediaControlsMenuHost::CreateMediaControlsMenuHostPtr() {
-  mojom::blink::MediaControlsMenuHostPtr ptr;
-  binding_.Bind(mojo::MakeRequest(&ptr));
-  return ptr;
+mojo::PendingRemote<mojom::blink::MediaControlsMenuHost>
+TestMediaControlsMenuHost::CreateMediaControlsMenuHostRemote() {
+  return receiver_.BindNewPipeAndPassRemote();
 }
 
 void TestMediaControlsMenuHost::ShowMediaMenu(
     const WTF::Vector<mojom::MenuItem>& menu_items,
-    mojom::blink::VideoStatePtr video_state,
+    mojo::PendingRemote<mojom::blink::VideoState> video_state,
     base::Optional<WTF::Vector<mojom::blink::TextTrackMetadataPtr>> text_tracks,
     ShowMediaMenuCallback callback) {
   arg_list_.menu_items = WTF::Vector<mojom::MenuItem>(menu_items);
diff --git a/third_party/blink/renderer/modules/media_controls/touchless/test_media_controls_menu_host.h b/third_party/blink/renderer/modules/media_controls/touchless/test_media_controls_menu_host.h
index 4378945..018022f 100644
--- a/third_party/blink/renderer/modules/media_controls/touchless/test_media_controls_menu_host.h
+++ b/third_party/blink/renderer/modules/media_controls/touchless/test_media_controls_menu_host.h
@@ -5,7 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_TOUCHLESS_TEST_MEDIA_CONTROLS_MENU_HOST_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_TOUCHLESS_TEST_MEDIA_CONTROLS_MENU_HOST_H_
 
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/media_controls/touchless/media_controls.mojom-blink.h"
 
 namespace blink {
@@ -18,10 +19,11 @@
 
 class TestMediaControlsMenuHost : public mojom::blink::MediaControlsMenuHost {
  public:
-  mojom::blink::MediaControlsMenuHostPtr CreateMediaControlsMenuHostPtr();
+  mojo::PendingRemote<mojom::blink::MediaControlsMenuHost>
+  CreateMediaControlsMenuHostRemote();
   void ShowMediaMenu(
       const WTF::Vector<mojom::MenuItem>& menu_items,
-      mojom::blink::VideoStatePtr video_state,
+      mojo::PendingRemote<mojom::blink::VideoState> video_state,
       base::Optional<WTF::Vector<mojom::blink::TextTrackMetadataPtr>>
           text_tracks,
       ShowMediaMenuCallback callback) override;
@@ -30,9 +32,9 @@
   void SetMenuResponse(mojom::blink::MenuItem menu_item, int track_index);
 
  private:
-  mojo::Binding<mojom::blink::MediaControlsMenuHost> binding_{this};
+  mojo::Receiver<mojom::blink::MediaControlsMenuHost> receiver_{this};
   TestMenuHostArgList arg_list_;
-  mojom::blink::MenuResponsePtr response_;
+  mojo::Remote<mojom::blink::MenuResponse> response_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.cc b/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.cc
index 1c1e9e04..9c37226 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.cc
@@ -42,8 +42,7 @@
   MediaStreamDevices video_devices;
 };
 
-MediaStreamDeviceObserver::MediaStreamDeviceObserver(WebLocalFrame* frame)
-    : binding_(this) {
+MediaStreamDeviceObserver::MediaStreamDeviceObserver(WebLocalFrame* frame) {
   // There is no frame on unit tests.
   if (!frame)
     return;
@@ -135,8 +134,8 @@
 }
 
 void MediaStreamDeviceObserver::BindMediaStreamDeviceObserverRequest(
-    mojom::blink::MediaStreamDeviceObserverRequest request) {
-  binding_.Bind(std::move(request));
+    mojo::PendingReceiver<mojom::blink::MediaStreamDeviceObserver> receiver) {
+  receiver_.Bind(std::move(receiver));
 }
 
 void MediaStreamDeviceObserver::AddStream(
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.h b/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.h
index f66dd1b..b79f3e4 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.h
@@ -15,7 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-blink.h"
 #include "third_party/blink/public/web/modules/mediastream/web_media_stream_device_observer.h"
@@ -76,9 +76,9 @@
                        const MediaStreamDevice& new_device) override;
 
   void BindMediaStreamDeviceObserverRequest(
-      mojom::blink::MediaStreamDeviceObserverRequest request);
+      mojo::PendingReceiver<mojom::blink::MediaStreamDeviceObserver> receiver);
 
-  mojo::Binding<mojom::blink::MediaStreamDeviceObserver> binding_;
+  mojo::Receiver<mojom::blink::MediaStreamDeviceObserver> receiver_{this};
 
   // Used for DCHECKs so methods calls won't execute in the wrong thread.
   THREAD_CHECKER(thread_checker_);
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc
index 4784eb3..feefbfa 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context.cc
@@ -175,8 +175,7 @@
   RegisterExtension<OESVertexArrayObject>(oes_vertex_array_object_);
   RegisterExtension<WebGLColorBufferFloat>(webgl_color_buffer_float_);
   RegisterExtension<WebGLCompressedTextureASTC>(webgl_compressed_texture_astc_);
-  RegisterExtension<WebGLCompressedTextureETC>(webgl_compressed_texture_etc_,
-                                               kDraftExtension);
+  RegisterExtension<WebGLCompressedTextureETC>(webgl_compressed_texture_etc_);
   RegisterExtension<WebGLCompressedTextureETC1>(webgl_compressed_texture_etc1_);
   RegisterExtension<WebGLCompressedTexturePVRTC>(
       webgl_compressed_texture_pvrtc_, kApprovedExtension, kBothPrefixes);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 271cb42d..382c32e5 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -587,6 +587,7 @@
     // https://github.com/WICG/js-self-profiling
     {
       name: "ExperimentalJSProfiler",
+      origin_trial_feature_name: "ExperimentalJSProfiler",
       status: "experimental"
     },
     // Enables a set of features intended to help improve web developer
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread.cc
index 7cf886d..f55ef03 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread.cc
@@ -52,6 +52,7 @@
         const_cast<scheduler::WorkerThread*>(this));
   }
   thread_->Quit();
+  base::ScopedAllowBaseSyncPrimitives allow_wait;
   thread_->Join();
 }
 
@@ -118,6 +119,7 @@
   internal_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal,
                                 base::Unretained(&initialized)));
+  base::ScopedAllowBaseSyncPrimitives allow_wait;
   initialized.Wait();
 }
 
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 1bb5c47..9a3e6bc 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -570,6 +570,12 @@
         ],
     },
     {
+        'paths': ['third_party/blink/renderer/core/workers/worker_thread.cc'],
+        'allowed': [
+            'base::ScopedAllowBaseSyncPrimitives',
+        ],
+    },
+    {
         'paths': [
             'third_party/blink/renderer/modules/device_orientation/',
             'third_party/blink/renderer/modules/gamepad/',
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 25abd7e2a..49573627 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -581,6 +581,11 @@
     "args": ["--enable-features=OffMainThreadServiceWorkerStartup"]
   },
   {
+    "prefix": "omt-service-worker-startup",
+    "base": "external/wpt/shape-detection",
+    "args": ["--enable-features=OffMainThreadServiceWorkerStartup"]
+  },
+  {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/content-security-policy/inside-worker",
     "args": ["--enable-features=PlzDedicatedWorker"]
diff --git a/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js b/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js
index b53b3b0..292fe88 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testdriver-actions.js
@@ -3,10 +3,10 @@
 
   /**
    * Builder for creating a sequence of actions
-   * The default tick duration is set to 32ms, which is 2 frame time based on
+   * The default tick duration is set to 16ms, which is one frame time based on
    * 60Hz display.
    */
-  function Actions(defaultTickDuration=32) {
+  function Actions(defaultTickDuration=16) {
     this.sourceTypes = new Map([["key", KeySource],
                                 ["pointer", PointerSource],
                                 ["none", GeneralSource]]);
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-take-body-invalid-id-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-take-body-invalid-id-expected.txt
new file mode 100644
index 0000000..09de4ca
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-take-body-invalid-id-expected.txt
@@ -0,0 +1,3 @@
+Tests that Fetch.takeResponseBodyAsStream does not trigger a DCHECK when dispatching an error.
+Error from Fetch.takeResponseBodyAsStream: {"code":-32602,"message":"Invalid InterceptionId."}
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-take-body-invalid-id.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-take-body-invalid-id.js
new file mode 100644
index 0000000..1391fb0
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-take-body-invalid-id.js
@@ -0,0 +1,10 @@
+(async function(testRunner) {
+    var {page, session, dp} = await testRunner.startBlank(
+        `Tests that Fetch.takeResponseBodyAsStream does not trigger a DCHECK when dispatching an error.`);
+
+    await dp.Fetch.enable();
+    const error = (await dp.Fetch.takeResponseBodyAsStream({requestId: "I'm not there"})).error;
+    testRunner.log(`Error from Fetch.takeResponseBodyAsStream: ${JSON.stringify(error)}`);
+
+    testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/jsselfprofiling-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/jsselfprofiling-origin-trial-interfaces.html
new file mode 100644
index 0000000..8a5e51d6
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/jsselfprofiling-origin-trial-interfaces.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<!-- Generate this token with the command:
+generate_token.py http://127.0.0.1:8000 ExperimentalJSProfiler --expire-timestamp=2000000000
+-- -->
+
+<meta http-equiv="origin-trial" content="AjfjLAG2ob7vRGForWYa9wTsLhhxfIR4iYHWmv4NRkUC/64+gtKIdKbDE/AUb4y4W/RfCMx0ULvFrYQbuDk1CwcAAABeeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiRXhwZXJpbWVudGFsSlNQcm9maWxlciIsICJleHBpcnkiOiAyMDAwMDAwMDAwfQ=="/>
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/origin-trials-helper.js"></script>
+
+<script>
+test(t => {
+  OriginTrialsHelper.check_properties_exist(this,
+      {
+        'Performance': ['profile'],
+      },
+  );
+}, "JS Self-Profiling API exposed in Origin-Trial enabled document.");
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/std-switch/tentative/form-reset.html b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/form-reset.html
new file mode 100644
index 0000000..2e1e3e0
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/form-reset.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="container"></div>
+<script type=module>
+import 'std:elements/switch';
+
+const $ = document.querySelector.bind(document);
+
+function resetAndAssertOnIs(expected) {
+  $('form').reset();
+  assert_equals($('std-switch').on, expected);
+}
+
+test(() => {
+  $('#container').innerHTML = `
+    <form><std-switch></std-switch></form>`;
+  $('std-switch').on = true;
+  resetAndAssertOnIs(false);
+}, 'no "on" and no "defaulton"');
+
+test(() => {
+  $('#container').innerHTML = `
+    <form><std-switch defaulton></std-switch></form>`;
+  resetAndAssertOnIs(true);
+}, 'no "on" and "defaulton"');
+
+test(() => {
+  $('#container').innerHTML = `
+    <form><std-switch on></std-switch></form>`;
+  resetAndAssertOnIs(false);
+}, '"on" and no "defaulton"');
+
+test(() => {
+  $('#container').innerHTML = `
+    <form><std-switch on defaulton></std-switch></form>`;
+  $('std-switch').on = false;
+  resetAndAssertOnIs(true);
+}, '"on" and "defaulton"');
+</script>
+</body>
diff --git a/third_party/bspatch/OWNERS b/third_party/bspatch/OWNERS
new file mode 100644
index 0000000..4fa623e
--- /dev/null
+++ b/third_party/bspatch/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: Internal>Installer
diff --git a/third_party/colorama/OWNERS b/third_party/colorama/OWNERS
new file mode 100644
index 0000000..32e3eca0
--- /dev/null
+++ b/third_party/colorama/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: Tools
diff --git a/third_party/feed/BUILD.gn b/third_party/feed/BUILD.gn
index 3757358..65829c2 100644
--- a/third_party/feed/BUILD.gn
+++ b/third_party/feed/BUILD.gn
@@ -105,9 +105,6 @@
     "//third_party/android_deps:javax_inject_javax_inject_java",
     "//third_party/jsr-305:jsr_305_javalib",
   ]
-  if (!is_java_debug) {
-    proguard_configs = [ "src/src/main/java/com/google/android/libraries/feed/buildtools/proguard/r8_release.flags" ]
-  }
 }
 
 android_library("feed_conformance_test_lib_android_java") {
diff --git a/third_party/google_appengine_cloudstorage/OWNERS b/third_party/google_appengine_cloudstorage/OWNERS
index 0ec9365..34f8a58 100644
--- a/third_party/google_appengine_cloudstorage/OWNERS
+++ b/third_party/google_appengine_cloudstorage/OWNERS
@@ -1,3 +1,4 @@
+file://chrome/common/extensions/docs/server2/OWNERS
 jyasskin@chromium.org
-mangini@chromium.org
 rockot@google.com
+# COMPONENT: Platform>Extensions>Documentation
diff --git a/third_party/lcov/OWNERS b/third_party/lcov/OWNERS
new file mode 100644
index 0000000..f136fc1
--- /dev/null
+++ b/third_party/lcov/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: Internals
diff --git a/third_party/libXNVCtrl/OWNERS b/third_party/libXNVCtrl/OWNERS
new file mode 100644
index 0000000..8498c21
--- /dev/null
+++ b/third_party/libXNVCtrl/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: Internals>GPU
diff --git a/third_party/libjpeg/OWNERS b/third_party/libjpeg/OWNERS
new file mode 100644
index 0000000..d9ab6ba8
--- /dev/null
+++ b/third_party/libjpeg/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: Internals>Images>Codecs
diff --git a/third_party/pexpect/OWNERS b/third_party/pexpect/OWNERS
index ad5af4f..d783ece 100644
--- a/third_party/pexpect/OWNERS
+++ b/third_party/pexpect/OWNERS
@@ -1,2 +1,3 @@
 nduca@chromium.org
 tonyg@chromium.org
+# COMPONENT: Tools
diff --git a/third_party/ply/OWNERS b/third_party/ply/OWNERS
new file mode 100644
index 0000000..32e3eca0
--- /dev/null
+++ b/third_party/ply/OWNERS
@@ -0,0 +1 @@
+# COMPONENT: Tools
diff --git a/tools/lldb/lldbinit.py b/tools/lldb/lldbinit.py
new file mode 100644
index 0000000..8617b5c
--- /dev/null
+++ b/tools/lldb/lldbinit.py
@@ -0,0 +1,16 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# The GN arg `strip_absolute_paths_from_debug_symbols = 1` uses relative paths
+# for debug symbols. This confuses lldb. We explicitly set the source-map to
+# point at the root directory of the chromium checkout.
+import os
+import lldb
+this_dir = os.path.dirname(os.path.abspath(__file__))
+source_dir = os.path.join(os.path.join(this_dir, os.pardir), os.pardir)
+
+lldb.debugger.HandleCommand(
+    'settings set target.source-map ../.. ' + source_dir)
+lldb.debugger.HandleCommand(
+    'settings set target.env-vars CHROMIUM_LLDBINIT_SOURCED=1')
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 34ba7322..e86d3ac 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -1464,12 +1464,14 @@
 </action>
 
 <action name="Android.ChromeHome.Closed">
+  <obsolete>Unused as of 7/2019</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>User closed the Chrome Home bottom sheet.</description>
 </action>
 
 <action name="Android.ChromeHome.ClosedByBackPress">
+  <obsolete>Unused as of 7/2019</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1478,6 +1480,7 @@
 </action>
 
 <action name="Android.ChromeHome.ClosedByNavigation">
+  <obsolete>Unused as of 7/2019</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1497,6 +1500,7 @@
 </action>
 
 <action name="Android.ChromeHome.ClosedBySwipe">
+  <obsolete>Unused as of 7/2019</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1505,6 +1509,7 @@
 </action>
 
 <action name="Android.ChromeHome.ClosedByTapScrim">
+  <obsolete>Unused as of 7/2019</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1513,6 +1518,7 @@
 </action>
 
 <action name="Android.ChromeHome.FullState">
+  <obsolete>Unused as of 7/2019</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1521,6 +1527,7 @@
 </action>
 
 <action name="Android.ChromeHome.HalfState">
+  <obsolete>Unused as of 7/2019</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1529,6 +1536,7 @@
 </action>
 
 <action name="Android.ChromeHome.IPHMenuItemClicked">
+  <obsolete>Unused as of 7/2019</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1538,6 +1546,7 @@
 </action>
 
 <action name="Android.ChromeHome.NativeNTPShown">
+  <obsolete>Unused as of 7/2019</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1602,6 +1611,7 @@
 </action>
 
 <action name="Android.ChromeHome.OptOutSnackbarClicked">
+  <obsolete>Unused as of 7/2019</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -21752,6 +21762,15 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="TerminalSystemAppMenuButtonButton_Clicked">
+  <owner>alancutter@chromium.org</owner>
+  <owner>calamity@chromium.org</owner>
+  <owner>joelhockey@chromium.org</owner>
+  <description>
+    User clicked on the terminal system app menu button. Chrome OS only.
+  </description>
+</action>
+
 <action name="Terminate_ProcessMismatch_CreateNewWidget">
   <obsolete>
     Replaced in 1/2018 with bad_message::WCI_NEW_WIDGET_PROCESS_MISMATCH.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 026e265fe..fa9d4fe6 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -1375,6 +1375,10 @@
   <int value="6" label="SNACK_BAR"/>
   <int value="7" label="AUTO_OPEN"/>
   <int value="8" label="DOWNLOAD_PROGRESS_INFO_BAR"/>
+  <int value="9" label="MENU"/>
+  <int value="10" label="DINO_PAGE_OFFLINE_CONTENT"/>
+  <int value="11" label="OFFLINE_INDICATOR"/>
+  <int value="12" label="OFFLINE_CONTENT_NOTIFICATION"/>
 </enum>
 
 <enum name="AndroidEvictionReason">
@@ -35182,6 +35186,7 @@
   <int value="-1077752943" label="enable-password-generation"/>
   <int value="-1075156797" label="enable-brotli"/>
   <int value="-1075089382" label="enable-physical-web"/>
+  <int value="-1074257709" label="ScalableAppList:enabled"/>
   <int value="-1074107607" label="data-reduction-proxy-experiment"/>
   <int value="-1073479583" label="ShowArcFilesApp:disabled"/>
   <int value="-1069453905" label="CCTModuleUseIntentExtras:disabled"/>
@@ -35854,6 +35859,7 @@
   <int value="-137303226" label="enable-chromevox-developer-option"/>
   <int value="-135584721"
       label="OmniboxUIExperimentBoldUserTextOnSearchSuggestions:disabled"/>
+  <int value="-135584175" label="EnableAggregatedMlAppRanking:enabled"/>
   <int value="-135223364" label="AutofillShowTypePredictions:disabled"/>
   <int value="-133098377" label="SyncPseudoUSSSearchEngines:disabled"/>
   <int value="-131673218" label="FileHandlingAPI:disabled"/>
@@ -36569,6 +36575,7 @@
   <int value="868009556" label="AutofillUpstream:enabled"/>
   <int value="869531646" label="enable-session-crashed-bubble"/>
   <int value="870664435" label="DownloadProgressInfoBar:disabled"/>
+  <int value="871435095" label="EnableAggregatedMlAppRanking:disabled"/>
   <int value="871713352" label="ImprovedLanguageSettings:enabled"/>
   <int value="876879670" label="OfflinePagesInDownloadHomeOpenInCct:enabled"/>
   <int value="878773995" label="ChromeHomeBottomNavLabels:disabled"/>
@@ -37371,6 +37378,7 @@
   <int value="1972232935" label="DisplayMoveWindowAccels:enabled"/>
   <int value="1972720114" label="WebPaymentsJustInTimePaymentApp:enabled"/>
   <int value="1974565950" label="enable-experimental-accessibility-labels"/>
+  <int value="1975657253" label="ScalableAppList:disabled"/>
   <int value="1976644015" label="enable-forbid-sync-xhr-in-page-dismissal"/>
   <int value="1979222611" label="XRSandbox:disabled"/>
   <int value="1980011075" label="debug-packed-apps"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 87781ec..6976415c6 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -1754,12 +1754,18 @@
 </histogram>
 
 <histogram name="Android.ChromeHome.DurationOpen" units="ms">
+  <obsolete>
+    Unused as of 7/2019
+  </obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>The duration the Chrome Home bottom sheet was open.</summary>
 </histogram>
 
 <histogram name="Android.ChromeHome.OpenReason" enum="ChromeHomeOpenReason">
+  <obsolete>
+    Unused as of 7/2019
+  </obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>The reason the bottom sheet was opened.</summary>
@@ -1919,6 +1925,9 @@
 
 <histogram name="Android.ChromeHome.TimeBetweenCloseAndNextOpen" units="ms"
     expires_after="M77">
+  <obsolete>
+    Unused as of 7/2019
+  </obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -1928,6 +1937,9 @@
 </histogram>
 
 <histogram name="Android.ChromeHome.TimeToFirstOpen" units="ms">
+  <obsolete>
+    Unused as of 7/2019
+  </obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -1938,6 +1950,9 @@
 
 <histogram name="Android.ChromeHome.UserPreference.Enabled"
     enum="BooleanEnabled">
+  <obsolete>
+    Unused as of 7/2019
+  </obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <summary>
@@ -2321,6 +2336,16 @@
   <summary>Records how users open audio download files on Android.</summary>
 </histogram>
 
+<histogram name="Android.DownloadManager.OpenSource.Other"
+    enum="AndroidDownloadOpenSource" expires_after="2020-02-02">
+  <owner>xingliu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
+  <summary>
+    Records the entry point for users opening downloaded files on Android, that
+    are not audio nor video.
+  </summary>
+</histogram>
+
 <histogram name="Android.DownloadManager.OpenSource.Video"
     enum="AndroidDownloadOpenSource" expires_after="2020-02-02">
   <owner>xingliu@chromium.org</owner>
@@ -2476,6 +2501,15 @@
   </summary>
 </histogram>
 
+<histogram name="Android.DownloadPage.OpenSource"
+    enum="AndroidDownloadOpenSource" expires_after="2020-02-02">
+  <owner>xingliu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
+  <summary>
+    Records the entry point for users opening the download page on Android.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Android.FeatureModules.AvailabilityStatus"
     enum="FeatureModuleAvailabilityStatus" expires_after="2020-01-01">
 <!-- Name completed by histogram_suffixes
@@ -11750,6 +11784,9 @@
 
 <histogram name="Autofill.WalletUseDateInMinutes.Address" units="minutes"
     expires_after="M76">
+  <obsolete>
+    Deprecated as of 2019-08.
+  </obsolete>
   <owner>jkrcal@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -11760,6 +11797,9 @@
 
 <histogram name="Autofill.WalletUseDateInMinutes.Card" units="minutes"
     expires_after="M76">
+  <obsolete>
+    Deprecated as of 2019-08.
+  </obsolete>
   <owner>jkrcal@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -99376,7 +99416,7 @@
 </histogram>
 
 <histogram name="PasswordManager.SuccessfulLoginHappened"
-    enum="BooleanSuccessfulLoginHappenedOnHttps" expires_after="M78">
+    enum="BooleanSuccessfulLoginHappenedOnHttps" expires_after="M88">
   <owner>nepper@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <owner>kolos@chromium.org</owner>
@@ -140067,6 +140107,9 @@
 
 <histogram name="Sync.WalletMetadata.DeletedOldOrphans" units="entities"
     expires_after="M76">
+  <obsolete>
+    Deprecated as of 2019-08.
+  </obsolete>
   <owner>jkrcal@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -143090,6 +143133,17 @@
   </summary>
 </histogram>
 
+<histogram name="TerminalSystemAppFrame.WrenchMenu.MenuAction"
+    enum="WrenchMenuAction" expires_after="2020-09-01">
+  <owner>alancutter@chromium.org</owner>
+  <owner>calamity@chromium.org</owner>
+  <owner>joelhockey@chromium.org</owner>
+  <summary>
+    Number of times that each menu item is clicked from the terminal system app
+    menu button.
+  </summary>
+</histogram>
+
 <histogram name="TextFragmentAnchor.AmbiguousMatch" enum="Boolean"
     expires_after="M81">
   <owner>nburris@chromium.org</owner>
diff --git a/tools/perf/contrib/media_router_benchmarks/media_router_benchmarks.py b/tools/perf/contrib/media_router_benchmarks/media_router_benchmarks.py
index 60da8a8..ba1a58d 100644
--- a/tools/perf/contrib/media_router_benchmarks/media_router_benchmarks.py
+++ b/tools/perf/contrib/media_router_benchmarks/media_router_benchmarks.py
@@ -17,6 +17,7 @@
                 component='Internals>Cast')
 class MediaRouterCPUMemoryCast(perf_benchmark.PerfBenchmark):
   """Obtains media performance for key user scenarios on desktop."""
+
   SUPPORTED_PLATFORMS = [story.expectations.ALL_DESKTOP]
   options = {'pageset_repeat': 1}
   page_set = media_router_pages.MediaRouterCPUMemoryPageSet
@@ -30,7 +31,7 @@
     options.AppendExtraBrowserArgs([
         '--load-extension=' + ','.join(
             [os.path.join(path_util.GetChromiumSrcDir(), 'out',
-             'Release', 'mr_extension', 'release'),
+             'Release', 'mr_extension'),
              os.path.join(path_util.GetChromiumSrcDir(), 'out',
              'Release', 'media_router', 'telemetry_extension')]),
         '--disable-features=ViewsCastDialog',
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index f2db5e11..66c6bb7 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -2179,6 +2179,48 @@
   return type;
 }
 
+void AXPlatformNodeAuraLinux::SetDocumentParentOnFrameIfNecessary() {
+  if (GetAtkRole() != ATK_ROLE_DOCUMENT_WEB)
+    return;
+
+  if (!GetDelegate()->IsWebContent())
+    return;
+
+  AtkObject* parent_atk_object = GetParent();
+  AXPlatformNodeAuraLinux* parent =
+      AtkObjectToAXPlatformNodeAuraLinux(parent_atk_object);
+  if (!parent)
+    return;
+
+  if (parent->GetDelegate()->IsWebContent())
+    return;
+
+  AXPlatformNodeAuraLinux* frame = AtkObjectToAXPlatformNodeAuraLinux(
+      FindAtkObjectParentFrame(parent_atk_object));
+  if (!frame)
+    return;
+
+  frame->SetDocumentParent(parent_atk_object);
+}
+
+AtkObject* AXPlatformNodeAuraLinux::FindFirstWebContentDocument() {
+  for (auto child_iterator_ptr = GetDelegate()->ChildrenBegin();
+       *child_iterator_ptr != *GetDelegate()->ChildrenEnd();
+       ++(*child_iterator_ptr)) {
+    AtkObject* child = child_iterator_ptr->GetNativeViewAccessible();
+    auto* child_node = AtkObjectToAXPlatformNodeAuraLinux(child);
+    if (!child_node)
+      continue;
+    if (!child_node->GetDelegate()->IsWebContent())
+      continue;
+    if (child_node->GetAtkRole() != ATK_ROLE_DOCUMENT_WEB)
+      continue;
+    return child;
+  }
+
+  return nullptr;
+}
+
 AtkObject* AXPlatformNodeAuraLinux::CreateAtkObject() {
   EnsureGTypeInit();
   interface_mask_ = GetGTypeInterfaceMask();
@@ -2187,6 +2229,8 @@
 
   atk_object_initialize(atk_object, this);
 
+  SetDocumentParentOnFrameIfNecessary();
+
   return ATK_OBJECT(atk_object);
 }
 
@@ -2795,14 +2839,21 @@
 AtkRelationSet* AXPlatformNodeAuraLinux::GetAtkRelations() {
   AtkRelationSet* relation_set = atk_relation_set_new();
 
-  if (embedded_document_) {
-    atk_relation_set_add_relation_by_type(relation_set, ATK_RELATION_EMBEDS,
-                                          embedded_document_);
+  if (GetDelegate()->IsWebContent() && GetAtkRole() == ATK_ROLE_DOCUMENT_WEB) {
+    AtkObject* parent_frame = FindAtkObjectParentFrame(GetOrCreateAtkObject());
+    if (parent_frame) {
+      atk_relation_set_add_relation_by_type(
+          relation_set, ATK_RELATION_EMBEDDED_BY, parent_frame);
+    }
   }
 
-  if (embedding_window_) {
-    atk_relation_set_add_relation_by_type(
-        relation_set, ATK_RELATION_EMBEDDED_BY, embedding_window_);
+  if (auto* document_parent =
+          AtkObjectToAXPlatformNodeAuraLinux(document_parent_)) {
+    AtkObject* document = document_parent->FindFirstWebContentDocument();
+    if (document) {
+      atk_relation_set_add_relation_by_type(relation_set, ATK_RELATION_EMBEDS,
+                                            document);
+    }
   }
 
   // For each possible relation defined by an IntAttribute, we test that
@@ -2856,8 +2907,7 @@
 
   DestroyAtkObjects();
 
-  SetWeakGPtrToAtkObject(&embedded_document_, nullptr);
-  SetWeakGPtrToAtkObject(&embedding_window_, nullptr);
+  SetWeakGPtrToAtkObject(&document_parent_, nullptr);
 }
 
 void AXPlatformNodeAuraLinux::Destroy() {
@@ -3280,6 +3330,21 @@
                         GetIndexInParent(), GetOrCreateAtkObject());
 }
 
+void AXPlatformNodeAuraLinux::OnParentChanged() {
+  if (!atk_object_)
+    return;
+
+  AtkPropertyValues property_values;
+  property_values.property_name = "accessible-parent";
+  property_values.new_value = G_VALUE_INIT;
+  g_value_init(&property_values.new_value, G_TYPE_OBJECT);
+  g_value_set_object(&property_values.new_value, GetParent());
+  g_signal_emit_by_name(G_OBJECT(atk_object_),
+                        "property-change::accessible-parent", &property_values,
+                        nullptr);
+  g_value_unset(&property_values.new_value);
+}
+
 void AXPlatformNodeAuraLinux::OnInvalidStatusChanged() {
   atk_object_notify_state_change(
       ATK_OBJECT(GetOrCreateAtkObject()), ATK_STATE_INVALID_ENTRY,
@@ -3792,14 +3857,10 @@
   *attributes = PrependAtkAttributeToAtkAttributeSet(name, value, *attributes);
 }
 
-void AXPlatformNodeAuraLinux::SetEmbeddedDocument(
-    AtkObject* new_embedded_document) {
-  SetWeakGPtrToAtkObject(&embedded_document_, new_embedded_document);
-}
-
-void AXPlatformNodeAuraLinux::SetEmbeddingWindow(
-    AtkObject* new_embedding_window) {
-  SetWeakGPtrToAtkObject(&embedding_window_, new_embedding_window);
+void AXPlatformNodeAuraLinux::SetDocumentParent(
+    AtkObject* new_document_parent) {
+  DCHECK(GetAtkRole() == ATK_ROLE_FRAME);
+  SetWeakGPtrToAtkObject(&document_parent_, new_document_parent);
 }
 
 base::string16 AXPlatformNodeAuraLinux::GetHypertext() const {
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.h b/ui/accessibility/platform/ax_platform_node_auralinux.h
index 02c35be..2d64e2a 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.h
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.h
@@ -164,6 +164,7 @@
   void OnDocumentTitleChanged();
   void OnSubtreeCreated();
   void OnSubtreeWillBeDeleted();
+  void OnParentChanged();
   void OnWindowVisibilityChanged();
 
   bool SupportsSelectionWithAtkSelection();
@@ -186,8 +187,10 @@
   size_t UTF16ToUnicodeOffsetInText(size_t utf16_offset);
   size_t UnicodeToUTF16OffsetInText(int unicode_offset);
 
-  void SetEmbeddedDocument(AtkObject* new_document);
-  void SetEmbeddingWindow(AtkObject* new_embedding_window);
+  // Called on a toplevel frame to set the document parent, which is the parent
+  // of the toplevel document. This is used to properly express the ATK embeds
+  // relationship between a toplevel frame and its embedded document.
+  void SetDocumentParent(AtkObject* new_document_parent);
 
   int GetCaretOffset();
   bool SetCaretOffset(int offset);
@@ -277,6 +280,13 @@
   // Activate a find in page result for the toplevel document of this node.
   void ActivateFindInPageInParent(int start_offset, int end_offset);
 
+  // If this node is the toplevel document node, find its parent and set it on
+  // the toplevel frame which contains the node.
+  void SetDocumentParentOnFrameIfNecessary();
+
+  // Find the first child which is a document containing web content.
+  AtkObject* FindFirstWebContentDocument();
+
   // If the given argument can be found as a child of this node, return its
   // hypertext extents, otherwise return base::nullopt;
   base::Optional<std::pair<int, int>> GetHypertextExtentsOfChild(
@@ -293,9 +303,8 @@
   AtkObject* atk_object_ = nullptr;
   AtkHyperlink* atk_hyperlink_ = nullptr;
 
-  // Some weak pointers which help us track ATK embeds / embedded by relations.
-  AtkObject* embedded_document_ = nullptr;
-  AtkObject* embedding_window_ = nullptr;
+  // A weak pointers which help us track the ATK embeds relation.
+  AtkObject* document_parent_ = nullptr;
 
   // Whether or not this node (if it is a frame or a window) was
   // minimized the last time it's visibility changed.
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
index 31966672..44973634 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
@@ -2238,4 +2238,33 @@
   g_object_unref(original_atk_object);
 }
 
+TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkObjectParentChanged) {
+  AXNodeData root_data;
+  root_data.id = 1;
+  root_data.role = ax::mojom::Role::kListBox;
+  root_data.child_ids.push_back(2);
+
+  AXNodeData item_1_data;
+  item_1_data.id = 2;
+  item_1_data.role = ax::mojom::Role::kListBoxOption;
+
+  Init(root_data, item_1_data);
+
+  AXNode* item_1 = GetRootNode()->children()[0];
+  AtkObject* atk_object = AtkObjectFromNode(item_1);
+  AXPlatformNodeAuraLinux* node = GetPlatformNode(item_1);
+
+  bool saw_parent_changed = false;
+  g_signal_connect(
+      atk_object, "property-change::accessible-parent",
+      G_CALLBACK(+[](AtkObject*, void* property, bool* saw_parent_changed) {
+        *saw_parent_changed = true;
+      }),
+      &saw_parent_changed);
+
+  ASSERT_FALSE(saw_parent_changed);
+  node->OnParentChanged();
+  ASSERT_TRUE(saw_parent_changed);
+}
+
 }  // namespace ui
diff --git a/ui/base/test/skia_gold_pixel_diff.cc b/ui/base/test/skia_gold_pixel_diff.cc
index 942cfe5..c0d3fcc 100644
--- a/ui/base/test/skia_gold_pixel_diff.cc
+++ b/ui/base/test/skia_gold_pixel_diff.cc
@@ -27,8 +27,7 @@
 #include "ui/gfx/image/image.h"
 #include "ui/snapshot/snapshot.h"
 
-// This may change to a different bucket in the future.
-const char* kSkiaGoldInstance = "chrome-gpu";
+const char* kSkiaGoldInstance = "chrome";
 
 #if defined(OS_WIN)
 const wchar_t* kSkiaGoldCtl = L"tools/skia_goldctl/goldctl.exe";
@@ -201,6 +200,7 @@
   base::ScopedAllowBlockingForTesting allow_blocking;
   base::CommandLine cmd(GetAbsoluteSrcRelativePath(kSkiaGoldCtl));
   cmd.AppendSwitchASCII("test-name", remote_golden_image_name);
+  cmd.AppendSwitchASCII("add-test-key", "source_type:gtest-pixeltests");
   cmd.AppendSwitchPath("png-file", local_file_path);
   cmd.AppendSwitchPath("work-dir", working_dir_);
   AppendArgsJustAfterProgram(
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 904ec2e..fa26af6 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -70,6 +70,8 @@
     "color_space_utils.h",
     "dc_renderer_layer_params.cc",
     "dc_renderer_layer_params.h",
+    "dual_gpu_state.cc",
+    "dual_gpu_state.h",
     "egl_timestamps.h",
     "gl_bindings.cc",
     "gl_bindings.h",
@@ -332,6 +334,8 @@
   }
   if (is_mac) {
     sources += [
+      "dual_gpu_state_mac.cc",
+      "dual_gpu_state_mac.h",
       "gl_context_cgl.cc",
       "gl_context_cgl.h",
       "gl_fence_apple.cc",
diff --git a/ui/gl/dual_gpu_state.cc b/ui/gl/dual_gpu_state.cc
new file mode 100644
index 0000000..53ed4796
--- /dev/null
+++ b/ui/gl/dual_gpu_state.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gl/dual_gpu_state.h"
+
+#include "base/trace_event/trace_event.h"
+
+namespace gl {
+
+DualGPUState::DualGPUState() : num_high_performance_contexts_(0) {}
+
+void DualGPUState::RegisterHighPerformanceContext() {
+  CancelDelayedSwitchToLowPowerGPU();
+  num_high_performance_contexts_++;
+  SwitchToHighPerformanceGPUIfNeeded();
+}
+
+void DualGPUState::RemoveHighPerformanceContext() {
+  DCHECK(num_high_performance_contexts_ > 0);
+
+  num_high_performance_contexts_--;
+  if (num_high_performance_contexts_ == 0)
+    AttemptSwitchToLowPowerGPUWithDelay();
+}
+
+}  // namespace gl
diff --git a/ui/gl/dual_gpu_state.h b/ui/gl/dual_gpu_state.h
new file mode 100644
index 0000000..b91839d
--- /dev/null
+++ b/ui/gl/dual_gpu_state.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GL_DUAL_GPU_STATE_H_
+#define UI_GL_DUAL_GPU_STATE_H_
+
+#include "base/macros.h"
+#include "ui/gl/gl_export.h"
+
+namespace gl {
+
+class GL_EXPORT DualGPUState {
+ public:
+  void RegisterHighPerformanceContext();
+  void RemoveHighPerformanceContext();
+
+ protected:
+  DualGPUState();
+
+ private:
+  virtual void SwitchToHighPerformanceGPUIfNeeded() = 0;
+  virtual void SwitchToLowPowerGPU() = 0;
+  virtual void AttemptSwitchToLowPowerGPUWithDelay() = 0;
+  virtual void CancelDelayedSwitchToLowPowerGPU() = 0;
+
+  unsigned int num_high_performance_contexts_;
+
+  DISALLOW_COPY_AND_ASSIGN(DualGPUState);
+};
+
+}  // namespace gl
+
+#endif  // UI_GL_DUAL_GPU_STATE_H_
diff --git a/ui/gl/dual_gpu_state_mac.cc b/ui/gl/dual_gpu_state_mac.cc
new file mode 100644
index 0000000..c9c5190
--- /dev/null
+++ b/ui/gl/dual_gpu_state_mac.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gl/dual_gpu_state_mac.h"
+
+#include <OpenGL/CGLRenderers.h>
+#include <OpenGL/CGLTypes.h>
+
+#include "base/bind.h"
+#include "base/cancelable_callback.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "ui/gl/gl_bindings.h"
+
+namespace gl {
+
+namespace {
+
+const unsigned int kDelayLengthSeconds = 10;
+
+}  // namespace
+
+// static
+DualGPUStateMac* DualGPUStateMac::GetInstance() {
+  static base::NoDestructor<DualGPUStateMac> instance;
+  return instance.get();
+}
+
+DualGPUStateMac::DualGPUStateMac() {}
+
+DualGPUStateMac::~DualGPUStateMac() {}
+
+void DualGPUStateMac::SwitchToHighPerformanceGPUIfNeeded() {
+  AllocateDiscretePixelFormatObjectIfNeeded();
+}
+
+// This function is called by a delayed task posted in
+// DualGPUStateMac::AttemptSwitchToLowPowerGPUWithDelay.
+void DualGPUStateMac::SwitchToLowPowerGPU() {
+  ReleaseDiscretePixelFormatObjectIfNeeded();
+}
+
+void DualGPUStateMac::AttemptSwitchToLowPowerGPUWithDelay() {
+  if (base::ThreadTaskRunnerHandle::IsSet()) {
+    cancelable_delay_callback_.Reset(base::BindOnce(
+        []() { DualGPUStateMac::GetInstance()->SwitchToLowPowerGPU(); }));
+
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, cancelable_delay_callback_.callback(),
+        base::TimeDelta::FromSeconds(kDelayLengthSeconds));
+  } else {
+    SwitchToLowPowerGPU();
+  }
+}
+
+void DualGPUStateMac::CancelDelayedSwitchToLowPowerGPU() {
+  cancelable_delay_callback_.Cancel();
+}
+
+void DualGPUStateMac::AllocateDiscretePixelFormatObjectIfNeeded() {
+  if (discrete_pixelformat_)
+    return;
+  CGLPixelFormatAttribute attribs[1];
+  attribs[0] = static_cast<CGLPixelFormatAttribute>(0);
+  GLint num_pixel_formats = 0;
+  CGLChoosePixelFormat(attribs, &discrete_pixelformat_, &num_pixel_formats);
+}
+
+void DualGPUStateMac::ReleaseDiscretePixelFormatObjectIfNeeded() {
+  if (!discrete_pixelformat_)
+    return;
+  CGLReleasePixelFormat(discrete_pixelformat_);
+  discrete_pixelformat_ = nullptr;
+}
+
+}  // namespace gl
diff --git a/ui/gl/dual_gpu_state_mac.h b/ui/gl/dual_gpu_state_mac.h
new file mode 100644
index 0000000..43b5a45f
--- /dev/null
+++ b/ui/gl/dual_gpu_state_mac.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GL_DUAL_GPU_STATE_MAC_H_
+#define UI_GL_DUAL_GPU_STATE_MAC_H_
+
+#include <OpenGL/CGLRenderers.h>
+#include <OpenGL/CGLTypes.h>
+
+#include "base/cancelable_callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/no_destructor.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "ui/gl/dual_gpu_state.h"
+#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_export.h"
+
+namespace gl {
+
+class GL_EXPORT DualGPUStateMac : public DualGPUState {
+ public:
+  static DualGPUStateMac* GetInstance();
+
+ private:
+  friend base::NoDestructor<DualGPUStateMac>;
+
+  DualGPUStateMac();
+  ~DualGPUStateMac();
+
+  void SwitchToHighPerformanceGPUIfNeeded() override;
+  void SwitchToLowPowerGPU() override;
+  void AttemptSwitchToLowPowerGPUWithDelay() override;
+  void CancelDelayedSwitchToLowPowerGPU() override;
+
+  void AllocateDiscretePixelFormatObjectIfNeeded();
+  void ReleaseDiscretePixelFormatObjectIfNeeded();
+
+  CGLPixelFormatObj discrete_pixelformat_ = nullptr;
+  base::CancelableOnceClosure cancelable_delay_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(DualGPUStateMac);
+};
+
+}  // namespace gl
+
+#endif  // UI_GL_DUAL_GPU_STATE_MAC_H_
diff --git a/ui/gl/gl_context_cgl.cc b/ui/gl/gl_context_cgl.cc
index a826eb7..43d843c7 100644
--- a/ui/gl/gl_context_cgl.cc
+++ b/ui/gl/gl_context_cgl.cc
@@ -17,6 +17,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
+#include "ui/gl/dual_gpu_state_mac.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_fence.h"
 #include "ui/gl/gl_gl_api_implementation.h"
@@ -99,18 +100,12 @@
     return false;
 
   // If using the discrete gpu, create a pixel format requiring it before we
-  // create the context.
+  // create the context. If switchable GPUs are unsupported, we should bias
+  // toward the discrete gpu.
   if (!GLContext::SwitchableGPUsSupported() ||
       gpu_preference == GpuPreference::kHighPerformance) {
-    std::vector<CGLPixelFormatAttribute> discrete_attribs;
-    discrete_attribs.push_back((CGLPixelFormatAttribute) 0);
-    GLint num_pixel_formats;
-    if (CGLChoosePixelFormat(&discrete_attribs.front(),
-                             &discrete_pixelformat_,
-                             &num_pixel_formats) != kCGLNoError) {
-      LOG(ERROR) << "Error choosing pixel format.";
-      return false;
-    }
+    DualGPUStateMac::GetInstance()->RegisterHighPerformanceContext();
+    is_high_performance_context_ = true;
     // The renderer might be switched after this, so ignore the saved ID.
     share_group()->SetRendererID(-1);
   }
@@ -154,18 +149,9 @@
       SetCurrentGL(current_context->GetCurrentGL());
     }
   }
-  if (discrete_pixelformat_) {
-    if (base::ThreadTaskRunnerHandle::IsSet()) {
-      // Delay releasing the pixel format for 10 seconds to reduce the number of
-      // unnecessary GPU switches.
-      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-          FROM_HERE,
-          base::BindOnce(&CGLReleasePixelFormat, discrete_pixelformat_),
-          base::TimeDelta::FromSeconds(10));
-    } else {
-      CGLReleasePixelFormat(discrete_pixelformat_);
-    }
-    discrete_pixelformat_ = nullptr;
+
+  if (is_high_performance_context_) {
+    DualGPUStateMac::GetInstance()->RemoveHighPerformanceContext();
   }
   if (context_) {
     CGLDestroyContext(static_cast<CGLContextObj>(context_));
@@ -183,8 +169,8 @@
     int screen;
     CGLGetVirtualScreen(static_cast<CGLContextObj>(context_), &screen);
 
-    if (g_support_renderer_switching &&
-        !discrete_pixelformat_ && renderer_id != -1 &&
+    if (g_support_renderer_switching && !is_high_performance_context_ &&
+        renderer_id != -1 &&
         (screen != screen_ || renderer_id != renderer_id_)) {
       // Attempt to find a virtual screen that's using the requested renderer,
       // and switch the context to use that screen. Don't attempt to switch if
diff --git a/ui/gl/gl_context_cgl.h b/ui/gl/gl_context_cgl.h
index eab630f..2242c29 100644
--- a/ui/gl/gl_context_cgl.h
+++ b/ui/gl/gl_context_cgl.h
@@ -55,11 +55,10 @@
   std::map<uint64_t, std::unique_ptr<GLFence>> backpressure_fences_;
   uint64_t next_backpressure_fence_ = 0;
 
-  CGLPixelFormatObj discrete_pixelformat_ = nullptr;
-
   int screen_ = -1;
   int renderer_id_ = -1;
   bool safe_to_force_gpu_switch_ = true;
+  bool is_high_performance_context_ = false;
 
   // Debugging for https://crbug.com/863817
   bool has_switched_gpus_ = false;
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate.h b/ui/views/accessibility/view_ax_platform_node_delegate.h
index bb06557..6723ecf 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate.h
+++ b/ui/views/accessibility/view_ax_platform_node_delegate.h
@@ -75,6 +75,8 @@
  protected:
   explicit ViewAXPlatformNodeDelegate(View* view);
 
+  ui::AXPlatformNode* ax_platform_node() { return ax_platform_node_; }
+
  private:
   // Uses Views::GetViewsInGroup to find nearby Views in the same group.
   // Searches from the View's parent to include siblings within that group.
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
index cd191e5f..c2584d93 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
@@ -141,7 +141,9 @@
 
 ViewAXPlatformNodeDelegateAuraLinux::ViewAXPlatformNodeDelegateAuraLinux(
     View* view)
-    : ViewAXPlatformNodeDelegate(view) {}
+    : ViewAXPlatformNodeDelegate(view) {
+  view->AddObserver(this);
+}
 
 ViewAXPlatformNodeDelegateAuraLinux::~ViewAXPlatformNodeDelegateAuraLinux() =
     default;
@@ -153,4 +155,13 @@
   return parent;
 }
 
+void ViewAXPlatformNodeDelegateAuraLinux::OnViewHierarchyChanged(
+    views::View* observed_view,
+    const views::ViewHierarchyChangedDetails& details) {
+  if (view() != details.child || !details.is_add)
+    return;
+  static_cast<ui::AXPlatformNodeAuraLinux*>(ax_platform_node())
+      ->OnParentChanged();
+}
+
 }  // namespace views
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.h b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.h
index d7bac7f2..e76c4592 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.h
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.h
@@ -7,12 +7,14 @@
 
 #include "base/macros.h"
 #include "ui/views/accessibility/view_ax_platform_node_delegate.h"
+#include "ui/views/view_observer.h"
 
 namespace views {
 
 class View;
 
-class ViewAXPlatformNodeDelegateAuraLinux : public ViewAXPlatformNodeDelegate {
+class ViewAXPlatformNodeDelegateAuraLinux : public ViewAXPlatformNodeDelegate,
+                                            public views::ViewObserver {
  public:
   explicit ViewAXPlatformNodeDelegateAuraLinux(View* view);
   ~ViewAXPlatformNodeDelegateAuraLinux() override;
@@ -21,6 +23,10 @@
   gfx::NativeViewAccessible GetParent() override;
 
  private:
+  void OnViewHierarchyChanged(
+      views::View* observed_view,
+      const views::ViewHierarchyChangedDetails& details) override;
+
   DISALLOW_COPY_AND_ASSIGN(ViewAXPlatformNodeDelegateAuraLinux);
 };
 
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
index e79ee95..48407f0e 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
@@ -7,6 +7,9 @@
 import android.app.Activity;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.LinearLayout;
 
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.components.embedder_support.view.ContentViewRenderView;
@@ -21,10 +24,14 @@
     private long mNativeBrowserController;
 
     private ActivityWindowAndroid mWindowAndroid;
+    // This is set as the content view of the activity. It contains mContentView.
+    private LinearLayout mLinearLayout;
+    // This is parented to mLinearLayout.
     private ContentViewRenderView mContentView;
     private ProfileImpl mProfile;
     private WebContents mWebContents;
     private BrowserObserverProxy mBrowserObserverProxy;
+    private View mTopView;
 
     private static class InternalAccessDelegateImpl
             implements ViewEventSink.InternalAccessDelegate {
@@ -51,9 +58,12 @@
             Activity activity, ProfileImpl profile, BrowserControllerClient client) {
         mProfile = profile;
 
+        mLinearLayout = new LinearLayout(activity);
+        mLinearLayout.setOrientation(LinearLayout.VERTICAL);
+
         mWindowAndroid = new ActivityWindowAndroid(activity);
         mContentView = new ContentViewRenderView(activity);
-        activity.setContentView(mContentView);
+        activity.setContentView(mLinearLayout);
         mWindowAndroid.setAnimationPlaceholderView(mContentView.getSurfaceView());
 
         mContentView.onNativeLibraryLoaded(mWindowAndroid);
@@ -65,6 +75,9 @@
                 WebContents.createDefaultInternalsHolder());
 
         mContentView.setCurrentWebContents(mWebContents);
+        mLinearLayout.addView(mContentView,
+                new LinearLayout.LayoutParams(
+                        LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 1f));
         mWebContents.onShow();
         mBrowserObserverProxy = new BrowserObserverProxy(mNativeBrowserController, client);
     }
@@ -75,6 +88,17 @@
         mNativeBrowserController = 0;
     }
 
+    public void setTopView(View view) {
+        if (mTopView == view) return;
+        if (mTopView != null) mLinearLayout.removeView(mTopView);
+        mTopView = view;
+        if (mTopView != null) {
+            mLinearLayout.addView(mTopView, 0,
+                    new LinearLayout.LayoutParams(
+                            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 0f));
+        }
+    }
+
     // TODO: this is temporary, move to NavigationControllerImpl.
     public void navigate(String uri) {
         mWebContents.getNavigationController().loadUrl(new LoadUrlParams(uri));
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserController.java b/weblayer/public/java/org/chromium/weblayer/BrowserController.java
index 0e96e8e..ed4f761c 100644
--- a/weblayer/public/java/org/chromium/weblayer/BrowserController.java
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserController.java
@@ -6,6 +6,7 @@
 
 import android.app.Activity;
 import android.net.Uri;
+import android.view.View;
 
 import org.chromium.weblayer_private.BrowserControllerClient;
 import org.chromium.weblayer_private.BrowserControllerImpl;
@@ -34,6 +35,10 @@
         return mNavigationController;
     }
 
+    public void setTopView(View view) {
+        mBrowserController.setTopView(view);
+    }
+
     public void addObserver(BrowserObserver observer) {
         mObservers.add(observer);
     }
diff --git a/weblayer/shell/android/src/org/chromium/weblayer/shell/WebLayerShellActivity.java b/weblayer/shell/android/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
index de90d131..538d6b6 100644
--- a/weblayer/shell/android/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
+++ b/weblayer/shell/android/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
@@ -7,9 +7,16 @@
 import android.app.Activity;
 import android.net.Uri;
 import android.os.Bundle;
+import android.text.InputType;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
 
 import org.chromium.base.CommandLine;
 import org.chromium.weblayer.BrowserController;
+import org.chromium.weblayer.BrowserObserver;
 import org.chromium.weblayer.Profile;
 import org.chromium.weblayer.WebLayer;
 
@@ -24,6 +31,7 @@
     private WebLayer mWebLayer;
     private Profile mProfile;
     private BrowserController mBrowserController;
+    private EditText mUrlView;
 
     @Override
     protected void onCreate(final Bundle savedInstanceState) {
@@ -35,13 +43,40 @@
             ((WebLayerShellApplication) getApplication()).initCommandLine();
         }
 
+        mUrlView = new EditText(this);
+        mUrlView.setSelectAllOnFocus(true);
+        mUrlView.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
+        mUrlView.setOnEditorActionListener(new OnEditorActionListener() {
+            @Override
+            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+                if ((actionId != EditorInfo.IME_ACTION_GO)
+                        && (event == null || event.getKeyCode() != KeyEvent.KEYCODE_ENTER
+                                || event.getAction() != KeyEvent.ACTION_DOWN)) {
+                    return false;
+                }
+                loadUrl(mUrlView.getText().toString());
+                return true;
+            }
+        });
+
         mProfile = WebLayer.getInstance().createProfile(new File(""));
         mBrowserController = new BrowserController(this, mProfile);
-        mBrowserController.getNavigationController().navigate(Uri.parse("http://google.com"));
+        mBrowserController.setTopView(mUrlView);
+        loadUrl("http://google.com");
+        mBrowserController.addObserver(new BrowserObserver() {
+            @Override
+            public void displayURLChanged(Uri uri) {
+                mUrlView.setText(uri.toString());
+            }
+        });
     }
 
     @Override
     protected void onStart() {
         super.onStart();
     }
+
+    private void loadUrl(String url) {
+        mBrowserController.getNavigationController().navigate(Uri.parse(url));
+    }
 }