diff --git a/DEPS b/DEPS
index 2344ffd..a09ff2c 100644
--- a/DEPS
+++ b/DEPS
@@ -79,11 +79,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': '50ea238a8898475b51c573a9cbea148ae8b94f70',
+  'skia_revision': '4125b6165d4314c6c64ce12d31b2565d52c6fad3',
   # 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': '610f27b9dc2777501807617177404b2b481a1633',
+  'v8_revision': '5c0ec61e5061683382c0d5953b1af43b4419f88b',
   # 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.
@@ -91,7 +91,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'b391ec40837c3e2773b43a4a0687b3c6d5e8ca36',
+  'angle_revision': 'd429ac569e9772c25b16c09d6b90228064adf63b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -103,7 +103,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'b3a5240832fce3f0b706c16070a1e69c2c1edb86',
+  'pdfium_revision': '2e6405c333d8daae4e3edaa6b48f5ac5a8d7675b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -155,7 +155,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.
-  'feed_revision': '0ff595791bd7e6316f8bae206090dd2583f2cf27',
+  'feed_revision': '089c8e9316ed3a2afd631ba2b773f13e69121dc7',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -664,7 +664,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '3c1cb0203b6cfc10389e85a350b2ea6ca29d01ce',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '2e0da5aa389fca1a238edb0e2ed79d9a17a7eff9', # commit position 21742
+    Var('webrtc_git') + '/src.git' + '@' + '3dc0125cf7e8df6e60222fc5b69f08c52486a959', # commit position 21742
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 117881c0..e6c28f9 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -557,8 +557,8 @@
 # Bypass the AUTHORS check for these accounts.
 _KNOWN_ROBOTS = set(
   '%s-chromium-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com' % s
-  for s in ('afdo', 'angle', 'catapult', 'chromite', 'depot-tools', 'nacl',
-            'pdfium', 'skia', 'src-internal', 'webrtc'))
+  for s in ('afdo', 'angle', 'catapult', 'chromite', 'depot-tools',
+            'fuchsia-sdk', 'nacl', 'pdfium', 'skia', 'src-internal', 'webrtc'))
 
 
 def _CheckNoProductionCodeUsingTestOnlyFunctions(input_api, output_api):
diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc
index 4ca3e476..3382cee5 100644
--- a/android_webview/browser/aw_browser_main_parts.cc
+++ b/android_webview/browser/aw_browser_main_parts.cc
@@ -132,9 +132,6 @@
       browser_client_->GetNetLog());
 
   content::RenderFrameHost::AllowInjectingJavaScriptForAndroidWebView();
-
-  // TODO(meacer): Remove when PlzNavigate ships.
-  content::RenderFrameHost::AllowDataUrlNavigationForAndroidWebView();
 }
 
 bool AwBrowserMainParts::MainMessageLoopRun(int* result_code) {
diff --git a/android_webview/browser/net/aw_request_interceptor.cc b/android_webview/browser/net/aw_request_interceptor.cc
index ebe8b2c2..bfccbbe 100644
--- a/android_webview/browser/net/aw_request_interceptor.cc
+++ b/android_webview/browser/net/aw_request_interceptor.cc
@@ -154,9 +154,9 @@
   if (request->GetUserData(kRequestAlreadyHasJobDataKey))
     return nullptr;
 
-  // With PlzNavigate, we now seem to receive blob URLs in interceptor.
-  // Ignore these URLs.
-  // TODO(sgurun) is this the best place to do that? Talk with jam@.
+  // It's not useful to emit shouldInterceptRequest for blob URLs, so we
+  // intentionally skip the callback for all such URLs. See
+  // http://crbug.com/822983.
   if (request->url().SchemeIs(url::kBlobScheme)) {
     return nullptr;
   }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwAutofillManager.java b/android_webview/java/src/org/chromium/android_webview/AwAutofillManager.java
index e910b75..82dc863 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwAutofillManager.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwAutofillManager.java
@@ -12,7 +12,6 @@
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.Log;
 
 import java.lang.ref.WeakReference;
@@ -48,10 +47,10 @@
 
     public AwAutofillManager(Context context) {
         if (DEBUG) Log.i(TAG, "constructor");
-        mDisabled = !BuildInfo.isAtLeastP() && AwContents.activityFromContext(context) == null;
+        mAutofillManager = context.getSystemService(AutofillManager.class);
+        mDisabled = mAutofillManager == null || !mAutofillManager.isEnabled();
         if (mDisabled) return;
 
-        mAutofillManager = context.getSystemService(AutofillManager.class);
         mMonitor = new AutofillInputUIMonitor(this);
         mAutofillManager.registerCallback(mMonitor);
     }
diff --git a/ash/DEPS b/ash/DEPS
index 12ec130..00816fed 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -75,6 +75,9 @@
   "+chromeos/settings/timezone_settings.h",
   "+chromeos/system",
 
+  # InputMethodManager lives in the browser process. Use ImeController.
+  "-ui/base/ime/chromeos/input_method_manager.h"
+
   # Ozone does not run in process in mus/mash.
   "-ui/events/ozone",
   "-ui/ozone",
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index 5cade50..b49e3b70 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -44,8 +44,6 @@
 #include "ui/aura/window.h"
 #include "ui/base/ime/chromeos/fake_ime_keyboard.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
-#include "ui/base/ime/chromeos/input_method_manager.h"
-#include "ui/base/ime/chromeos/mock_input_method_manager.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
@@ -54,8 +52,6 @@
 #include "ui/message_center/message_center.h"
 #include "ui/views/widget/widget.h"
 
-using chromeos::input_method::InputMethodManager;
-
 namespace ash {
 
 namespace {
@@ -138,23 +134,6 @@
   DISALLOW_COPY_AND_ASSIGN(DummyBrightnessControlDelegate);
 };
 
-class TestInputMethodManager
-    : public chromeos::input_method::MockInputMethodManager {
- public:
-  TestInputMethodManager() = default;
-  ~TestInputMethodManager() override = default;
-
-  // MockInputMethodManager:
-  chromeos::input_method::ImeKeyboard* GetImeKeyboard() override {
-    return &keyboard_;
-  }
-
-  chromeos::input_method::FakeImeKeyboard keyboard_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestInputMethodManager);
-};
-
 class DummyKeyboardBrightnessControlDelegate
     : public KeyboardBrightnessControlDelegate {
  public:
@@ -211,18 +190,6 @@
   AcceleratorControllerTest() = default;
   ~AcceleratorControllerTest() override = default;
 
-  void SetUp() override {
-    AshTestBase::SetUp();
-    test_input_method_manager_ = new TestInputMethodManager;
-    // Takes ownership.
-    InputMethodManager::Initialize(test_input_method_manager_);
-  }
-
-  void TearDown() override {
-    InputMethodManager::Shutdown();
-    AshTestBase::TearDown();
-  }
-
  protected:
   static AcceleratorController* GetController();
 
@@ -291,9 +258,6 @@
     Shell::Get()->keyboard_brightness_control_delegate_ = std::move(delegate);
   }
 
-  // Owned by InputMethodManager.
-  TestInputMethodManager* test_input_method_manager_ = nullptr;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(AcceleratorControllerTest);
 };
diff --git a/ash/shelf/shelf_application_menu_model.cc b/ash/shelf/shelf_application_menu_model.cc
index 9c2d2fc8..76aabbbc9 100644
--- a/ash/shelf/shelf_application_menu_model.cc
+++ b/ash/shelf/shelf_application_menu_model.cc
@@ -11,6 +11,7 @@
 
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "base/metrics/histogram_macros.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/gfx/image/image.h"
 
@@ -27,9 +28,11 @@
     std::vector<mojom::MenuItemPtr> items,
     ShelfItemDelegate* delegate)
     : ui::SimpleMenuModel(this), items_(std::move(items)), delegate_(delegate) {
-  AddSeparator(ui::SPACING_SEPARATOR);
+  if (!features::IsTouchableAppContextMenuEnabled())
+    AddSeparator(ui::SPACING_SEPARATOR);
   AddItem(kInvalidCommandId, title);
-  AddSeparator(ui::SPACING_SEPARATOR);
+  if (!features::IsTouchableAppContextMenuEnabled())
+    AddSeparator(ui::SPACING_SEPARATOR);
 
   for (size_t i = 0; i < items_.size(); i++) {
     mojom::MenuItem* item = items_[i].get();
diff --git a/ash/shelf/shelf_constants.h b/ash/shelf/shelf_constants.h
index bf70054..8eaa292 100644
--- a/ash/shelf/shelf_constants.h
+++ b/ash/shelf/shelf_constants.h
@@ -58,6 +58,9 @@
 // notifications, etc).
 ASH_EXPORT constexpr SkColor kShelfIconColor = SK_ColorWHITE;
 
+// The dip offset for showing a context menu with a long touch press.
+ASH_EXPORT constexpr int kScaledIconContextMenuOffset = 5;
+
 // The alpha value for the shelf background when a window is overlapping.
 ASH_EXPORT constexpr int kShelfTranslucentAlpha = 153;
 
diff --git a/ash/shelf/shelf_context_menu_model.cc b/ash/shelf/shelf_context_menu_model.cc
index f24f777..136f37eb 100644
--- a/ash/shelf/shelf_context_menu_model.cc
+++ b/ash/shelf/shelf_context_menu_model.cc
@@ -23,6 +23,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "components/prefs/pref_service.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/image/image.h"
 
 using l10n_util::GetStringUTF16;
@@ -128,8 +129,9 @@
       menu_items_(std::move(menu_items)),
       delegate_(delegate),
       display_id_(display_id) {
-  // Append some menu items that are handled locally by Ash.
-  AddLocalMenuItems(&menu_items_, display_id);
+  // Append shelf settings and wallpaper items if no shelf item was selected.
+  if (!features::IsTouchableAppContextMenuEnabled() || !delegate)
+    AddLocalMenuItems(&menu_items_, display_id);
   menu_utils::PopulateMenuFromMojoMenuItems(this, this, menu_items_,
                                             &submenus_);
 }
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 56aca09..6212ede 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -63,17 +63,17 @@
 // The proportion of the shelf space reserved for non-panel icons. Panels
 // may flow into this space but will be put into the overflow bubble if there
 // is contention for the space.
-const float kReservedNonPanelIconProportion = 0.67f;
+constexpr float kReservedNonPanelIconProportion = 0.67f;
 
 // The distance of the cursor from the outer rim of the shelf before it
 // separates.
-const int kRipOffDistance = 48;
+constexpr int kRipOffDistance = 48;
 
 // The rip off drag and drop proxy image should get scaled by this factor.
-const float kDragAndDropProxyScale = 1.2f;
+constexpr float kDragAndDropProxyScale = 1.2f;
 
 // The opacity represents that this partially disappeared item will get removed.
-const float kDraggedImageOpacity = 0.5f;
+constexpr float kDraggedImageOpacity = 0.5f;
 
 namespace {
 
@@ -1463,6 +1463,99 @@
   overflow_view->last_visible_index_ = last_overflow_index;
 }
 
+gfx::Rect ShelfView::GetMenuAnchorRect(const views::View* source,
+                                       const gfx::Point& location,
+                                       ui::MenuSourceType source_type,
+                                       bool context_menu) const {
+  if (context_menu) {
+    if (source_type == ui::MenuSourceType::MENU_SOURCE_TOUCH)
+      return GetTouchMenuAnchorRect(source, location);
+    return gfx::Rect(location, gfx::Size());
+  }
+
+  // The menu is for an application list.
+  DCHECK(source) << "Application lists require a source button view.";
+  // Application lists use a bubble. It is possible to invoke the menu while
+  // it is sliding into view. To cover that case, the screen coordinates are
+  // offsetted by the animation delta.
+  aura::Window* window = GetWidget()->GetNativeWindow();
+  gfx::Rect anchor =
+      source->GetBoundsInScreen() +
+      (window->GetTargetBounds().origin() - window->bounds().origin());
+  if (source->border())
+    anchor.Inset(source->border()->GetInsets());
+  return anchor;
+}
+
+gfx::Rect ShelfView::GetTouchMenuAnchorRect(const views::View* source,
+                                            const gfx::Point& location) const {
+  const bool for_item = ShelfItemForView(source);
+  const bool use_touchable_menu_alignment =
+      features::IsTouchableAppContextMenuEnabled() && for_item;
+  const gfx::Rect shelf_bounds =
+      is_overflow_mode()
+          ? owner_overflow_bubble_->bubble_view()->GetBubbleBounds()
+          : screen_util::GetDisplayBoundsWithShelf(
+                shelf_widget_->GetNativeWindow());
+  const gfx::Rect& source_bounds_in_screen = source->GetBoundsInScreen();
+  gfx::Point origin;
+  switch (shelf_->alignment()) {
+    case SHELF_ALIGNMENT_BOTTOM:
+    case SHELF_ALIGNMENT_BOTTOM_LOCKED:
+      origin =
+          gfx::Point(use_touchable_menu_alignment ? source_bounds_in_screen.x()
+                                                  : location.x(),
+                     shelf_bounds.bottom() - kShelfSize);
+      break;
+    case SHELF_ALIGNMENT_LEFT:
+      if (use_touchable_menu_alignment)
+        origin = gfx::Point(shelf_bounds.x(), source_bounds_in_screen.y());
+      else
+        origin = gfx::Point(shelf_bounds.x() + kShelfSize, location.y());
+      break;
+    case SHELF_ALIGNMENT_RIGHT:
+      origin =
+          gfx::Point(shelf_bounds.right() - kShelfSize,
+                     use_touchable_menu_alignment ? source_bounds_in_screen.y()
+                                                  : location.y());
+      break;
+  }
+  if (use_touchable_menu_alignment) {
+    // When showing a context menu with long press, the icon enlarges by 20%
+    // from the center point. After the context menu is shown and the long
+    // press is released the icon will scale back down and the context menu
+    // is left 5px off.
+    origin.Offset(
+        shelf_->IsHorizontalAlignment() ? kScaledIconContextMenuOffset : 0,
+        shelf_->IsHorizontalAlignment() ? 0 : kScaledIconContextMenuOffset);
+  }
+  return gfx::Rect(origin,
+                   for_item ? source_bounds_in_screen.size() : gfx::Size());
+}
+
+views::MenuAnchorPosition ShelfView::GetMenuAnchorPosition(
+    bool for_item,
+    bool context_menu) const {
+  if (features::IsTouchableAppContextMenuEnabled() && for_item) {
+    return shelf_->IsHorizontalAlignment()
+               ? views::MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE
+               : views::MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT;
+  }
+  if (!context_menu) {
+    switch (shelf_->alignment()) {
+      case SHELF_ALIGNMENT_BOTTOM:
+      case SHELF_ALIGNMENT_BOTTOM_LOCKED:
+        return views::MENU_ANCHOR_BUBBLE_ABOVE;
+      case SHELF_ALIGNMENT_LEFT:
+        return views::MENU_ANCHOR_BUBBLE_RIGHT;
+      case SHELF_ALIGNMENT_RIGHT:
+        return views::MENU_ANCHOR_BUBBLE_LEFT;
+    }
+  }
+  return shelf_->IsHorizontalAlignment() ? views::MENU_ANCHOR_FIXED_BOTTOMCENTER
+                                         : views::MENU_ANCHOR_FIXED_SIDECENTER;
+}
+
 gfx::Rect ShelfView::GetBoundsForDragInsertInScreen() {
   gfx::Size preferred_size;
   if (is_overflow_mode()) {
@@ -1839,35 +1932,9 @@
 void ShelfView::ShowContextMenuForView(views::View* source,
                                        const gfx::Point& point,
                                        ui::MenuSourceType source_type) {
-  gfx::Point context_menu_point = point;
-  aura::Window* shelf_window = shelf_widget_->GetNativeWindow();
-
-  // Align the context menu to the edge of the shelf for touch events.
-  if (source_type == ui::MenuSourceType::MENU_SOURCE_TOUCH) {
-    gfx::Rect shelf_bounds =
-        is_overflow_mode()
-            ? owner_overflow_bubble_->bubble_view()->GetBubbleBounds()
-            : screen_util::GetDisplayBoundsWithShelf(shelf_window);
-
-    switch (shelf_->alignment()) {
-      case SHELF_ALIGNMENT_BOTTOM:
-      case SHELF_ALIGNMENT_BOTTOM_LOCKED:
-        context_menu_point.SetPoint(point.x(),
-                                    shelf_bounds.bottom() - kShelfSize);
-        break;
-      case SHELF_ALIGNMENT_LEFT:
-        context_menu_point.SetPoint(shelf_bounds.x() + kShelfSize, point.y());
-        break;
-      case SHELF_ALIGNMENT_RIGHT:
-        context_menu_point.SetPoint(shelf_bounds.right() - kShelfSize,
-                                    point.y());
-        break;
-    }
-  }
   last_pressed_index_ = -1;
-
-  const int64_t display_id = GetDisplayIdForView(this);
   const ShelfItem* item = ShelfItemForView(source);
+  const int64_t display_id = GetDisplayIdForView(this);
   if (!item || !model_->GetShelfItemDelegate(item->id)) {
     UMA_HISTOGRAM_ENUMERATION("Apps.ContextMenuShowSource.Shelf", source_type,
                               ui::MENU_SOURCE_TYPE_LAST);
@@ -1876,8 +1943,7 @@
         std::make_unique<ShelfContextMenuModel>(
             std::vector<mojom::MenuItemPtr>(), nullptr, display_id);
     menu_model->set_histogram_name(kNonAppContextMenuExecuteCommand);
-    ShowMenu(std::move(menu_model), source, context_menu_point, true,
-             source_type, nullptr);
+    ShowMenu(std::move(menu_model), source, point, true, source_type, nullptr);
     return;
   }
 
@@ -1888,8 +1954,8 @@
   // Get any custom entries; show the context menu in AfterGetContextMenuItems.
   model_->GetShelfItemDelegate(item->id)->GetContextMenuItems(
       display_id, base::Bind(&ShelfView::AfterGetContextMenuItems,
-                             weak_factory_.GetWeakPtr(), item->id,
-                             context_menu_point, source, source_type));
+                             weak_factory_.GetWeakPtr(), item->id, point,
+                             source, source_type));
 }
 
 void ShelfView::ShowMenu(std::unique_ptr<ui::MenuModel> menu_model,
@@ -1898,16 +1964,22 @@
                          bool context_menu,
                          ui::MenuSourceType source_type,
                          views::InkDrop* ink_drop) {
-  menu_model_ = std::move(menu_model);
+  if (menu_model->GetItemCount() == 0)
+    return;
 
+  menu_model_ = std::move(menu_model);
   closing_event_time_ = base::TimeTicks();
   int run_types = 0;
   if (context_menu)
     run_types |=
         views::MenuRunner::CONTEXT_MENU | views::MenuRunner::FIXED_ANCHOR;
 
-  // Only selected shelf items with context menu opened can be dragged.
   const ShelfItem* item = ShelfItemForView(source);
+  // Only use the touchable layout if the menu is for an app.
+  if (features::IsTouchableAppContextMenuEnabled() && item)
+    run_types |= views::MenuRunner::USE_TOUCHABLE_LAYOUT;
+
+  // Only selected shelf items with context menu opened can be dragged.
   if (context_menu && item && ShelfButtonIsInDrag(item->type, source) &&
       source_type == ui::MenuSourceType::MENU_SOURCE_TOUCH) {
     run_types |= views::MenuRunner::SEND_GESTURE_EVENTS_TO_OWNER;
@@ -1917,47 +1989,11 @@
       menu_model_.get(), run_types,
       base::Bind(&ShelfView::OnMenuClosed, base::Unretained(this), ink_drop));
 
-  views::MenuAnchorPosition menu_alignment = views::MENU_ANCHOR_TOPLEFT;
-  gfx::Rect anchor = gfx::Rect(click_point, gfx::Size());
-
-  if (!context_menu) {
-    DCHECK(source) << "Application lists require a source button view.";
-    // Application lists use a bubble.
-    // It is possible to invoke the menu while it is sliding into view. To cover
-    // that case, the screen coordinates are offsetted by the animation delta.
-    aura::Window* window = GetWidget()->GetNativeWindow();
-    anchor = source->GetBoundsInScreen() +
-             (window->GetTargetBounds().origin() - window->bounds().origin());
-
-    // Adjust the anchor location for shelf items with asymmetrical borders.
-    if (source->border())
-      anchor.Inset(source->border()->GetInsets());
-
-    // Determine the menu alignment dependent on the shelf.
-    switch (shelf_->alignment()) {
-      case SHELF_ALIGNMENT_BOTTOM:
-      case SHELF_ALIGNMENT_BOTTOM_LOCKED:
-        menu_alignment = views::MENU_ANCHOR_BUBBLE_ABOVE;
-        break;
-      case SHELF_ALIGNMENT_LEFT:
-        menu_alignment = views::MENU_ANCHOR_BUBBLE_RIGHT;
-        break;
-      case SHELF_ALIGNMENT_RIGHT:
-        menu_alignment = views::MENU_ANCHOR_BUBBLE_LEFT;
-        break;
-    }
-  } else {
-    // Distinguish the touch events that triggered on the bottom or left / right
-    // shelf. Since they should have different |MenuAnchorPosition|.
-    if (shelf_->IsHorizontalAlignment())
-      menu_alignment = views::MENU_ANCHOR_FIXED_BOTTOMCENTER;
-    else
-      menu_alignment = views::MENU_ANCHOR_FIXED_SIDECENTER;
-  }
-
   // NOTE: if you convert to HAS_MNEMONICS be sure to update menu building code.
-  launcher_menu_runner_->RunMenuAt(GetWidget(), nullptr, anchor, menu_alignment,
-                                   source_type);
+  launcher_menu_runner_->RunMenuAt(
+      GetWidget(), nullptr,
+      GetMenuAnchorRect(source, click_point, source_type, context_menu),
+      GetMenuAnchorPosition(item, context_menu), source_type);
 }
 
 void ShelfView::OnMenuClosed(views::InkDrop* ink_drop) {
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index da49b56..b529e42 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -24,6 +24,7 @@
 #include "ui/views/animation/ink_drop_state.h"
 #include "ui/views/context_menu_controller.h"
 #include "ui/views/controls/button/button.h"
+#include "ui/views/controls/menu/menu_types.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/view.h"
 #include "ui/views/view_model.h"
@@ -294,6 +295,28 @@
   // Updates the visible range of overflow items in |overflow_view|.
   void UpdateOverflowRange(ShelfView* overflow_view) const;
 
+  // Gets the menu anchor rect for menus. |source| is the view that is
+  // asking for a menu, |location| is the location of the event,
+  // |source_type| is the type of event that asked for the menu, and
+  // |context_menu| is whether the menu is for a context or app menu.
+  gfx::Rect GetMenuAnchorRect(const views::View* source,
+                              const gfx::Point& location,
+                              ui::MenuSourceType source_type,
+                              bool context_menu) const;
+
+  // Gets the menu anchor rect that aligns the menu to the edge of the
+  // shelf for touch events. |source| is the view that is asking for the
+  // menu, |location| is the location of the event.
+  gfx::Rect GetTouchMenuAnchorRect(const views::View* source,
+                                   const gfx::Point& location) const;
+
+  // Gets the menu anchor position for a menu. |for_item| is true if the menu is
+  // for an item on the shelf, or false if the menu is for the shelf view
+  // itself, |context_menu| is whether the menu will be an application menu or
+  // context menu, and |touch_menu| is whether the menu was initiated by touch.
+  views::MenuAnchorPosition GetMenuAnchorPosition(bool for_item,
+                                                  bool context_menu) const;
+
   // Overridden from views::View:
   gfx::Size CalculatePreferredSize() const override;
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
@@ -334,16 +357,6 @@
       ShelfAction action,
       base::Optional<std::vector<mojom::MenuItemPtr>> menu_items);
 
-  // Show a list of all running items for this shelf |item|; it only shows a
-  // menu if there are multiple running items. |source| specifies the view
-  // responsible for showing the menu, and the bubble will point towards it.
-  // The |event_flags| are the flags of the event which triggered this menu.
-  // Returns |true| if a menu is shown.
-  bool ShowListMenuForView(const ShelfItem& item,
-                           views::View* source,
-                           const ui::Event& event,
-                           views::InkDrop* ink_drop);
-
   // Overridden from views::ContextMenuController:
   void ShowContextMenuForView(views::View* source,
                               const gfx::Point& point,
diff --git a/ash/shelf/shelf_view_test_api.cc b/ash/shelf/shelf_view_test_api.cc
index 9c0c037..5f0fda44 100644
--- a/ash/shelf/shelf_view_test_api.cc
+++ b/ash/shelf/shelf_view_test_api.cc
@@ -117,6 +117,14 @@
   shelf_view_->bounds_animator_->RemoveObserver(observer.get());
 }
 
+gfx::Rect ShelfViewTestAPI::GetMenuAnchorRect(const views::View* source,
+                                              const gfx::Point& location,
+                                              ui::MenuSourceType source_type,
+                                              bool context_menu) const {
+  return shelf_view_->GetMenuAnchorRect(source, location, source_type,
+                                        context_menu);
+}
+
 bool ShelfViewTestAPI::CloseMenu() {
   if (!shelf_view_->launcher_menu_runner_)
     return false;
diff --git a/ash/shelf/shelf_view_test_api.h b/ash/shelf/shelf_view_test_api.h
index 97aab42..cc1a63d 100644
--- a/ash/shelf/shelf_view_test_api.h
+++ b/ash/shelf/shelf_view_test_api.h
@@ -7,8 +7,10 @@
 
 #include "ash/public/cpp/shelf_item.h"
 #include "base/macros.h"
+#include "ui/base/ui_base_types.h"
 
 namespace gfx {
+class Point;
 class Rect;
 }
 
@@ -71,6 +73,13 @@
   // Runs message loop and waits until all add/remove animations are done.
   void RunMessageLoopUntilAnimationsDone();
 
+  // Gets the anchor point that would be used for a context menu with these
+  // parameters.
+  gfx::Rect GetMenuAnchorRect(const views::View* source,
+                              const gfx::Point& location,
+                              ui::MenuSourceType source_type,
+                              bool context_menu) const;
+
   // Close any open app list or context menu; returns true if a menu was closed.
   bool CloseMenu();
 
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 868db6e..9bb1803 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -16,6 +16,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
+#include "ash/screen_util.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/app_list_button.h"
 #include "ash/shelf/back_button.h"
@@ -2006,7 +2007,8 @@
   EXPECT_TRUE(test_api_->CloseMenu());
 }
 
-// Tests that the app list button shows a context menu on right click.
+// Tests that the app list button shows a context menu on right click when
+// touchable app context menus are not enabled.
 TEST_F(ShelfViewTest, AppListButtonShowsContextMenu) {
   ui::test::EventGenerator& generator = GetEventGenerator();
   AppListButton* app_list_button = shelf_view_->GetAppListButton();
@@ -2078,6 +2080,94 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfViewTouchableContextMenuTest);
 };
 
+// Tests that anchor points are aligned with the shelf button bounds for touch.
+TEST_F(ShelfViewTouchableContextMenuTest,
+       ShelfViewContextMenuAnchorPointTouch) {
+  const ShelfButton* shelf_button = GetButtonByID(AddApp());
+  EXPECT_EQ(ash::ShelfAlignment::SHELF_ALIGNMENT_BOTTOM,
+            GetPrimaryShelf()->alignment());
+
+  // Test for bottom shelf.
+  EXPECT_EQ(shelf_button->GetBoundsInScreen().y(),
+            test_api_
+                ->GetMenuAnchorRect(shelf_button, gfx::Point(),
+                                    ui::MenuSourceType::MENU_SOURCE_TOUCH,
+                                    true /*context_menu*/)
+                .y());
+
+  // Test for left shelf.
+  GetPrimaryShelf()->SetAlignment(ash::ShelfAlignment::SHELF_ALIGNMENT_LEFT);
+
+  EXPECT_EQ(shelf_button->GetBoundsInScreen().x(),
+            test_api_
+                ->GetMenuAnchorRect(shelf_button, gfx::Point(),
+                                    ui::MenuSourceType::MENU_SOURCE_TOUCH,
+                                    true /*context_menu*/)
+                .x());
+
+  // Test for right shelf.
+  GetPrimaryShelf()->SetAlignment(ash::ShelfAlignment::SHELF_ALIGNMENT_RIGHT);
+
+  EXPECT_EQ(shelf_button->GetBoundsInScreen().x(),
+            test_api_
+                ->GetMenuAnchorRect(shelf_button, gfx::Point(),
+                                    ui::MenuSourceType::MENU_SOURCE_TOUCH,
+                                    true /*context_menu*/)
+                .x());
+}
+
+// Tests that anchor points are the click point for mouse context menus.
+TEST_F(ShelfViewTouchableContextMenuTest, ShelfViewContextMenuAnchorPoint) {
+  const ShelfButton* shelf_button = GetButtonByID(AddApp());
+  EXPECT_EQ(ash::ShelfAlignment::SHELF_ALIGNMENT_BOTTOM,
+            GetPrimaryShelf()->alignment());
+
+  // Test for bottom shelf.
+  const gfx::Point click_point_bottom =
+      shelf_button->GetBoundsInScreen().CenterPoint();
+
+  EXPECT_EQ(click_point_bottom,
+            test_api_
+                ->GetMenuAnchorRect(shelf_button, click_point_bottom,
+                                    ui::MenuSourceType::MENU_SOURCE_MOUSE,
+                                    true /*context_menu*/)
+                .origin());
+
+  // Test for left shelf.
+  GetPrimaryShelf()->SetAlignment(ash::ShelfAlignment::SHELF_ALIGNMENT_LEFT);
+  const gfx::Point click_point_left =
+      shelf_button->GetBoundsInScreen().CenterPoint();
+
+  EXPECT_EQ(click_point_left,
+            test_api_
+                ->GetMenuAnchorRect(shelf_button, click_point_left,
+                                    ui::MenuSourceType::MENU_SOURCE_MOUSE,
+                                    true /*context_menu*/)
+                .origin());
+
+  // Test for right shelf.
+  GetPrimaryShelf()->SetAlignment(ash::ShelfAlignment::SHELF_ALIGNMENT_RIGHT);
+  const gfx::Point click_point_right =
+      shelf_button->GetBoundsInScreen().CenterPoint();
+
+  EXPECT_EQ(click_point_right,
+            test_api_
+                ->GetMenuAnchorRect(shelf_button, click_point_right,
+                                    ui::MenuSourceType::MENU_SOURCE_MOUSE,
+                                    true /*context_menu*/)
+                .origin());
+}
+
+// Tests that the app list button does not show a context menu on right click
+// when touchable app context menus are enabled.
+TEST_F(ShelfViewTouchableContextMenuTest, AppListButtonDoesNotShowContextMenu) {
+  ui::test::EventGenerator& generator = GetEventGenerator();
+  const AppListButton* app_list_button = shelf_view_->GetAppListButton();
+  generator.MoveMouseTo(app_list_button->GetBoundsInScreen().CenterPoint());
+  generator.PressRightButton();
+  EXPECT_FALSE(test_api_->CloseMenu());
+}
+
 // Tests that an item has a notification indicator when it recieves a
 // notification.
 TEST_F(ShelfViewTouchableContextMenuTest, AddedItemHasNotificationIndicator) {
diff --git a/ash/shelf/shelf_window_watcher_item_delegate.cc b/ash/shelf/shelf_window_watcher_item_delegate.cc
index 27f5c7b1..4fa0b6e 100644
--- a/ash/shelf/shelf_window_watcher_item_delegate.cc
+++ b/ash/shelf/shelf_window_watcher_item_delegate.cc
@@ -17,6 +17,7 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/event_constants.h"
 #include "ui/wm/core/window_animations.h"
 
@@ -84,9 +85,11 @@
   close->label = l10n_util::GetStringUTF16(IDS_CLOSE);
   close->enabled = true;
   items.push_back(std::move(close));
-  ash::mojom::MenuItemPtr separator(ash::mojom::MenuItem::New());
-  separator->type = ui::MenuModel::TYPE_SEPARATOR;
-  items.push_back(std::move(separator));
+  if (!features::IsTouchableAppContextMenuEnabled()) {
+    ash::mojom::MenuItemPtr separator(ash::mojom::MenuItem::New());
+    separator->type = ui::MenuModel::TYPE_SEPARATOR;
+    items.push_back(std::move(separator));
+  }
   std::move(callback).Run(std::move(items));
 }
 
diff --git a/ash/system/ime_menu/ime_menu_tray_unittest.cc b/ash/system/ime_menu/ime_menu_tray_unittest.cc
index f0f9a28..b3b4563 100644
--- a/ash/system/ime_menu/ime_menu_tray_unittest.cc
+++ b/ash/system/ime_menu/ime_menu_tray_unittest.cc
@@ -17,16 +17,12 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
-#include "ui/base/ime/chromeos/input_method_manager.h"
-#include "ui/base/ime/chromeos/mock_input_method_manager.h"
 #include "ui/base/ime/ime_bridge.h"
 #include "ui/base/ime/text_input_flags.h"
 #include "ui/events/event.h"
 #include "ui/views/controls/label.h"
 
 using base::UTF8ToUTF16;
-using chromeos::input_method::InputMethodManager;
-using chromeos::input_method::MockInputMethodManager;
 
 namespace ash {
 namespace {
@@ -54,18 +50,6 @@
   ImeMenuTrayTest() = default;
   ~ImeMenuTrayTest() override = default;
 
-  void SetUp() override {
-    AshTestBase::SetUp();
-    // MockInputMethodManager enables emoji, handwriting and voice input by
-    // default.
-    InputMethodManager::Initialize(new MockInputMethodManager);
-  }
-
-  void TearDown() override {
-    InputMethodManager::Shutdown();
-    AshTestBase::TearDown();
-  }
-
  protected:
   // Returns true if the IME menu tray is visible.
   bool IsVisible() { return GetTray()->visible(); }
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 2af1fcd..6450041 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2976,9 +2976,29 @@
 
 fuzzer_test("base_json_correctness_fuzzer") {
   sources = [
-    "json/correctness_fuzzer.cc",
+    "json/json_correctness_fuzzer.cc",
   ]
   deps = [
     ":base",
   ]
+  dict = "//testing/libfuzzer/fuzzers/dicts/json.dict"
+}
+
+fuzzer_test("base_json_reader_fuzzer") {
+  sources = [
+    "json/json_reader_fuzzer.cc",
+  ]
+  deps = [
+    "//base",
+  ]
+  dict = "//testing/libfuzzer/fuzzers/dicts/json.dict"
+}
+
+fuzzer_test("base_json_string_escape_fuzzer") {
+  sources = [
+    "json/string_escape_fuzzer.cc",
+  ]
+  deps = [
+    "//base",
+  ]
 }
diff --git a/base/allocator/partition_allocator/page_allocator_unittest.cc b/base/allocator/partition_allocator/page_allocator_unittest.cc
index 02e4f61..9323d666 100644
--- a/base/allocator/partition_allocator/page_allocator_unittest.cc
+++ b/base/allocator/partition_allocator/page_allocator_unittest.cc
@@ -11,12 +11,12 @@
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) && !defined(OS_FUCHSIA)
 #include <setjmp.h>
 #include <signal.h>
 #include <sys/mman.h>
 #include <sys/time.h>
-#endif  // defined(OS_POSIX)
+#endif  // defined(OS_POSIX) && !defined(OS_FUCHSIA)
 
 #if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
 
@@ -103,7 +103,7 @@
 }
 
 // Test permission setting on POSIX, where we can set a trap handler.
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) && !defined(OS_FUCHSIA)
 
 namespace {
 sigjmp_buf g_continuation;
@@ -186,7 +186,7 @@
   FreePages(buffer, kPageAllocationGranularity);
 }
 
-#endif  // defined(OS_POSIX)
+#endif  // defined(OS_POSIX) && !defined(OS_FUCHSIA)
 
 }  // namespace base
 
diff --git a/base/android/java/src/org/chromium/base/TraceEvent.java b/base/android/java/src/org/chromium/base/TraceEvent.java
index 8795c8c..0710a199 100644
--- a/base/android/java/src/org/chromium/base/TraceEvent.java
+++ b/base/android/java/src/org/chromium/base/TraceEvent.java
@@ -205,9 +205,9 @@
     /**
      * Constructor used to support the "try with resource" construct.
      */
-    private TraceEvent(String name) {
+    private TraceEvent(String name, String arg) {
         mName = name;
-        begin(name);
+        begin(name, arg);
     }
 
     @Override
@@ -221,11 +221,19 @@
      * Note that if tracing is not enabled, this will not result in allocating an object.
      *
      * @param name Trace event name.
+     * @param name The arguments of the event.
      * @return a TraceEvent, or null if tracing is not enabled.
      */
-    public static TraceEvent scoped(String name) {
+    public static TraceEvent scoped(String name, String arg) {
         if (!(EarlyTraceEvent.enabled() || enabled())) return null;
-        return new TraceEvent(name);
+        return new TraceEvent(name, arg);
+    }
+
+    /**
+     * Similar to {@link #scoped(String, String arg)}, but uses null for |arg|.
+     */
+    public static TraceEvent scoped(String name) {
+        return scoped(name, null);
     }
 
     /**
diff --git a/base/json/correctness_fuzzer.cc b/base/json/json_correctness_fuzzer.cc
similarity index 100%
rename from base/json/correctness_fuzzer.cc
rename to base/json/json_correctness_fuzzer.cc
diff --git a/testing/libfuzzer/fuzzers/base_json_reader_fuzzer.cc b/base/json/json_reader_fuzzer.cc
similarity index 92%
rename from testing/libfuzzer/fuzzers/base_json_reader_fuzzer.cc
rename to base/json/json_reader_fuzzer.cc
index afae71f..a8490da 100644
--- a/testing/libfuzzer/fuzzers/base_json_reader_fuzzer.cc
+++ b/base/json/json_reader_fuzzer.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <stddef.h>
-#include <stdint.h>
-
-#include <string>
-
 #include "base/json/json_reader.h"
 #include "base/values.h"
 
@@ -15,7 +10,7 @@
 
 // Entry point for LibFuzzer.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  if (size < 1)
+  if (size < 2)
     return 0;
 
   // Create a copy of input buffer, as otherwise we don't catch
diff --git a/base/json/string_escape_fuzzer.cc b/base/json/string_escape_fuzzer.cc
new file mode 100644
index 0000000..bdd754b
--- /dev/null
+++ b/base/json/string_escape_fuzzer.cc
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/json/string_escape.h"
+
+std::string escaped_string;
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < 2)
+    return 0;
+
+  const bool put_in_quotes = data[size - 1];
+
+  // Create a copy of input buffer, as otherwise we don't catch
+  // overflow that touches the last byte (which is used in put_in_quotes).
+  size_t actual_size_char8 = size - 1;
+  std::unique_ptr<char[]> input(new char[actual_size_char8]);
+  memcpy(input.get(), data, actual_size_char8);
+
+  base::StringPiece input_string(input.get(), actual_size_char8);
+  base::EscapeJSONString(input_string, put_in_quotes, &escaped_string);
+
+  // Test for wide-strings if available size is even.
+  if (actual_size_char8 & 1)
+    return 0;
+
+  size_t actual_size_char16 = actual_size_char8 / 2;
+  base::StringPiece16 input_string16(
+      reinterpret_cast<base::char16*>(input.get()), actual_size_char16);
+  base::EscapeJSONString(input_string16, put_in_quotes, &escaped_string);
+
+  return 0;
+}
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index 71ff0484..45e9f07 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -135,11 +135,6 @@
   // convenience wrapper for CreateProcessMetrics().
   static std::unique_ptr<ProcessMetrics> CreateCurrentProcessMetrics();
 
-  // Returns private and sharedusage, in bytes. Private bytes is the amount of
-  // memory currently allocated to a process that cannot be shared. Returns
-  // false on platform specific error conditions.  Note: |private_bytes|
-  // returns 0 on unsupported OSes: prior to XP SP2.
-  bool GetMemoryBytes(size_t* private_bytes, size_t* shared_bytes) const;
   // Fills a CommittedKBytes with both resident and paged
   // memory usage as per definition of CommittedBytes.
   void GetCommittedKBytes(CommittedKBytes* usage) const;
@@ -175,14 +170,6 @@
     uint64_t compressed = 0;
   };
   TaskVMInfo GetTaskVMInfo() const;
-
-  // Returns private, shared, and total resident bytes. |locked_bytes| refers to
-  // bytes that must stay resident. |locked_bytes| only counts bytes locked by
-  // this task, not bytes locked by the kernel.
-  bool GetMemoryBytes(size_t* private_bytes,
-                      size_t* shared_bytes,
-                      size_t* resident_bytes,
-                      size_t* locked_bytes) const;
 #endif
 
   // Returns the percentage of time spent executing, across all threads of the
diff --git a/base/process/process_metrics_freebsd.cc b/base/process/process_metrics_freebsd.cc
index 1f9e4b6..45385e16 100644
--- a/base/process/process_metrics_freebsd.cc
+++ b/base/process/process_metrics_freebsd.cc
@@ -39,21 +39,6 @@
 }
 }  // namespace
 
-bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
-                                    size_t* shared_bytes) const {
-  WorkingSetKBytes ws_usage;
-  if (!GetWorkingSetKBytes(&ws_usage))
-    return false;
-
-  if (private_bytes)
-    *private_bytes = ws_usage.priv << 10;
-
-  if (shared_bytes)
-    *shared_bytes = ws_usage.shared * 1024;
-
-  return true;
-}
-
 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
 // TODO(bapt) be sure we can't be precise
   size_t priv = GetWorkingSetSize();
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index 27c1496..aec4f625f 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -216,21 +216,6 @@
       getpagesize();
 }
 
-bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
-                                    size_t* shared_bytes) const {
-  WorkingSetKBytes ws_usage;
-  if (!GetWorkingSetKBytes(&ws_usage))
-    return false;
-
-  if (private_bytes)
-    *private_bytes = ws_usage.priv * 1024;
-
-  if (shared_bytes)
-    *shared_bytes = ws_usage.shared * 1024;
-
-  return true;
-}
-
 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
 #if defined(OS_CHROMEOS)
   if (GetWorkingSetKBytesTotmaps(ws_usage))
diff --git a/base/process/process_metrics_mac.cc b/base/process/process_metrics_mac.cc
index 0a8112bd..91e249d 100644
--- a/base/process/process_metrics_mac.cc
+++ b/base/process/process_metrics_mac.cc
@@ -68,33 +68,6 @@
   return kr == KERN_SUCCESS;
 }
 
-bool GetCPUType(cpu_type_t* cpu_type) {
-  size_t len = sizeof(*cpu_type);
-  int result = sysctlbyname("sysctl.proc_cputype",
-                            cpu_type,
-                            &len,
-                            NULL,
-                            0);
-  if (result != 0) {
-    DPLOG(ERROR) << "sysctlbyname(""sysctl.proc_cputype"")";
-    return false;
-  }
-
-  return true;
-}
-
-bool IsAddressInSharedRegion(mach_vm_address_t addr, cpu_type_t type) {
-  if (type == CPU_TYPE_I386) {
-    return addr >= SHARED_REGION_BASE_I386 &&
-           addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386);
-  } else if (type == CPU_TYPE_X86_64) {
-    return addr >= SHARED_REGION_BASE_X86_64 &&
-           addr < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64);
-  } else {
-    return false;
-  }
-}
-
 MachVMRegionResult ParseOutputFromMachVMRegion(kern_return_t kr) {
   if (kr == KERN_INVALID_ADDRESS) {
     // We're at the end of the address space.
@@ -132,121 +105,6 @@
   return WrapUnique(new ProcessMetrics(process, port_provider));
 }
 
-bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
-                                    size_t* shared_bytes) const {
-  return GetMemoryBytes(private_bytes, shared_bytes, nullptr, nullptr);
-}
-
-// This is a rough approximation of the algorithm that libtop uses.
-// private_bytes is the size of private resident memory.
-// shared_bytes is the size of shared resident memory.
-bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
-                                    size_t* shared_bytes,
-                                    size_t* resident_bytes,
-                                    size_t* locked_bytes) const {
-  size_t private_pages_count = 0;
-  size_t shared_pages_count = 0;
-  size_t wired_pages_count = 0;
-
-  mach_port_t task = TaskForPid(process_);
-  if (task == MACH_PORT_NULL) {
-    DLOG(ERROR) << "Invalid process";
-    return false;
-  }
-
-  cpu_type_t cpu_type;
-  if (!GetCPUType(&cpu_type))
-    return false;
-
-  // The same region can be referenced multiple times. To avoid double counting
-  // we need to keep track of which regions we've already counted.
-  hash_set<int> seen_objects;
-
-  // We iterate through each VM region in the task's address map. For shared
-  // memory we add up all the pages that are marked as shared. Like libtop we
-  // try to avoid counting pages that are also referenced by other tasks. Since
-  // we don't have access to the VM regions of other tasks the only hint we have
-  // is if the address is in the shared region area.
-  //
-  // Private memory is much simpler. We simply count the pages that are marked
-  // as private or copy on write (COW).
-  //
-  // See libtop_update_vm_regions in
-  // http://www.opensource.apple.com/source/top/top-67/libtop.c
-  mach_vm_size_t size = 0;
-  mach_vm_address_t address = MACH_VM_MIN_ADDRESS;
-  while (true) {
-    base::CheckedNumeric<mach_vm_address_t> next_address(address);
-    next_address += size;
-    if (!next_address.IsValid())
-      return false;
-    address = next_address.ValueOrDie();
-
-    mach_vm_address_t address_copy = address;
-    vm_region_top_info_data_t info;
-    MachVMRegionResult result = GetTopInfo(task, &size, &address, &info);
-    if (result == MachVMRegionResult::Error)
-      return false;
-    if (result == MachVMRegionResult::Finished)
-      break;
-
-    vm_region_basic_info_64 basic_info;
-    mach_vm_size_t dummy_size = 0;
-    result = GetBasicInfo(task, &dummy_size, &address_copy, &basic_info);
-    if (result == MachVMRegionResult::Error)
-      return false;
-    if (result == MachVMRegionResult::Finished)
-      break;
-
-    bool is_wired = basic_info.user_wired_count > 0;
-
-    if (IsAddressInSharedRegion(address, cpu_type) &&
-        info.share_mode != SM_PRIVATE)
-      continue;
-
-    if (info.share_mode == SM_COW && info.ref_count == 1)
-      info.share_mode = SM_PRIVATE;
-
-    switch (info.share_mode) {
-      case SM_LARGE_PAGE:
-      case SM_PRIVATE:
-        private_pages_count += info.private_pages_resident;
-        private_pages_count += info.shared_pages_resident;
-        break;
-      case SM_COW:
-        private_pages_count += info.private_pages_resident;
-        FALLTHROUGH;
-      case SM_SHARED:
-      case SM_PRIVATE_ALIASED:
-      case SM_TRUESHARED:
-      case SM_SHARED_ALIASED:
-        if (seen_objects.count(info.obj_id) == 0) {
-          // Only count the first reference to this region.
-          seen_objects.insert(info.obj_id);
-          shared_pages_count += info.shared_pages_resident;
-        }
-        break;
-      default:
-        break;
-    }
-    if (is_wired) {
-      wired_pages_count +=
-          info.private_pages_resident + info.shared_pages_resident;
-    }
-  }
-
-  if (private_bytes)
-    *private_bytes = private_pages_count * PAGE_SIZE;
-  if (shared_bytes)
-    *shared_bytes = shared_pages_count * PAGE_SIZE;
-  if (resident_bytes)
-    *resident_bytes = (private_pages_count + shared_pages_count) * PAGE_SIZE;
-  if (locked_bytes)
-    *locked_bytes = wired_pages_count * PAGE_SIZE;
-
-  return true;
-}
-
 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
   WorkingSetKBytes unused;
   if (!GetCommittedAndWorkingSetKBytes(usage, &unused)) {
diff --git a/base/process/process_metrics_openbsd.cc b/base/process/process_metrics_openbsd.cc
index 5a700dd..40d810f 100644
--- a/base/process/process_metrics_openbsd.cc
+++ b/base/process/process_metrics_openbsd.cc
@@ -40,22 +40,6 @@
 }
 }  // namespace
 
-bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
-                                    size_t* shared_bytes) const {
-  WorkingSetKBytes ws_usage;
-
-  if (!GetWorkingSetKBytes(&ws_usage))
-    return false;
-
-  if (private_bytes)
-    *private_bytes = ws_usage.priv << 10;
-
-  if (shared_bytes)
-    *shared_bytes = ws_usage.shared * 1024;
-
-  return true;
-}
-
 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
   // TODO(bapt): be sure we can't be precise
   size_t priv = GetWorkingSetSize();
diff --git a/base/process/process_metrics_unittest.cc b/base/process/process_metrics_unittest.cc
index c622050..cb952950 100644
--- a/base/process/process_metrics_unittest.cc
+++ b/base/process/process_metrics_unittest.cc
@@ -57,44 +57,6 @@
   DISALLOW_COPY_AND_ASSIGN(SystemMetricsTest);
 };
 
-/////////////////////////////////////////////////////////////////////////////
-
-#if defined(OS_MACOSX) && !defined(OS_IOS) && !defined(ADDRESS_SANITIZER)
-TEST_F(SystemMetricsTest, LockedBytes) {
-  ProcessHandle handle = GetCurrentProcessHandle();
-  std::unique_ptr<ProcessMetrics> metrics(
-      ProcessMetrics::CreateProcessMetrics(handle, nullptr));
-
-  size_t initial_locked_bytes;
-  bool result =
-      metrics->GetMemoryBytes(nullptr, nullptr, nullptr, &initial_locked_bytes);
-  ASSERT_TRUE(result);
-
-  size_t size = 8 * 1024 * 1024;
-  std::unique_ptr<char[]> memory(new char[size]);
-  int r = mlock(memory.get(), size);
-  ASSERT_EQ(0, r);
-
-  size_t new_locked_bytes;
-  result =
-      metrics->GetMemoryBytes(nullptr, nullptr, nullptr, &new_locked_bytes);
-  ASSERT_TRUE(result);
-
-  // There should be around |size| more locked bytes, but multi-threading might
-  // cause noise.
-  EXPECT_LT(initial_locked_bytes + size / 2, new_locked_bytes);
-  EXPECT_GT(initial_locked_bytes + size * 1.5, new_locked_bytes);
-
-  r = munlock(memory.get(), size);
-  ASSERT_EQ(0, r);
-
-  result =
-      metrics->GetMemoryBytes(nullptr, nullptr, nullptr, &new_locked_bytes);
-  ASSERT_TRUE(result);
-  EXPECT_EQ(initial_locked_bytes, new_locked_bytes);
-}
-#endif  // defined(OS_MACOSX) && !defined(OS_IOS) && !defined(ADDRESS_SANITIZER)
-
 #if defined(OS_LINUX) || defined(OS_ANDROID)
 TEST_F(SystemMetricsTest, IsValidDiskName) {
   const char invalid_input1[] = "";
diff --git a/base/process/process_metrics_win.cc b/base/process/process_metrics_win.cc
index 6d1c7d5..32b9282 100644
--- a/base/process/process_metrics_win.cc
+++ b/base/process/process_metrics_win.cc
@@ -46,31 +46,6 @@
   return WrapUnique(new ProcessMetrics(process));
 }
 
-bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
-                                    size_t* shared_bytes) const {
-  // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
-  // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
-  // information is simply not available. Hence, we will return 0 on unsupported
-  // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
-  PROCESS_MEMORY_COUNTERS_EX pmcx;
-  if (private_bytes &&
-      GetProcessMemoryInfo(process_.Get(),
-                           reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx),
-                           sizeof(pmcx))) {
-    *private_bytes = pmcx.PrivateUsage;
-  }
-
-  if (shared_bytes) {
-    WorkingSetKBytes ws_usage;
-    if (!GetWorkingSetKBytes(&ws_usage))
-      return false;
-
-    *shared_bytes = ws_usage.shared * 1024;
-  }
-
-  return true;
-}
-
 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
   MEMORY_BASIC_INFORMATION mbi = {0};
   size_t committed_private = 0;
diff --git a/base/strings/string_tokenizer.h b/base/strings/string_tokenizer.h
index 8defbac..03ec687 100644
--- a/base/strings/string_tokenizer.h
+++ b/base/strings/string_tokenizer.h
@@ -147,9 +147,9 @@
   const_iterator token_begin() const { return token_begin_; }
   const_iterator token_end() const { return token_end_; }
   str token() const { return str(token_begin_, token_end_); }
-  base::StringPiece token_piece() const {
-    return base::StringPiece(&*token_begin_,
-                             std::distance(token_begin_, token_end_));
+  BasicStringPiece<str> token_piece() const {
+    return BasicStringPiece<str>(&*token_begin_,
+                                 std::distance(token_begin_, token_end_));
   }
 
  private:
diff --git a/base/trace_event/memory_infra_background_whitelist.cc b/base/trace_event/memory_infra_background_whitelist.cc
index ee0919e1..df33eb4 100644
--- a/base/trace_event/memory_infra_background_whitelist.cc
+++ b/base/trace_event/memory_infra_background_whitelist.cc
@@ -35,6 +35,7 @@
     "gpu::TextureManager",
     "FontCaches",
     "HistoryReport",
+    "IPCChannel",
     "IndexedDBBackingStore",
     "InMemoryURLIndex",
     "JavaHeap",
@@ -47,6 +48,7 @@
     "MojoLevelDB",
     "PartitionAlloc",
     "ProcessMemoryMetrics",
+    "RenderProcessHost",
     "SharedMemoryTracker",
     "Skia",
     "Sql",
@@ -110,6 +112,8 @@
     "mojo/data_pipe_producer",
     "mojo/message_pipe",
     "mojo/platform_handle",
+    "mojo/queued_ipc_channel_message/0x?",
+    "mojo/render_process_host/0x?",
     "mojo/shared_buffer",
     "mojo/unknown",
     "mojo/watcher",
diff --git a/build/android/apk_operations.py b/build/android/apk_operations.py
index 2143003..740ae5c 100755
--- a/build/android/apk_operations.py
+++ b/build/android/apk_operations.py
@@ -447,8 +447,7 @@
       # possibly forked() processes.
       if ':' not in process.name and self._primary_pid is None:
         self._primary_pid = process.pid
-      else:
-        self._my_pids.add(process.pid)
+      self._my_pids.add(process.pid)
 
   def _GetPidStyle(self, pid, dim=False):
     if pid == self._primary_pid:
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index bb3eb22..ebc99b5 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1346,6 +1346,12 @@
         # TODO(hans): https://crbug.com/766891
         "-Wno-null-pointer-arithmetic",
       ]
+      if (llvm_force_head_revision) {
+        cflags += [
+          # TODO(crbug.com/823655): Enable by default in next clang roll.
+          "-Wno-ignored-pragma-optimize",
+        ]
+      }
     } else if (use_xcode_clang) {
       cflags += [
         # TODO(thakis): https://crbug.com/604888
diff --git a/build/config/fuchsia/OWNERS b/build/config/fuchsia/OWNERS
index 3f809e82..e7034ea 100644
--- a/build/config/fuchsia/OWNERS
+++ b/build/config/fuchsia/OWNERS
@@ -1 +1 @@
-scottmg@chromium.org
+file://build/fuchsia/OWNERS
diff --git a/build/config/mac/rules.gni b/build/config/mac/rules.gni
index d5a4561..9872661 100644
--- a/build/config/mac/rules.gni
+++ b/build/config/mac/rules.gni
@@ -125,6 +125,15 @@
 #
 # Arguments
 #
+#     framework_version:
+#         string, version of the framework. Typically this is a
+#         single letter, like "A".
+#
+#     framework_contents:
+#         list of string, top-level items in the framework. This is
+#         the list of symlinks to create in the .framework directory that link
+#         into Versions/Current/.
+#
 #     info_plist:
 #         (optional) string, path to the Info.plist file that will be used for
 #         the bundle.
@@ -138,17 +147,6 @@
 #         (optional) string, name of the generated framework without the
 #         .framework suffix. If omitted, defaults to target_name.
 #
-#     framework_version:
-#         (optional) string, version of the framework. Typically this is a
-#         single letter, like "A". If omitted, the Versions/ subdirectory
-#         structure will not be created, and build output will go directly
-#         into the framework subdirectory.
-#
-#     framework_contents:
-#         (optional) list of string, top-level items in the framework. For
-#         frameworks with a framework_version, this is the list of symlinks to
-#         create in the .framework directory that link into Versions/Current/.
-#
 #     extra_substitutions:
 #         (optional) string array, 'key=value' pairs for extra fields which are
 #         specified in a source Info.plist template.
@@ -222,9 +220,8 @@
 template("mac_framework_bundle") {
   assert(defined(invoker.deps),
          "Dependencies must be specified for $target_name")
-  assert(!defined(invoker.framework_contents) ||
-             defined(invoker.framework_version),
-         "framework_contents requres a versioned framework")
+  assert(invoker.framework_version != "", "framework_version is required")
+  assert(defined(invoker.framework_contents), "framework_contents is required")
 
   _info_plist_target = target_name + "_info_plist"
 
@@ -263,18 +260,11 @@
 
   # Create a file to track the build dependency on the framework_version and
   # framework_contents variables.
-  _framework_toc = []
-  if (defined(invoker.framework_version)) {
-    _framework_toc += [
-      "Version=" + invoker.framework_version,
-      _output_name,
-    ]
-    _framework_contents = [ _output_name ]
-  }
-  if (defined(invoker.framework_contents)) {
-    _framework_toc += invoker.framework_contents
-    _framework_contents += invoker.framework_contents
-  }
+  _framework_toc = [
+                     "Version=" + invoker.framework_version,
+                     _output_name,
+                   ] + invoker.framework_contents
+  _framework_contents = [ _output_name ] + invoker.framework_contents
   _framework_toc_file = "$target_out_dir/${target_name}.toc"
   write_file(_framework_toc_file, _framework_toc)
 
@@ -282,24 +272,16 @@
   _framework_target = _target_name
   _framework_name = _output_name + ".framework"
   _framework_base_dir = "$root_out_dir/$_framework_name"
-  if (defined(invoker.framework_version) && invoker.framework_version != "") {
-    _framework_version = invoker.framework_version
-    _framework_root_dir = _framework_base_dir + "/Versions/$_framework_version"
-  } else {
-    _framework_root_dir = _framework_base_dir
-  }
+  _framework_root_dir =
+      _framework_base_dir + "/Versions/${invoker.framework_version}"
 
   # Clean the entire framework if the framework_version changes.
-  _version_arg = "''"
-  if (defined(invoker.framework_version)) {
-    _version_arg = _framework_version
-  }
   _version_file = "$target_out_dir/${target_name}_version"
   exec_script("//build/config/mac/prepare_framework_version.py",
               [
                 rebase_path(_version_file),
                 rebase_path(_framework_base_dir),
-                _version_arg,
+                invoker.framework_version,
               ])
 
   # Create the symlinks.
@@ -322,22 +304,18 @@
     visibility = [ ":$_framework_target" ]
 
     args = [
-      "--framework",
-      rebase_path(_framework_base_dir, root_build_dir),
-      "--stamp",
-      rebase_path(_stamp_file, root_build_dir),
-    ]
+             "--framework",
+             rebase_path(_framework_base_dir, root_build_dir),
+             "--stamp",
+             rebase_path(_stamp_file, root_build_dir),
+             "--version",
+             invoker.framework_version,
+             "--contents",
+           ] + _framework_contents
 
-    if (defined(invoker.framework_version)) {
-      args += [
-                "--version",
-                invoker.framework_version,
-                "--contents",
-              ] + _framework_contents
-      # It is not possible to list _framework_contents as outputs, since
-      # ninja does not properly stat symbolic links.
-      # https://github.com/ninja-build/ninja/issues/1186
-    }
+    # It is not possible to list _framework_contents as outputs, since
+    # ninja does not properly stat symbolic links.
+    # https://github.com/ninja-build/ninja/issues/1186
   }
 
   _link_shared_library_target = target_name + "_shared_library"
diff --git a/build/fuchsia/update_sdk.py b/build/fuchsia/update_sdk.py
index 61c1c478..cdee484 100755
--- a/build/fuchsia/update_sdk.py
+++ b/build/fuchsia/update_sdk.py
@@ -13,7 +13,7 @@
 import tarfile
 import tempfile
 
-SDK_HASH = '6e46feb3b26db267c65ea0923426a16f4da835bb'
+SDK_HASH = '62b7da622853d3517d79ff76607522f7092989a6'
 
 REPOSITORY_ROOT = os.path.abspath(os.path.join(
     os.path.dirname(__file__), '..', '..'))
diff --git a/cc/blink/web_layer_impl.cc b/cc/blink/web_layer_impl.cc
index df7e0a8a..4b3e0ca 100644
--- a/cc/blink/web_layer_impl.cc
+++ b/cc/blink/web_layer_impl.cc
@@ -460,8 +460,8 @@
   layer_->SetScrollOffsetFromImplSide(offset);
 }
 
-void WebLayerImpl::SetLayerClient(base::WeakPtr<cc::LayerClient> client) {
-  layer_->SetLayerClient(std::move(client));
+void WebLayerImpl::SetLayerClient(cc::LayerClient* client) {
+  layer_->SetLayerClient(client);
 }
 
 const cc::Layer* WebLayerImpl::CcLayer() const {
diff --git a/cc/blink/web_layer_impl.h b/cc/blink/web_layer_impl.h
index 17f2fa34..84e3d2b 100644
--- a/cc/blink/web_layer_impl.h
+++ b/cc/blink/web_layer_impl.h
@@ -122,7 +122,7 @@
       const override;
   void SetScrollClient(blink::WebLayerScrollClient* client) override;
   void SetScrollOffsetFromImplSideForTesting(const gfx::ScrollOffset&) override;
-  void SetLayerClient(base::WeakPtr<cc::LayerClient> client) override;
+  void SetLayerClient(cc::LayerClient* client) override;
   const cc::Layer* CcLayer() const override;
   cc::Layer* CcLayer() override;
   void SetElementId(const cc::ElementId&) override;
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 0a9129af..ad25a15d 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -335,9 +335,7 @@
   TakeDebugInfo();
   virtual void didUpdateMainThreadScrollingReasons();
 
-  void SetLayerClient(base::WeakPtr<LayerClient> client) {
-    inputs_.client = std::move(client);
-  }
+  void SetLayerClient(LayerClient* client) { inputs_.client = client; }
 
   virtual bool IsSnapped();
 
@@ -629,7 +627,7 @@
     bool hide_layer_and_subtree : 1;
 
     // The following elements can not and are not serialized.
-    base::WeakPtr<LayerClient> client;
+    LayerClient* client;
     base::Callback<void(const gfx::ScrollOffset&, const ElementId&)>
         did_scroll_callback;
     std::vector<std::unique_ptr<viz::CopyOutputRequest>> copy_requests;
diff --git a/cc/paint/display_item_list.h b/cc/paint/display_item_list.h
index 273caf3..a7a211a 100644
--- a/cc/paint/display_item_list.h
+++ b/cc/paint/display_item_list.h
@@ -68,14 +68,26 @@
 
   // Push functions construct a new op on the paint op buffer, while maintaining
   // bookkeeping information. Must be called after invoking StartPaint().
+  // Returns the id (which is an opaque value) of the operation that can be used
+  // in UpdateSaveLayerBounds().
   template <typename T, typename... Args>
-  void push(Args&&... args) {
+  size_t push(Args&&... args) {
 #if DCHECK_IS_ON()
     DCHECK(IsPainting());
 #endif
+    size_t offset = paint_op_buffer_.next_op_offset();
     if (usage_hint_ == kTopLevelDisplayItemList)
-      offsets_.push_back(paint_op_buffer_.next_op_offset());
+      offsets_.push_back(offset);
     paint_op_buffer_.push<T>(std::forward<Args>(args)...);
+    return offset;
+  }
+
+  // Called by blink::PaintChunksToCcLayer when an effect ends, to update the
+  // bounds of a SaveLayerOp which was emitted when the effect started. This is
+  // needed because blink doesn't know the bounds when an effect starts. Don't
+  // add other mutation methods like this if there is better alternative.
+  void UpdateSaveLayerBounds(size_t id, const SkRect& bounds) {
+    paint_op_buffer_.UpdateSaveLayerBounds(id, bounds);
   }
 
   void EndPaintOfUnpaired(const gfx::Rect& visual_rect) {
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 864a5f3..3ec40ab 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -269,6 +269,10 @@
   return "UNKNOWN";
 }
 
+std::ostream& operator<<(std::ostream& os, PaintOpType type) {
+  return os << PaintOpTypeToString(type);
+}
+
 template <typename T>
 size_t SimpleSerialize(const PaintOp* op, void* memory, size_t size) {
   if (sizeof(T) > size)
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index 122730b8..26ca2e30 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -90,6 +90,7 @@
 };
 
 CC_PAINT_EXPORT std::string PaintOpTypeToString(PaintOpType type);
+CC_PAINT_EXPORT std::ostream& operator<<(std::ostream&, PaintOpType);
 
 struct CC_PAINT_EXPORT PlaybackParams {
   using CustomDataRasterCallback =
@@ -893,6 +894,18 @@
     AnalyzeAddedOp(op);
   }
 
+  void UpdateSaveLayerBounds(size_t offset, const SkRect& bounds) {
+    CHECK_LT(offset, used_);
+    CHECK_LE(offset + sizeof(SaveLayerOp), used_);
+
+    auto* op = reinterpret_cast<SaveLayerOp*>(data_.get() + offset);
+    CHECK_EQ(op->type, static_cast<uint32_t>(SaveLayerOp::kType));
+    size_t skip = op->skip;
+    DCHECK_EQ(skip, ComputeOpSkip(sizeof(SaveLayerOp)));
+
+    op->bounds = bounds;
+  }
+
   template <typename T>
   void AnalyzeAddedOp(const T* op) {
     static_assert(!std::is_same<T, PaintOp>::value,
@@ -909,6 +922,16 @@
     subrecord_bytes_used_ += op->AdditionalBytesUsed();
   }
 
+  template <typename T>
+  const T* GetOpAtForTesting(size_t index) const {
+    size_t i = 0;
+    for (PaintOpBuffer::Iterator it(this); it && i <= index; ++it, ++i) {
+      if (i == index && (*it)->GetType() == T::kType)
+        return static_cast<const T*>(*it);
+    }
+    return nullptr;
+  }
+
   class CC_PAINT_EXPORT Iterator {
    public:
     explicit Iterator(const PaintOpBuffer* buffer)
diff --git a/cc/test/fake_ui_resource_layer_tree_host_impl.cc b/cc/test/fake_ui_resource_layer_tree_host_impl.cc
index f1b602ed..2b80fc0 100644
--- a/cc/test/fake_ui_resource_layer_tree_host_impl.cc
+++ b/cc/test/fake_ui_resource_layer_tree_host_impl.cc
@@ -23,12 +23,14 @@
     DeleteUIResource(uid);
 
   UIResourceData data;
-  data.resource_id = resource_provider()->CreateGpuTextureResource(
-      bitmap.GetSize(), viz::ResourceTextureHint::kDefault, viz::RGBA_8888,
-      gfx::ColorSpace());
+
+  data.resource_id_for_export = resource_provider()->ImportResource(
+      viz::TransferableResource::MakeGL(gpu::Mailbox::Generate(), GL_LINEAR,
+                                        GL_TEXTURE_2D, gpu::SyncToken()),
+      viz::SingleReleaseCallback::Create(base::DoNothing()));
 
   data.opaque = bitmap.GetOpaque();
-  fake_ui_resource_map_[uid] = data;
+  fake_ui_resource_map_[uid] = std::move(data);
 }
 
 void FakeUIResourceLayerTreeHostImpl::DeleteUIResource(UIResourceId uid) {
@@ -39,15 +41,15 @@
 
 viz::ResourceId FakeUIResourceLayerTreeHostImpl::ResourceIdForUIResource(
     UIResourceId uid) const {
-  UIResourceMap::const_iterator iter = fake_ui_resource_map_.find(uid);
+  auto iter = fake_ui_resource_map_.find(uid);
   if (iter != fake_ui_resource_map_.end())
-    return iter->second.resource_id;
-  return 0;
+    return iter->second.resource_id_for_export;
+  return viz::kInvalidResourceId;
 }
 
 bool FakeUIResourceLayerTreeHostImpl::IsUIResourceOpaque(UIResourceId uid)
     const {
-  UIResourceMap::const_iterator iter = fake_ui_resource_map_.find(uid);
+  auto iter = fake_ui_resource_map_.find(uid);
   DCHECK(iter != fake_ui_resource_map_.end());
   return iter->second.opaque;
 }
diff --git a/cc/test/fake_ui_resource_layer_tree_host_impl.h b/cc/test/fake_ui_resource_layer_tree_host_impl.h
index bfd77f9..45e89e7 100644
--- a/cc/test/fake_ui_resource_layer_tree_host_impl.h
+++ b/cc/test/fake_ui_resource_layer_tree_host_impl.h
@@ -29,9 +29,8 @@
   bool IsUIResourceOpaque(UIResourceId uid) const override;
 
  private:
-  using UIResourceMap =
-      std::unordered_map<UIResourceId, LayerTreeHostImpl::UIResourceData>;
-  UIResourceMap fake_ui_resource_map_;
+  std::unordered_map<UIResourceId, LayerTreeHostImpl::UIResourceData>
+      fake_ui_resource_map_;
 };
 
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 8c4d640..7d6ab52e 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -80,6 +80,7 @@
 #include "cc/trees/tree_synchronizer.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/delay_based_time_source.h"
+#include "components/viz/common/gpu/texture_allocation.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/compositor_frame_metadata.h"
 #include "components/viz/common/quads/frame_deadline.h"
@@ -87,6 +88,7 @@
 #include "components/viz/common/quads/shared_quad_state.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/common/resources/bitmap_allocation.h"
 #include "components/viz/common/traced_value.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/context_support.h"
@@ -99,6 +101,7 @@
 #include "ui/gfx/geometry/scroll_offset.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/geometry/vector2d_conversions.h"
+#include "ui/gfx/skia_util.h"
 
 namespace cc {
 namespace {
@@ -175,12 +178,14 @@
     ImageInvalidationUpdateDurationHistogramTimer,
     "Scheduling.%s.ImageInvalidationUpdateDuration");
 
-LayerTreeHostImpl::FrameData::FrameData()
-    : render_surface_list(nullptr),
-      has_no_damage(false),
-      may_contain_video(false) {}
-
+LayerTreeHostImpl::FrameData::FrameData() = default;
 LayerTreeHostImpl::FrameData::~FrameData() = default;
+LayerTreeHostImpl::UIResourceData::UIResourceData() = default;
+LayerTreeHostImpl::UIResourceData::~UIResourceData() = default;
+LayerTreeHostImpl::UIResourceData::UIResourceData(UIResourceData&&) noexcept =
+    default;
+LayerTreeHostImpl::UIResourceData& LayerTreeHostImpl::UIResourceData::operator=(
+    UIResourceData&&) = default;
 
 std::unique_ptr<LayerTreeHostImpl> LayerTreeHostImpl::Create(
     const LayerTreeSettings& settings,
@@ -1842,6 +1847,7 @@
   RenderFrameMetadata metadata;
   metadata.root_scroll_offset =
       gfx::ScrollOffsetToVector2dF(active_tree_->TotalScrollOffset());
+  metadata.root_background_color = active_tree_->background_color();
   return metadata;
 }
 
@@ -4472,6 +4478,8 @@
   const gfx::Size source_size = bitmap.GetSize();
   gfx::Size upload_size = bitmap.GetSize();
   bool scaled = false;
+  // UIResources are assumed to be rastered in SRGB.
+  const gfx::ColorSpace& color_space = gfx::ColorSpace::CreateSRGB();
 
   int max_texture_size = resource_provider_->max_texture_size();
   if (source_size.width() > max_texture_size ||
@@ -4484,18 +4492,48 @@
     upload_size = gfx::ScaleToCeiledSize(source_size, scale, scale);
   }
 
+  // For gpu compositing, a texture will be allocated and the UIResource
+  // will be uploaded into it.
+  viz::TextureAllocation texture_alloc;
+  // For software compositing, shared memory will be allocated and the
+  // UIResource will be copied into it.
+  std::unique_ptr<base::SharedMemory> shared_memory;
+  viz::SharedBitmapId shared_bitmap_id;
+
   if (layer_tree_frame_sink_->context_provider()) {
-    id = resource_provider_->CreateGpuTextureResource(
-        upload_size, viz::ResourceTextureHint::kDefault, format,
-        gfx::ColorSpace::CreateSRGB());
+    viz::ContextProvider* context_provider =
+        layer_tree_frame_sink_->context_provider();
+    texture_alloc = viz::TextureAllocation::MakeTextureId(
+        context_provider->ContextGL(), context_provider->ContextCapabilities(),
+        format, settings_.resource_settings.use_gpu_memory_buffer_resources,
+        /*for_framebuffer_attachment=*/false);
   } else {
-    DCHECK_EQ(format, viz::RGBA_8888);
-    id = resource_provider_->CreateBitmapResource(
-        upload_size, gfx::ColorSpace::CreateSRGB(), viz::RGBA_8888);
+    shared_memory =
+        viz::bitmap_allocation::AllocateMappedBitmap(upload_size, format);
+    shared_bitmap_id = viz::SharedBitmap::GenerateId();
   }
 
   if (!scaled) {
-    resource_provider_->CopyToResource(id, bitmap.GetPixels(), source_size);
+    // If not scaled, we can copy the pixels 1:1 from the source bitmap to our
+    // destination backing of a texture or shared bitmap.
+    if (layer_tree_frame_sink_->context_provider()) {
+      viz::TextureAllocation::UploadStorage(
+          layer_tree_frame_sink_->context_provider()->ContextGL(),
+          layer_tree_frame_sink_->context_provider()->ContextCapabilities(),
+          format, upload_size, texture_alloc, color_space, bitmap.GetPixels());
+    } else {
+      DCHECK_EQ(bitmap.GetFormat(), UIResourceBitmap::RGBA8);
+      SkImageInfo src_info =
+          SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(source_size));
+      SkImageInfo dst_info =
+          SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(upload_size));
+
+      sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(
+          dst_info, shared_memory->memory(), dst_info.minRowBytes());
+      surface->getCanvas()->writePixels(
+          src_info, const_cast<uint8_t*>(bitmap.GetPixels()),
+          src_info.minRowBytes(), 0, 0);
+    }
   } else {
     // Only support auto-resizing for N32 textures (since this is primarily for
     // scrollbars). Users of other types need to ensure they are not too big.
@@ -4506,57 +4544,170 @@
     float canvas_scale_y =
         upload_size.height() / static_cast<float>(source_size.height());
 
-    // Uses kPremul_SkAlphaType since that is what SkBitmap's allocN32Pixels
-    // makes, and we only support the RGBA8 format here.
-    SkImageInfo info = SkImageInfo::MakeN32(
-        source_size.width(), source_size.height(), kPremul_SkAlphaType);
-    int row_bytes = source_size.width() * 4;
+    // Uses N32Premul since that is what SkBitmap's allocN32Pixels makes, and we
+    // only support the RGBA8 format here.
+    SkImageInfo info =
+        SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(source_size));
 
     SkBitmap source_bitmap;
-    source_bitmap.setInfo(info, row_bytes);
+    source_bitmap.setInfo(info);
     source_bitmap.setPixels(const_cast<uint8_t*>(bitmap.GetPixels()));
 
-    // This applies the scale to draw the |bitmap| into |scaled_bitmap|.
-    SkBitmap scaled_bitmap;
-    scaled_bitmap.allocN32Pixels(upload_size.width(), upload_size.height());
-    SkCanvas scaled_canvas(scaled_bitmap);
-    scaled_canvas.scale(canvas_scale_x, canvas_scale_y);
+    // This applies the scale to draw the |bitmap| into |scaled_surface|. For
+    // gpu compositing, we scale into a software bitmap-backed SkSurface here,
+    // then upload from there into a texture. For software compositing, we scale
+    // directly into the shared memory backing.
+    sk_sp<SkSurface> scaled_surface;
+    if (layer_tree_frame_sink_->context_provider()) {
+      scaled_surface = SkSurface::MakeRasterN32Premul(upload_size.width(),
+                                                      upload_size.height());
+    } else {
+      SkImageInfo dst_info =
+          SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(upload_size));
+      scaled_surface = SkSurface::MakeRasterDirect(
+          dst_info, shared_memory->memory(), dst_info.minRowBytes());
+    }
+    SkCanvas* scaled_canvas = scaled_surface->getCanvas();
+    scaled_canvas->scale(canvas_scale_x, canvas_scale_y);
     // The |canvas_scale_x| and |canvas_scale_y| may have some floating point
     // error for large enough values, causing pixels on the edge to be not
     // fully filled by drawBitmap(), so we ensure they start empty. (See
     // crbug.com/642011 for an example.)
-    scaled_canvas.clear(SK_ColorTRANSPARENT);
-    scaled_canvas.drawBitmap(source_bitmap, 0, 0);
+    scaled_canvas->clear(SK_ColorTRANSPARENT);
+    scaled_canvas->drawBitmap(source_bitmap, 0, 0);
 
-    auto* pixels = static_cast<uint8_t*>(scaled_bitmap.getPixels());
-    resource_provider_->CopyToResource(id, pixels, upload_size);
+    if (layer_tree_frame_sink_->context_provider()) {
+      SkPixmap pixmap;
+      scaled_surface->peekPixels(&pixmap);
+      viz::TextureAllocation::UploadStorage(
+          layer_tree_frame_sink_->context_provider()->ContextGL(),
+          layer_tree_frame_sink_->context_provider()->ContextCapabilities(),
+          format, upload_size, texture_alloc, color_space, pixmap.addr());
+    }
   }
 
+  // Once the backing has the UIResource inside it, we have to prepare it for
+  // export to the display compositor via ImportResource(). For gpu compositing,
+  // this requires a Mailbox+SyncToken as well. For software compositing, the
+  // SharedBitmapId must be notified to the LayerTreeFrameSink. The
+  // OnUIResourceReleased() method will be called once the resource is deleted
+  // and the display compositor is no longer using it, to free the memory
+  // allocated in this method above.
+  viz::TransferableResource transferable;
+  if (layer_tree_frame_sink_->context_provider()) {
+    gpu::gles2::GLES2Interface* gl =
+        layer_tree_frame_sink_->context_provider()->ContextGL();
+    gpu::Mailbox mailbox = gpu::Mailbox::Generate();
+    gl->ProduceTextureDirectCHROMIUM(texture_alloc.texture_id, mailbox.name);
+    gpu::SyncToken sync_token =
+        LayerTreeResourceProvider::GenerateSyncTokenHelper(gl);
+
+    transferable = viz::TransferableResource::MakeGLOverlay(
+        mailbox, GL_LINEAR, texture_alloc.texture_target, sync_token,
+        upload_size, texture_alloc.overlay_candidate);
+    transferable.format = format;
+    transferable.buffer_format = viz::BufferFormat(format);
+  } else {
+    mojo::ScopedSharedBufferHandle memory_handle =
+        viz::bitmap_allocation::DuplicateAndCloseMappedBitmap(
+            shared_memory.get(), upload_size, format);
+    layer_tree_frame_sink_->DidAllocateSharedBitmap(std::move(memory_handle),
+                                                    shared_bitmap_id);
+    transferable = viz::TransferableResource::MakeSoftware(shared_bitmap_id, 0,
+                                                           upload_size, format);
+  }
+  transferable.color_space = color_space;
+  id = resource_provider_->ImportResource(
+      transferable,
+      // The OnUIResourceReleased method is bound with a WeakPtr, but the
+      // resource backing will be deleted when the LayerTreeFrameSink is
+      // removed before shutdown, so nothing leaks if the WeakPtr is
+      // invalidated.
+      viz::SingleReleaseCallback::Create(base::BindOnce(
+          &LayerTreeHostImpl::OnUIResourceReleased, AsWeakPtr(), uid)));
+
   UIResourceData data;
-  data.resource_id = id;
   data.opaque = bitmap.GetOpaque();
-  ui_resource_map_[uid] = data;
+  data.format = format;
+  data.shared_bitmap_id = shared_bitmap_id;
+  data.shared_memory = std::move(shared_memory);
+  data.texture_id = texture_alloc.texture_id;
+  data.resource_id_for_export = id;
+  ui_resource_map_[uid] = std::move(data);
 
   MarkUIResourceNotEvicted(uid);
 }
 
 void LayerTreeHostImpl::DeleteUIResource(UIResourceId uid) {
-  viz::ResourceId id = ResourceIdForUIResource(uid);
-  if (id) {
-    if (has_valid_layer_tree_frame_sink_)
-      resource_provider_->DeleteResource(id);
-    ui_resource_map_.erase(uid);
+  auto it = ui_resource_map_.find(uid);
+  if (it != ui_resource_map_.end()) {
+    UIResourceData& data = it->second;
+    viz::ResourceId id = data.resource_id_for_export;
+    // Move the |data| to |deleted_ui_resources_| before removing it from the
+    // LayerTreeResourceProvider, so that the ReleaseCallback can see it there.
+    deleted_ui_resources_[uid] = std::move(data);
+    ui_resource_map_.erase(it);
+
+    resource_provider_->RemoveImportedResource(id);
   }
   MarkUIResourceNotEvicted(uid);
 }
 
+void LayerTreeHostImpl::DeleteUIResourceBacking(
+    UIResourceData data,
+    const gpu::SyncToken& sync_token) {
+  // Resources are either software or gpu backed, not both.
+  DCHECK(!(data.shared_memory && data.texture_id));
+  if (data.shared_memory)
+    layer_tree_frame_sink_->DidDeleteSharedBitmap(data.shared_bitmap_id);
+  if (data.texture_id) {
+    gpu::gles2::GLES2Interface* gl =
+        layer_tree_frame_sink_->context_provider()->ContextGL();
+    if (sync_token.HasData())
+      gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
+    gl->DeleteTextures(1, &data.texture_id);
+  }
+  // |data| goes out of scope and deletes anything it owned.
+}
+
+void LayerTreeHostImpl::OnUIResourceReleased(UIResourceId uid,
+                                             const gpu::SyncToken& sync_token,
+                                             bool lost) {
+  auto it = deleted_ui_resources_.find(uid);
+  if (it == deleted_ui_resources_.end()) {
+    // Backing was already deleted, eg if the context was lost.
+    return;
+  }
+  UIResourceData& data = it->second;
+  // We don't recycle backings here, so |lost| is not relevant, we always delete
+  // them.
+  DeleteUIResourceBacking(std::move(data), sync_token);
+  deleted_ui_resources_.erase(it);
+}
+
 void LayerTreeHostImpl::ClearUIResources() {
-  for (UIResourceMap::const_iterator iter = ui_resource_map_.begin();
-       iter != ui_resource_map_.end(); ++iter) {
-    evicted_ui_resources_.insert(iter->first);
-    resource_provider_->DeleteResource(iter->second.resource_id);
+  for (auto& pair : ui_resource_map_) {
+    UIResourceId uid = pair.first;
+    UIResourceData& data = pair.second;
+    resource_provider_->RemoveImportedResource(data.resource_id_for_export);
+    // Immediately drop the backing instead of waiting for the resource to be
+    // returned from the ResourceProvider, as this is called in cases where the
+    // ability to clean up the backings will go away (context loss, shutdown).
+    DeleteUIResourceBacking(std::move(data), gpu::SyncToken());
+    // This resource is not deleted, and its |uid| is still valid, so it moves
+    // to the evicted list, not the |deleted_ui_resources_| set. Also, its
+    // backing is gone, so it would not belong in |deleted_ui_resources_|.
+    evicted_ui_resources_.insert(uid);
   }
   ui_resource_map_.clear();
+  for (auto& pair : deleted_ui_resources_) {
+    UIResourceData& data = pair.second;
+    // Immediately drop the backing instead of waiting for the resource to be
+    // returned from the ResourceProvider, as this is called in cases where the
+    // ability to clean up the backings will go away (context loss, shutdown).
+    DeleteUIResourceBacking(std::move(data), gpu::SyncToken());
+  }
+  deleted_ui_resources_.clear();
 }
 
 void LayerTreeHostImpl::EvictAllUIResources() {
@@ -4571,14 +4722,14 @@
 
 viz::ResourceId LayerTreeHostImpl::ResourceIdForUIResource(
     UIResourceId uid) const {
-  UIResourceMap::const_iterator iter = ui_resource_map_.find(uid);
+  auto iter = ui_resource_map_.find(uid);
   if (iter != ui_resource_map_.end())
-    return iter->second.resource_id;
-  return 0;
+    return iter->second.resource_id_for_export;
+  return viz::kInvalidResourceId;
 }
 
 bool LayerTreeHostImpl::IsUIResourceOpaque(UIResourceId uid) const {
-  UIResourceMap::const_iterator iter = ui_resource_map_.find(uid);
+  auto iter = ui_resource_map_.find(uid);
   DCHECK(iter != ui_resource_map_.end());
   return iter->second.opaque;
 }
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 20af257..a5f0fe3 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -166,6 +166,56 @@
       public MutatorHostClient,
       public base::SupportsWeakPtr<LayerTreeHostImpl> {
  public:
+  // This structure is used to build all the state required for producing a
+  // single CompositorFrame. The |render_passes| list becomes the set of
+  // RenderPasses in the quad, and the other fields are used for computation
+  // or become part of the CompositorFrameMetadata.
+  struct CC_EXPORT FrameData {
+    FrameData();
+    ~FrameData();
+    void AsValueInto(base::trace_event::TracedValue* value) const;
+
+    std::vector<viz::SurfaceId> activation_dependencies;
+    base::Optional<uint32_t> deadline_in_frames;
+    bool use_default_lower_bound_deadline = false;
+    std::vector<gfx::Rect> occluding_screen_space_rects;
+    std::vector<gfx::Rect> non_occluding_screen_space_rects;
+    viz::RenderPassList render_passes;
+    const RenderSurfaceList* render_surface_list = nullptr;
+    LayerImplList will_draw_layers;
+    bool has_no_damage = false;
+    bool may_contain_video = false;
+    viz::BeginFrameAck begin_frame_ack;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(FrameData);
+  };
+
+  // A struct of data for a single UIResource, including the backing
+  // pixels, and metadata about it.
+  struct CC_EXPORT UIResourceData {
+    UIResourceData();
+    ~UIResourceData();
+    UIResourceData(UIResourceData&&) noexcept;
+    UIResourceData& operator=(UIResourceData&&);
+
+    bool opaque;
+    viz::ResourceFormat format;
+
+    // Backing for software compositing.
+    viz::SharedBitmapId shared_bitmap_id;
+    std::unique_ptr<base::SharedMemory> shared_memory;
+    // Backing for gpu compositing.
+    uint32_t texture_id;
+
+    // The name with which to refer to the resource in frames submitted to the
+    // display compositor.
+    viz::ResourceId resource_id_for_export;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(UIResourceData);
+  };
+
   static std::unique_ptr<LayerTreeHostImpl> Create(
       const LayerTreeSettings& settings,
       LayerTreeHostImplClient* client,
@@ -245,27 +295,6 @@
     resourceless_software_draw_ = true;
   }
 
-  struct CC_EXPORT FrameData {
-    FrameData();
-    ~FrameData();
-    void AsValueInto(base::trace_event::TracedValue* value) const;
-
-    std::vector<viz::SurfaceId> activation_dependencies;
-    base::Optional<uint32_t> deadline_in_frames;
-    bool use_default_lower_bound_deadline = false;
-    std::vector<gfx::Rect> occluding_screen_space_rects;
-    std::vector<gfx::Rect> non_occluding_screen_space_rects;
-    viz::RenderPassList render_passes;
-    const RenderSurfaceList* render_surface_list;
-    LayerImplList will_draw_layers;
-    bool has_no_damage;
-    bool may_contain_video;
-    viz::BeginFrameAck begin_frame_ack;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(FrameData);
-  };
-
   virtual void DidSendBeginMainFrame() {}
   virtual void BeginMainFrameAborted(
       CommitEarlyOutReason reason,
@@ -567,11 +596,6 @@
 
   virtual bool IsUIResourceOpaque(UIResourceId uid) const;
 
-  struct UIResourceData {
-    viz::ResourceId resource_id;
-    bool opaque;
-  };
-
   // Returns the amount of delta that can be applied to scroll_node, taking
   // page scale into account.
   gfx::Vector2dF ComputeScrollDelta(const ScrollNode& scroll_node,
@@ -758,8 +782,22 @@
   void StartScrollbarFadeRecursive(LayerImpl* layer);
   void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy);
 
+  // Once a resource is uploaded or deleted, it is no longer an evicted id, this
+  // removes it from the evicted set, and updates if we're able to draw now that
+  // all UIResources are valid.
   void MarkUIResourceNotEvicted(UIResourceId uid);
+  // Deletes all UIResource backings, and marks all the ids as evicted.
   void ClearUIResources();
+  // Frees the textures/bitmaps backing the UIResource, held in the
+  // UIResourceData.
+  void DeleteUIResourceBacking(UIResourceData data,
+                               const gpu::SyncToken& sync_token);
+  // Callback for when a UIResource is deleted *and* no longer in use by the
+  // display compositor. It will DeleteUIResourceBacking() if the backing was
+  // not already deleted preemptively.
+  void OnUIResourceReleased(UIResourceId uid,
+                            const gpu::SyncToken& sync_token,
+                            bool lost);
 
   void NotifySwapPromiseMonitorsOfSetNeedsRedraw();
   void NotifySwapPromiseMonitorsOfForwardingToMainThread();
@@ -803,12 +841,14 @@
   // active tree.
   void ActivateStateForImages();
 
-  using UIResourceMap = std::unordered_map<UIResourceId, UIResourceData>;
-  UIResourceMap ui_resource_map_;
-
+  std::unordered_map<UIResourceId, UIResourceData> ui_resource_map_;
+  // UIResources are held here once requested to be deleted until they are
+  // released from the display compositor, then the backing can be deleted.
+  std::unordered_map<UIResourceId, UIResourceData> deleted_ui_resources_;
   // Resources that were evicted by EvictAllUIResources. Resources are removed
   // from this when they are touched by a create or destroy from the UI resource
-  // request queue.
+  // request queue. The resource IDs held in here do not have any backing
+  // associated with them anymore, as that is freed at the time of eviction.
   std::set<UIResourceId> evicted_ui_resources_;
 
   LayerTreeFrameSink* layer_tree_frame_sink_;
diff --git a/cc/trees/render_frame_metadata.cc b/cc/trees/render_frame_metadata.cc
index fd3a1b9..8d98106 100644
--- a/cc/trees/render_frame_metadata.cc
+++ b/cc/trees/render_frame_metadata.cc
@@ -15,11 +15,11 @@
 
 RenderFrameMetadata::~RenderFrameMetadata() {}
 
+// static
 bool RenderFrameMetadata::HasAlwaysUpdateMetadataChanged(
     const RenderFrameMetadata& rfm1,
     const RenderFrameMetadata& rfm2) {
-  // TODO(jonross): as low frequency fields are added, update this method.
-  return false;
+  return rfm1.root_background_color != rfm2.root_background_color;
 }
 
 RenderFrameMetadata& RenderFrameMetadata::operator=(
@@ -29,7 +29,8 @@
     RenderFrameMetadata&& other) = default;
 
 bool RenderFrameMetadata::operator==(const RenderFrameMetadata& other) {
-  return root_scroll_offset == other.root_scroll_offset;
+  return root_scroll_offset == other.root_scroll_offset &&
+         root_background_color == other.root_background_color;
 }
 
 bool RenderFrameMetadata::operator!=(const RenderFrameMetadata& other) {
diff --git a/cc/trees/render_frame_metadata.h b/cc/trees/render_frame_metadata.h
index a733fe6d..c31ea7b 100644
--- a/cc/trees/render_frame_metadata.h
+++ b/cc/trees/render_frame_metadata.h
@@ -7,6 +7,7 @@
 
 #include "base/optional.h"
 #include "cc/cc_export.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/vector2d_f.h"
 
 namespace cc {
@@ -29,6 +30,11 @@
   bool operator==(const RenderFrameMetadata& other);
   bool operator!=(const RenderFrameMetadata& other);
 
+  // The background color of a CompositorFrame. It can be used for filling the
+  // content area if the primary surface is unavailable and fallback is not
+  // specified.
+  SkColor root_background_color = SK_ColorWHITE;
+
   // Scroll offset of the root layer. This optional parameter is only valid
   // during tests.
   base::Optional<gfx::Vector2dF> root_scroll_offset;
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 4ab1fa9b..d7be7791 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -315,7 +315,7 @@
 
       data_deps += [
         "//chrome/browser/resources/media/mei_preload:component",
-        "//third_party/widevine/cdm:widevinecdmadapter",
+        "//third_party/widevine/cdm",
       ]
 
       if (is_multi_dll_chrome) {
@@ -766,9 +766,7 @@
       ":verify_chrome_framework_order",
     ]
 
-    # Only official builds that include Widevine need the widevine
-    # signature file.
-    if (is_chrome_branded && enable_library_cdms) {
+    if (enable_widevine_cdm_host_verification) {
       sources += [ "$root_out_dir/Widevine Resources.bundle" ]
       public_deps += [ ":widevine_resources_bundle" ]
     }
@@ -1046,52 +1044,20 @@
     }
   }
 
-  _should_bundle_widevine =
-      (is_chrome_branded || enable_widevine) && enable_library_cdms
-  if (_should_bundle_widevine) {
-    # The Widevine CDM and manifest are either the actual Widevine CDM and
-    # manifest or stubs used for testing only. The choice is made within the
-    # corresponding Widevine targets based on branding.
+  if (should_bundle_widevine_cdm) {
     bundle_data("widevine_cdm_library_binaries") {
       sources = [
-        "$root_out_dir/$widevine_cdm_path/widevinecdmadapter.plugin",
-        "$root_out_dir/libwidevinecdm.dylib",
-      ]
-      outputs = [
-        "{{bundle_contents_dir}}/Libraries/$widevine_cdm_path/{{source_file_part}}",
-      ]
-      public_deps = [
-        # Need this intermediate dependency because "widevinecdm" is a
-        # shared_library if !is_chrome_branded, and then depending on
-        # "widevinecdm" directly would cause it to be linked into the Chromium
-        # Framework, which we don't want.
-        ":widevine_cdm_library_copy",
-        "//third_party/widevine/cdm:widevinecdmadapter",
-      ]
-
-      # Signatures are only generated for official chrome.
-      if (is_chrome_branded) {
-        sources += [
-          "$root_out_dir/$widevine_cdm_path/widevinecdmadapter.plugin.sig",
-          "$root_out_dir/libwidevinecdm.dylib.sig",
-        ]
-        public_deps += [ ":sign_cdm_adapter_for_widevine" ]
-      }
-    }
-
-    copy("widevine_cdm_library_copy") {
-      sources = [
         "$root_out_dir/$widevine_cdm_path/libwidevinecdm.dylib",
       ]
-      if (is_chrome_branded) {
+      if (enable_widevine_cdm_host_verification) {
         sources +=
             [ "$root_out_dir/$widevine_cdm_path/libwidevinecdm.dylib.sig" ]
       }
       outputs = [
-        "$root_out_dir/{{source_file_part}}",
+        "{{bundle_contents_dir}}/Libraries/$widevine_cdm_path/{{source_file_part}}",
       ]
-      deps = [
-        "//third_party/widevine/cdm:widevinecdm",
+      public_deps = [
+        "//third_party/widevine/cdm",
       ]
     }
 
@@ -1103,18 +1069,21 @@
         "{{bundle_contents_dir}}/Libraries/WidevineCdm/{{source_file_part}}",
       ]
       public_deps = [
-        "//third_party/widevine/cdm:widevine_cdm_manifest",
+        "//third_party/widevine/cdm",
       ]
     }
+  }
 
-    widevine_sign_file("sign_cdm_adapter_for_widevine") {
-      file = "$root_out_dir/$widevine_cdm_path/widevinecdmadapter.plugin"
-      flags = 0
+  group("widevine_cdm_library") {
+    if (should_bundle_widevine_cdm) {
       deps = [
-        "//third_party/widevine/cdm:widevinecdmadapter",
+        ":widevine_cdm_library_binaries",
+        ":widevine_cdm_library_manifest",
       ]
     }
+  }
 
+  if (enable_widevine_cdm_host_verification) {
     widevine_sign_file("sign_chrome_framework_for_widevine") {
       file = "$root_out_dir/$chrome_framework_name.framework/"
       if (defined(chrome_framework_version)) {
@@ -1174,15 +1143,6 @@
     }
   }
 
-  group("widevine_cdm_library") {
-    if (_should_bundle_widevine) {
-      deps = [
-        ":widevine_cdm_library_binaries",
-        ":widevine_cdm_library_manifest",
-      ]
-    }
-  }
-
   if (is_chrome_branded) {
     action("keystone_registration_framework") {
       script = "//chrome/tools/build/mac/copy_keystone_framework.py"
diff --git a/chrome/MAJOR_BRANCH_DATE b/chrome/MAJOR_BRANCH_DATE
index cc6d536..77b7ef4 100644
--- a/chrome/MAJOR_BRANCH_DATE
+++ b/chrome/MAJOR_BRANCH_DATE
@@ -1 +1 @@
-MAJOR_BRANCH_DATE=2018/03/02
+MAJOR_BRANCH_DATE=2018-03-02
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 93c7aa2..1d3cb16 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -164,8 +164,6 @@
     public static final String CHROME_HOME = "ChromeHome";
     public static final String CHROME_HOME_DROP_ALL_BUT_FIRST_THUMBNAIL =
             "ChromeHomeDropAllButFirstThumbnail";
-    public static final String CHROME_HOME_INACTIVITY_SHEET_EXPANSION =
-            "ChromeHomeInactivitySheetExpansion";
     public static final String CHROME_HOME_MENU_ITEMS_EXPAND_SHEET =
             "ChromeHomeMenuItemsExpandSheet";
     public static final String CHROME_HOME_PERSISTENT_IPH = "ChromeHomePersistentIph";
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 ad367d4..cf09ce1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -946,33 +946,6 @@
 
         if (!mIntentHandler.isIntentUserVisible()) return false;
 
-        if (FeatureUtilities.isChromeHomeEnabled()
-                && ChromeFeatureList.isEnabled(
-                           ChromeFeatureList.CHROME_HOME_INACTIVITY_SHEET_EXPANSION)) {
-            BottomSheet bottomSheet = getBottomSheet();
-            assert bottomSheet != null;
-
-            int timeoutMinsFieldTrialValue = ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
-                    ChromeFeatureList.CHROME_HOME_INACTIVITY_SHEET_EXPANSION,
-                    TIME_SINCE_BACKGROUNDED_IN_MINS_PARAM,
-                    TIME_SINCE_BACKGROUNDED_TO_SHOW_BOTTOM_SHEET_HALF_MINS);
-            long timeoutExpandBottomSheet = TimeUnit.MINUTES.toMillis(timeoutMinsFieldTrialValue);
-            if (bottomSheet.isSheetOpen()
-                    || (getTimeSinceLastBackgroundedMs() < timeoutExpandBottomSheet)) {
-                return false;
-            }
-
-            if (isInOverviewMode()) return false;
-
-            boolean hasTabs = getCurrentTabModel().getCount() > 0
-                    || mTabModelSelectorImpl.getRestoredTabCount() > 0;
-            if (hasTabs) {
-                bottomSheet.setSheetState(
-                        BottomSheet.SHEET_STATE_HALF, true, StateChangeReason.STARTUP);
-            }
-            return false;
-        }
-
         if (!ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_LAUNCH_AFTER_INACTIVITY)) {
             return false;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index 1a43d68..1c17561 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -885,12 +885,6 @@
             notifyDownloadFailed(id, fileName, icon);
             return;
         }
-        // Download is already paused.
-        if (entry != null && !entry.isAutoResumable) {
-            // Shutdown the service in case it was restarted unnecessarily.
-            stopTrackingInProgressDownload(id, true);
-            return;
-        }
         boolean canDownloadWhileMetered = entry == null ? false : entry.canDownloadWhileMetered;
         // If download is interrupted due to network disconnection, show download pending state.
         if (isAutoResumable) {
@@ -899,6 +893,12 @@
             stopTrackingInProgressDownload(id, true);
             return;
         }
+        // Download is already paused.
+        if (entry != null && !entry.isAutoResumable) {
+            // Shutdown the service in case it was restarted unnecessarily.
+            stopTrackingInProgressDownload(id, true);
+            return;
+        }
 
         String contentText =
                 mContext.getResources().getString(R.string.download_notification_paused);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService2.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService2.java
index 49f6d1a..f831309d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService2.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService2.java
@@ -304,8 +304,6 @@
             notifyDownloadFailed(id, fileName, icon);
             return;
         }
-        // If download is already paused, do nothing.
-        if (entry != null && !entry.isAutoResumable && !forceRebuild) return;
         boolean canDownloadWhileMetered = entry == null ? false : entry.canDownloadWhileMetered;
         // If download is interrupted due to network disconnection, show download pending state.
         if (isAutoResumable) {
@@ -314,6 +312,8 @@
             stopTrackingInProgressDownload(id);
             return;
         }
+        // If download is already paused, do nothing.
+        if (entry != null && !entry.isAutoResumable && !forceRebuild) return;
         int notificationId = entry == null ? getNotificationId(id) : entry.notificationId;
         Context context = ContextUtils.getApplicationContext();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index b8c0e04..2d8ebf2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -291,7 +291,7 @@
             if (renderFrameHost == null) return;
             if (TemplateUrlService.getInstance().isSearchResultsPageFromDefaultSearchProvider(
                         url)) {
-                renderFrameHost.setHasReceivedUserGesture();
+                renderFrameHost.notifyUserActivation();
             }
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java b/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java
index e1d9ec1..459e436 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java
@@ -173,8 +173,7 @@
                 @Override
                 public void onSheetClosed(int reason) {
                     bottomSheet.removeObserver(this);
-                    if (reason == BottomSheet.StateChangeReason.NAVIGATION
-                            || reason == BottomSheet.StateChangeReason.NEW_TAB) {
+                    if (reason == BottomSheet.StateChangeReason.NAVIGATION) {
                         // Dismiss the prompt as it would otherwise be dismissed momentarily once
                         // the navigation completes.
                         // TODO(timloh): This logs a dismiss (and we also already logged a show),
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 10cefdef..3506330d 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
@@ -85,21 +85,15 @@
     public static final int SHEET_STATE_SCROLLING = 4;
 
     /** The different reasons that the sheet's state can change. */
-    @IntDef({StateChangeReason.NONE, StateChangeReason.OMNIBOX_FOCUS, StateChangeReason.SWIPE,
-            StateChangeReason.NEW_TAB, StateChangeReason.EXPAND_BUTTON, StateChangeReason.STARTUP,
-            StateChangeReason.BACK_PRESS, StateChangeReason.TAP_SCRIM,
-            StateChangeReason.NAVIGATION})
+    @IntDef({StateChangeReason.NONE, StateChangeReason.SWIPE, StateChangeReason.BACK_PRESS,
+            StateChangeReason.TAP_SCRIM, StateChangeReason.NAVIGATION})
     @Retention(RetentionPolicy.SOURCE)
     public @interface StateChangeReason {
         int NONE = 0;
-        int OMNIBOX_FOCUS = 1;
-        int SWIPE = 2;
-        int NEW_TAB = 3;
-        int EXPAND_BUTTON = 4;
-        int STARTUP = 5;
-        int BACK_PRESS = 6;
-        int TAP_SCRIM = 7;
-        int NAVIGATION = 8;
+        int SWIPE = 1;
+        int BACK_PRESS = 2;
+        int TAP_SCRIM = 3;
+        int NAVIGATION = 4;
     }
 
     /**
@@ -414,13 +408,6 @@
         mIsTouchEnabled = enabled;
     }
 
-    /**
-     * A notification that the "expand" button for the bottom sheet has been pressed.
-     */
-    public void onExpandButtonPressed() {
-        setSheetState(BottomSheet.SHEET_STATE_HALF, true, StateChangeReason.EXPAND_BUTTON);
-    }
-
     /** Immediately end all animations and null the animators. */
     public void endAnimations() {
         if (mSettleAnimator != null) mSettleAnimator.end();
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
index f153c32c..a6aae78 100644
--- 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
@@ -23,15 +23,14 @@
      * 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({OPENED_BY_SWIPE, OPENED_BY_OMNIBOX_FOCUS, OPENED_BY_NEW_TAB_CREATION,
-            OPENED_BY_EXPAND_BUTTON, OPENED_BY_STARTUP})
+    @IntDef({OPENED_BY_SWIPE, OPENED_BY_BOUNDARY})
     @Retention(RetentionPolicy.SOURCE)
     private @interface SheetOpenReason {}
     private static final int OPENED_BY_SWIPE = 0;
-    private static final int OPENED_BY_OMNIBOX_FOCUS = 1;
-    private static final int OPENED_BY_NEW_TAB_CREATION = 2;
-    private static final int OPENED_BY_EXPAND_BUTTON = 3;
-    private static final int OPENED_BY_STARTUP = 4;
+    // Obsolete: private static final int OPENED_BY_OMNIBOX_FOCUS = 1;
+    // Obsolete: private static final int OPENED_BY_NEW_TAB_CREATION = 2;
+    // Obsolete: private static final int OPENED_BY_EXPAND_BUTTON = 3;
+    // Obsolete: private static final int OPENED_BY_STARTUP = 4;
     private static final int OPENED_BY_BOUNDARY = 5;
 
     private static final CachedMetrics.TimesHistogramSample TIMES_FIRST_OPEN =
@@ -57,14 +56,6 @@
 
     private static final CachedMetrics.ActionEvent ACTION_OPENED_BY_SWIPE =
             new CachedMetrics.ActionEvent("Android.ChromeHome.OpenedBySwipe");
-    private static final CachedMetrics.ActionEvent ACTION_OPENED_BY_OMNIBOX_FOCUS =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.OpenedByOmnibox");
-    private static final CachedMetrics.ActionEvent ACTION_OPENED_BY_NEW_TAB_CREATION =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.OpenedByNTP");
-    private static final CachedMetrics.ActionEvent ACTION_OPENED_BY_EXPAND_BUTTON =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.OpenedByExpandButton");
-    private static final CachedMetrics.ActionEvent ACTION_OPENED_BY_STARTUP =
-            new CachedMetrics.ActionEvent("Android.ChromeHome.OpenedByStartup");
 
     private static final CachedMetrics.ActionEvent ACTION_CLOSED_BY_SWIPE =
             new CachedMetrics.ActionEvent("Android.ChromeHome.ClosedBySwipe");
@@ -143,22 +134,6 @@
                 metricsReason = OPENED_BY_SWIPE;
                 ACTION_OPENED_BY_SWIPE.record();
                 break;
-            case StateChangeReason.OMNIBOX_FOCUS:
-                metricsReason = OPENED_BY_OMNIBOX_FOCUS;
-                ACTION_OPENED_BY_OMNIBOX_FOCUS.record();
-                break;
-            case StateChangeReason.NEW_TAB:
-                metricsReason = OPENED_BY_NEW_TAB_CREATION;
-                ACTION_OPENED_BY_NEW_TAB_CREATION.record();
-                break;
-            case StateChangeReason.EXPAND_BUTTON:
-                metricsReason = OPENED_BY_EXPAND_BUTTON;
-                ACTION_OPENED_BY_EXPAND_BUTTON.record();
-                break;
-            case StateChangeReason.STARTUP:
-                metricsReason = OPENED_BY_STARTUP;
-                ACTION_OPENED_BY_STARTUP.record();
-                break;
             case StateChangeReason.NONE:
                 // Intentionally empty.
                 break;
diff --git a/chrome/app/theme/default_100_percent/common/allowed_midi.png b/chrome/app/theme/default_100_percent/common/allowed_midi.png
deleted file mode 100644
index eb621fb..0000000
--- a/chrome/app/theme/default_100_percent/common/allowed_midi.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/blocked_camera.png b/chrome/app/theme/default_100_percent/common/blocked_camera.png
deleted file mode 100644
index c423740..0000000
--- a/chrome/app/theme/default_100_percent/common/blocked_camera.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/blocked_popups.png b/chrome/app/theme/default_100_percent/common/blocked_popups.png
deleted file mode 100644
index 893fc9e..0000000
--- a/chrome/app/theme/default_100_percent/common/blocked_popups.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/notification_welcome_icon.png b/chrome/app/theme/default_100_percent/common/notification_welcome_icon.png
deleted file mode 100644
index 5f69528..0000000
--- a/chrome/app/theme/default_100_percent/common/notification_welcome_icon.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/omnibox_extension_app_selected.png b/chrome/app/theme/default_100_percent/common/omnibox_extension_app_selected.png
deleted file mode 100644
index 923d432..0000000
--- a/chrome/app/theme/default_100_percent/common/omnibox_extension_app_selected.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/omnibox_http_selected.png b/chrome/app/theme/default_100_percent/common/omnibox_http_selected.png
deleted file mode 100644
index 1667dce4..0000000
--- a/chrome/app/theme/default_100_percent/common/omnibox_http_selected.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/omnibox_search_selected.png b/chrome/app/theme/default_100_percent/common/omnibox_search_selected.png
deleted file mode 100644
index 1d37d3c..0000000
--- a/chrome/app/theme/default_100_percent/common/omnibox_search_selected.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/allowed_midi.png b/chrome/app/theme/default_200_percent/common/allowed_midi.png
deleted file mode 100644
index 511b5e5..0000000
--- a/chrome/app/theme/default_200_percent/common/allowed_midi.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/blocked_camera.png b/chrome/app/theme/default_200_percent/common/blocked_camera.png
deleted file mode 100644
index 6402932..0000000
--- a/chrome/app/theme/default_200_percent/common/blocked_camera.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/blocked_popups.png b/chrome/app/theme/default_200_percent/common/blocked_popups.png
deleted file mode 100644
index aa858b0..0000000
--- a/chrome/app/theme/default_200_percent/common/blocked_popups.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/omnibox_extension_app_selected.png b/chrome/app/theme/default_200_percent/common/omnibox_extension_app_selected.png
deleted file mode 100644
index d26d988..0000000
--- a/chrome/app/theme/default_200_percent/common/omnibox_extension_app_selected.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/omnibox_http_selected.png b/chrome/app/theme/default_200_percent/common/omnibox_http_selected.png
deleted file mode 100644
index 3e94af65..0000000
--- a/chrome/app/theme/default_200_percent/common/omnibox_http_selected.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 2e4d080..adb9ac8 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -54,7 +54,6 @@
         <structure type="chrome_scaled_image" name="IDR_BACK_P" file="common/browser_back_pressed.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_BLOCKED_EXTENSION_SCRIPT" file="common/blocked_extension_script.png" />
-      <structure type="chrome_scaled_image" name="IDR_BLOCKED_POPUPS" file="common/blocked_popups.png" />
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_BLUETOOTH_KEYBOARD" file="cros/bluetooth_pairing_keyboard.png" />
         <structure type="chrome_scaled_image" name="IDR_BLUETOOTH_MOUSE" file="cros/bluetooth_pairing_mouse.png" />
diff --git a/chrome/browser/android/download/download_controller.cc b/chrome/browser/android/download/download_controller.cc
index 7430c53d..20caa0fb 100644
--- a/chrome/browser/android/download/download_controller.cc
+++ b/chrome/browser/android/download/download_controller.cc
@@ -107,6 +107,7 @@
   dl_params->set_request_origin(
       offline_pages::android::OfflinePageBridge::GetEncodedOriginApp(
           web_contents));
+  dl_params->set_suggested_name(params.suggested_filename);
   RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU);
   dl_params->set_download_source(download::DownloadSource::CONTEXT_MENU);
   dlm->DownloadUrl(std::move(dl_params));
diff --git a/chrome/browser/android/resource_id.h b/chrome/browser/android/resource_id.h
index 18b994c..9cb569a 100644
--- a/chrome/browser/android/resource_id.h
+++ b/chrome/browser/android/resource_id.h
@@ -21,11 +21,12 @@
 LINK_RESOURCE_ID(IDR_INFOBAR_3D_BLOCKED, R.drawable.infobar_3d_blocked)
 LINK_RESOURCE_ID(IDR_INFOBAR_AUTOFILL_CC, R.drawable.infobar_autofill_cc)
 LINK_RESOURCE_ID(IDR_INFOBAR_TRANSLATE, R.drawable.infobar_translate)
-LINK_RESOURCE_ID(IDR_BLOCKED_POPUPS, R.drawable.infobar_blocked_popups)
 
 // Android only infobars.
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_ACCESSIBILITY_EVENTS,
                     R.drawable.infobar_accessibility_events)
+DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_BLOCKED_POPUPS,
+                    R.drawable.infobar_blocked_popups)
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_CLIPBOARD,
                     R.drawable.infobar_clipboard)
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_FOLDER, R.drawable.ic_folder_blue_24dp)
@@ -49,8 +50,6 @@
                     R.drawable.infobar_protected_media_identifier)
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_SAVE_PASSWORD,
                     R.drawable.infobar_savepassword)
-DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_ADS_BLOCKED,
-                    R.drawable.infobar_blocked_popups)
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_WARNING, R.drawable.infobar_warning)
 
 // PageInfoUI images, used in ConnectionInfoPopup
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 58724db..f988ff1 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -2138,6 +2138,7 @@
       "platform_apps/web_view/storage_persistence", "PRE_StoragePersistence",
       kFlagEnableFileAccess))
       << message_;
+  content::EnsureCookiesFlushed(profile());
 }
 
 // This is the post-reset portion of the StoragePersistence test.  See
@@ -3071,6 +3072,8 @@
   // Leak the temporary download directory. We'll retake ownership in the next
   // browser session.
   temporary_download_dir.Take();
+
+  content::EnsureCookiesFlushed(profile());
 }
 
 IN_PROC_BROWSER_TEST_P(WebViewTest, DownloadCookieIsolation_CrossSession) {
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index ddd9d20..b57636f 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -48,6 +48,8 @@
         <structure name="IDR_CUSTOM_ELEMENTS_USER_POD_HTML" file="resources\chromeos\login\custom_elements_user_pod.html" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_CUSTOM_ELEMENTS_LOGIN_HTML" file="resources\chromeos\login\custom_elements_login.html" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_CUSTOM_ELEMENTS_LOGIN_JS" file="resources\chromeos\login\custom_elements_login.js" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_ASSISTANT_OPTIN_HTML" file="resources\chromeos\assistant_optin\assistant_optin.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
+        <structure name="IDR_ASSISTANT_OPTIN_JS" file="resources\chromeos\assistant_optin\assistant_optin.js" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
       </if>
       <structure name="IDR_SIGNIN_SHARED_CSS_HTML" file="resources\signin\signin_shared_css.html" preprocess="true" allowexternalscript="true" type="chrome_html" />
       <if expr="not is_android and not chromeos">
@@ -385,7 +387,6 @@
       <if expr="enable_print_preview">
         <include name="IDR_PRINT_PREVIEW_HTML" file="resources\print_preview\print_preview.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
         <include name="IDR_PRINT_PREVIEW_JS" file="resources\print_preview\print_preview.js" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_PRINT_PREVIEW_PDF_PREVIEW_HTML" file="resources\print_preview\pdf_preview.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
         <include name="IDR_PRINT_PREVIEW_IMAGES_1X_PRINTER"
                  file="resources\print_preview\images\1x\printer.png" type="BINDATA" />
         <include name="IDR_PRINT_PREVIEW_IMAGES_2X_PRINTER"
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 7f9ba4a..571970b 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -57,6 +57,8 @@
 #include "chrome/browser/memory/chrome_memory_coordinator_delegate.h"
 #include "chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.h"
 #include "chrome/browser/nacl_host/nacl_browser_delegate_impl.h"
+#include "chrome/browser/net/profile_network_context_service.h"
+#include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/net_benchmarking.h"
 #include "chrome/browser/notifications/platform_notification_service_impl.h"
 #include "chrome/browser/page_load_metrics/metrics_navigation_throttle.h"
@@ -925,6 +927,11 @@
   registry->RegisterBooleanPref(prefs::kSitePerProcess, false);
   registry->RegisterBooleanPref(prefs::kWebDriverOverridesIncompatiblePolicies,
                                 false);
+#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
+  // TODO(chrisha): Move this to chrome/browser/conflicts as we build the
+  // logic that responds to this pref.
+  registry->RegisterBooleanPref(prefs::kThirdPartyBlockingEnabled, true);
+#endif
 }
 
 // static
@@ -3921,6 +3928,7 @@
     content::BrowserContext* context,
     bool in_memory,
     const base::FilePath& relative_partition_path) {
+  Profile* profile = Profile::FromBrowserContext(context);
   // If the relative partition path is empty, this is creating the Profile's
   // main NetworkContext.
   if (relative_partition_path.empty()) {
@@ -3928,12 +3936,17 @@
     // ProfileIOData is removed. Currently, TestProfile (used in unit tests)
     // needs to be able to bypass ProfileNetworkContextServiceFactory, since
     // TestProfile bypasses ProfileIOData's URLRequestContext creation logic.
-    Profile* profile = Profile::FromBrowserContext(context);
     return profile->CreateMainNetworkContext();
   }
-  // TODO(mmenke):  Implement this once ProfileNetworkContextServiceFactory can
-  // create a fully functional NetworkContext for Apps when the network service
-  // is disabled.
+
+  // TODO(mmenke):  Share this with the non-network service code path once
+  // ProfileNetworkContextServiceFactory can create a fully functional
+  // NetworkContext for Apps when the network service is disabled.
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+    return ProfileNetworkContextServiceFactory::GetForContext(context)
+        ->CreateNetworkContextForPartition(in_memory, relative_partition_path);
+  }
+
   return ContentBrowserClient::CreateNetworkContext(context, in_memory,
                                                     relative_partition_path);
 }
diff --git a/chrome/browser/chrome_content_utility_manifest_overlay.json b/chrome/browser/chrome_content_utility_manifest_overlay.json
index 5bd06f88..c4d3d70 100644
--- a/chrome/browser/chrome_content_utility_manifest_overlay.json
+++ b/chrome/browser/chrome_content_utility_manifest_overlay.json
@@ -7,9 +7,6 @@
           "chrome::mojom::DialDeviceDescriptionParser",
           "chrome::mojom::ProfileImport",
           "chrome::mojom::ShellHandler",
-          "extensions::mojom::ExtensionUnpacker",
-          "extensions::mojom::ManifestParser",
-          "extensions::mojom::MediaParser",
           "payments::mojom::PaymentManifestParser",
           "profiling::mojom::ProfilingClient",
           "proxy_resolver::mojom::ProxyResolverFactory",
diff --git a/chrome/browser/chromeos/attestation/fake_certificate.cc b/chrome/browser/chromeos/attestation/fake_certificate.cc
index 1571bde..4ed3cbc 100644
--- a/chrome/browser/chromeos/attestation/fake_certificate.cc
+++ b/chrome/browser/chromeos/attestation/fake_certificate.cc
@@ -66,7 +66,7 @@
     return false;
   }
   return net::x509_util::CreateSelfSignedCert(
-      test_key.get(), net::x509_util::DIGEST_SHA256, "CN=subject", 12345,
+      test_key->key(), net::x509_util::DIGEST_SHA256, "CN=subject", 12345,
       valid_start, valid_expiry, certificate);
 }
 
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
index e35e7fc..526b42b 100644
--- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
+++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
 #include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/login/auth/extended_authenticator.h"
 #include "chromeos/login/auth/user_context.h"
@@ -260,6 +261,34 @@
   Release();  // Balanced in Run().
 }
 
+// quickUnlockPrivate.setLockScreenEnabled
+
+QuickUnlockPrivateSetLockScreenEnabledFunction::
+    QuickUnlockPrivateSetLockScreenEnabledFunction()
+    : chrome_details_(this) {}
+
+QuickUnlockPrivateSetLockScreenEnabledFunction::
+    ~QuickUnlockPrivateSetLockScreenEnabledFunction() {}
+
+ExtensionFunction::ResponseAction
+QuickUnlockPrivateSetLockScreenEnabledFunction::Run() {
+  auto params =
+      quick_unlock_private::SetLockScreenEnabled::Params::Create(*args_);
+  Profile* profile = Profile::FromBrowserContext(browser_context());
+  QuickUnlockStorage* quick_unlock_storage =
+      chromeos::quick_unlock::QuickUnlockFactory::GetForProfile(profile);
+  if (quick_unlock_storage->GetAuthTokenExpired())
+    return RespondNow(Error(kAuthTokenExpired));
+  if (params->token != quick_unlock_storage->GetAuthToken())
+    return RespondNow(Error(kAuthTokenInvalid));
+
+  profile->GetPrefs()->SetBoolean(prefs::kEnableAutoScreenLock,
+                                  params->enabled);
+
+  return RespondNow(ArgumentList(
+      quick_unlock_private::SetLockScreenEnabled::Results::Create()));
+}
+
 // quickUnlockPrivate.getAvailableModes
 
 QuickUnlockPrivateGetAvailableModesFunction::
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h
index 658ad5bd..764ed0b1 100644
--- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h
+++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h
@@ -57,6 +57,25 @@
   DISALLOW_COPY_AND_ASSIGN(QuickUnlockPrivateGetAuthTokenFunction);
 };
 
+class QuickUnlockPrivateSetLockScreenEnabledFunction
+    : public UIThreadExtensionFunction {
+ public:
+  QuickUnlockPrivateSetLockScreenEnabledFunction();
+  DECLARE_EXTENSION_FUNCTION("quickUnlockPrivate.setLockScreenEnabled",
+                             QUICKUNLOCKPRIVATE_SETLOCKSCREENENABLED);
+
+ protected:
+  ~QuickUnlockPrivateSetLockScreenEnabledFunction() override;
+
+  // ExtensionFunction overrides.
+  ResponseAction Run() override;
+
+ private:
+  ChromeExtensionFunctionDetails chrome_details_;
+
+  DISALLOW_COPY_AND_ASSIGN(QuickUnlockPrivateSetLockScreenEnabledFunction);
+};
+
 class QuickUnlockPrivateGetAvailableModesFunction
     : public UIThreadExtensionFunction {
  public:
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
index ee0ab12..9c83fc6 100644
--- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
@@ -177,6 +177,25 @@
     return RunFunctionAndReturnError(func, std::move(params));
   }
 
+  // Wrapper for chrome.quickUnlockPrivate.setLockScreenEnabled.
+  void SetLockScreenEnabled(const std::string& token, bool enabled) {
+    auto params = std::make_unique<base::ListValue>();
+    params->AppendString(token);
+    params->AppendBoolean(enabled);
+    RunFunction(new QuickUnlockPrivateSetLockScreenEnabledFunction(),
+                std::move(params));
+  }
+
+  // Wrapper for chrome.quickUnlockPrivate.setLockScreenEnabled.
+  std::string SetLockScreenEnabledWithInvalidToken(bool enabled) {
+    auto params = std::make_unique<base::ListValue>();
+    params->AppendString(kInvalidToken);
+    params->AppendBoolean(enabled);
+    return RunFunctionAndReturnError(
+        new QuickUnlockPrivateSetLockScreenEnabledFunction(),
+        std::move(params));
+  }
+
   // Wrapper for chrome.quickUnlockPrivate.getAvailableModes.
   QuickUnlockModeList GetAvailableModes() {
     // Run the function.
@@ -338,6 +357,8 @@
     return api_test_utils::RunFunctionAndReturnError(func, args, profile());
   }
 
+  std::string token() { return token_; }
+
  private:
   // Runs the given |func| with the given |params|.
   std::unique_ptr<base::Value> RunFunction(
@@ -397,6 +418,33 @@
   EXPECT_FALSE(error.empty());
 }
 
+// Verifies that setting lock screen enabled modifies the setting.
+TEST_F(QuickUnlockPrivateUnitTest, SetLockScreenEnabled) {
+  PrefService* pref_service = profile()->GetPrefs();
+  bool lock_screen_enabled =
+      pref_service->GetBoolean(prefs::kEnableAutoScreenLock);
+
+  SetLockScreenEnabled(token(), !lock_screen_enabled);
+
+  EXPECT_EQ(!lock_screen_enabled,
+            pref_service->GetBoolean(prefs::kEnableAutoScreenLock));
+}
+
+// Verifies that setting lock screen enabled fails to modify the setting with
+// an invalid token.
+TEST_F(QuickUnlockPrivateUnitTest, SetLockScreenEnabledFailsWithInvalidToken) {
+  PrefService* pref_service = profile()->GetPrefs();
+  bool lock_screen_enabled =
+      pref_service->GetBoolean(prefs::kEnableAutoScreenLock);
+
+  std::string error =
+      SetLockScreenEnabledWithInvalidToken(!lock_screen_enabled);
+  EXPECT_FALSE(error.empty());
+
+  EXPECT_EQ(lock_screen_enabled,
+            pref_service->GetBoolean(prefs::kEnableAutoScreenLock));
+}
+
 // Verifies that this returns PIN for GetAvailableModes.
 TEST_F(QuickUnlockPrivateUnitTest, GetAvailableModes) {
   EXPECT_EQ(GetAvailableModes(),
diff --git a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
index bd3df7df..c3adf60 100644
--- a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
@@ -37,6 +37,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/fake_auth_policy_client.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
 #include "chromeos/login/auth/authpolicy_login_helper.h"
 #include "chromeos/login/auth/key.h"
@@ -80,6 +81,8 @@
 
 const char kGaiaID[] = "12345";
 const char kUsername[] = "test_user@gmail.com";
+const char kUserWhitelist[] = "*@gmail.com";
+const char kUserNotMatchingWhitelist[] = "user@another_mail.com";
 const char kSupervisedUserID[] = "supervised_user@locally-managed.localhost";
 const char kPassword[] = "test_password";
 const char kActiveDirectoryRealm[] = "active.directory.realm";
@@ -773,12 +776,48 @@
     EXPECT_CALL(*mock_login_display_, SetUIEnabled(true)).Times(1);
   }
 
+  void ExpectLoginWhitelistFailure() {
+    EXPECT_CALL(*mock_login_display_, SetUIEnabled(false)).Times(2);
+    EXPECT_CALL(*mock_login_display_, ShowWhitelistCheckFailedError()).Times(1);
+    EXPECT_CALL(*mock_login_display_, SetUIEnabled(true)).Times(1);
+  }
+
   void ExpectLoginSuccess() {
     EXPECT_CALL(*mock_login_display_, SetUIEnabled(false)).Times(2);
     EXPECT_CALL(*mock_login_display_, SetUIEnabled(true)).Times(1);
   }
 };
 
+class ExistingUserControllerActiveDirectoryUserWhitelistTest
+    : public ExistingUserControllerActiveDirectoryTest {
+ public:
+  ExistingUserControllerActiveDirectoryUserWhitelistTest() = default;
+
+  void SetUpInProcessBrowserTestFixture() override {
+    ExistingUserControllerActiveDirectoryTest::
+        SetUpInProcessBrowserTestFixture();
+    chromeos::FakeAuthPolicyClient* fake_authpolicy_client =
+        static_cast<chromeos::FakeAuthPolicyClient*>(
+            chromeos::DBusThreadManager::Get()->GetAuthPolicyClient());
+    em::ChromeDeviceSettingsProto device_policy;
+    device_policy.mutable_user_whitelist()->add_user_whitelist()->assign(
+        kUserWhitelist);
+    fake_authpolicy_client->set_device_policy(device_policy);
+  }
+
+  void SetUpLoginDisplay() override {
+    EXPECT_CALL(*mock_login_display_host_.get(), CreateLoginDisplay(_))
+        .Times(1)
+        .WillOnce(Return(mock_login_display_));
+    EXPECT_CALL(*mock_login_display_host_.get(), GetNativeWindow())
+        .Times(1)
+        .WillOnce(ReturnNull());
+    EXPECT_CALL(*mock_login_display_host_.get(), OnPreferencesChanged())
+        .Times(1);
+    EXPECT_CALL(*mock_login_display_, Init(_, false, true, false)).Times(1);
+  }
+};
+
 // Tests that Active Directory online login succeeds on the Active Directory
 // managed device.
 IN_PROC_BROWSER_TEST_F(ExistingUserControllerActiveDirectoryTest,
@@ -824,6 +863,37 @@
   existing_user_controller()->CompleteLogin(user_context);
 }
 
+// Tests that authentication succeeds if user email matches whitelist.
+IN_PROC_BROWSER_TEST_F(ExistingUserControllerActiveDirectoryUserWhitelistTest,
+                       Success) {
+  ExpectLoginSuccess();
+  UserContext user_context(ad_account_id_);
+  user_context.SetKey(Key(kPassword));
+  user_context.SetUserIDHash(ad_account_id_.GetUserEmail());
+  user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
+  user_context.SetUserType(user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY);
+  content::WindowedNotificationObserver profile_prepared_observer(
+      chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
+      content::NotificationService::AllSources());
+  existing_user_controller()->CompleteLogin(user_context);
+
+  profile_prepared_observer.Wait();
+}
+
+// Tests that authentication fails if user email does not match whitelist.
+IN_PROC_BROWSER_TEST_F(ExistingUserControllerActiveDirectoryUserWhitelistTest,
+                       Fail) {
+  ExpectLoginWhitelistFailure();
+  AccountId account_id =
+      AccountId::AdFromUserEmailObjGuid(kUserNotMatchingWhitelist, kGaiaID);
+  UserContext user_context(account_id);
+  user_context.SetKey(Key(kPassword));
+  user_context.SetUserIDHash(account_id.GetUserEmail());
+  user_context.SetAuthFlow(UserContext::AUTH_FLOW_ACTIVE_DIRECTORY);
+  user_context.SetUserType(user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY);
+  existing_user_controller()->CompleteLogin(user_context);
+}
+
 class ExistingUserControllerSavePasswordHashTest
     : public ExistingUserControllerTest {
  public:
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_notification_controller.cc b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_notification_controller.cc
index 243da32..13ea44e 100644
--- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_notification_controller.cc
+++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_notification_controller.cc
@@ -57,6 +57,8 @@
   NotificationParams* params = &controller->params_;
   params->title_message_id = IDS_QUICK_UNLOCK_NOTIFICATION_TITLE;
   params->body_message_id = IDS_QUICK_UNLOCK_NOTIFICATION_BODY;
+  // TODO(http://crbug.com/291747): Change this to actual icon for quick unlock
+  // feature notification, also use a vector icon instead of raster asset.
   params->icon_id = IDR_SCREENSHOT_NOTIFICATION_ICON;
   params->notifier = kNotifierPinUnlock;
   params->feature_name_id = IDS_PIN_UNLOCK_FEATURE_NOTIFIER_NAME;
@@ -110,6 +112,7 @@
   NotificationParams* params = &controller->params_;
   params->title_message_id = IDS_FINGERPRINT_NOTIFICATION_TITLE;
   params->body_message_id = IDS_FINGERPRINT_NOTIFICATION_BODY;
+  // TODO(sammiequon): Change to a vector icon identifier.
   params->icon_id = IDR_NOTIFICATION_FINGERPRINT;
   params->notifier = kNotifierFingerprintUnlock;
   params->feature_name_id = IDS_FINGERPRINT_UNLOCK_FEATURE_NOTIFIER_NAME;
@@ -215,8 +218,6 @@
       message_center::NOTIFICATION_TYPE_SIMPLE, params_.notification_id,
       l10n_util::GetStringUTF16(params_.title_message_id),
       l10n_util::GetStringUTF16(params_.body_message_id),
-      // TODO(http://crbug.com/291747): Change this to actual icon for
-      // quick unlock feature notification.
       ui::ResourceBundle::GetSharedInstance().GetImageNamed(params_.icon_id),
       l10n_util::GetStringUTF16(params_.feature_name_id), GURL(),
       message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
diff --git a/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc b/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
index 4165c74..5888138c 100644
--- a/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.cc
@@ -375,7 +375,6 @@
             base::BindOnce(&chrome::AttemptUserExit) /* fatal_error_callback */,
             account_id, base::ThreadTaskRunnerHandle::Get(), io_task_runner);
 
-    // TODO(tnagel): Enable whitelist for Active Directory.
     bool wildcard_match = false;
     if (connector->IsEnterpriseManaged() &&
         chromeos::CrosSettings::Get()->IsUserWhitelisted(
diff --git a/chrome/browser/chromeos/printing/cups_print_job.h b/chrome/browser/chromeos/printing/cups_print_job.h
index e01e926..df2f1d9 100644
--- a/chrome/browser/chromeos/printing/cups_print_job.h
+++ b/chrome/browser/chromeos/printing/cups_print_job.h
@@ -53,7 +53,6 @@
   const std::string& document_title() const { return document_title_; }
   int total_page_number() const { return total_page_number_; }
   int printed_page_number() const { return printed_page_number_; }
-  bool expired() const { return expired_; }
   State state() const { return state_; }
   ErrorCode error_code() const { return error_code_; }
 
@@ -61,7 +60,6 @@
   void set_printed_page_number(int page_number) {
     printed_page_number_ = page_number;
   }
-  void set_expired(bool expired) { expired_ = expired; }
   void set_state(State state) { state_ = state; }
   void set_error_code(ErrorCode error_code) { error_code_ = error_code; }
 
@@ -79,9 +77,6 @@
   int total_page_number_ = 0;
   int printed_page_number_ = 0;
 
-  // True if the job has expired due to timeout.
-  bool expired_ = false;
-
   State state_ = State::STATE_NONE;
   ErrorCode error_code_ = ErrorCode::NO_ERROR;
 
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
index 3f2d3cea..5325295 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
@@ -39,11 +39,6 @@
 // The rate in milliseconds at which we will poll CUPS for print job updates.
 const int kPollRate = 1000;
 
-// How long we'll wait to connect to a printer before declaring an error.
-// TODO(crbug.com/786182): Increase to 120s to give pipeline more time to
-// complete.
-const int kConnectingTimeout = 120;
-
 // Threshold for giving up on communicating with CUPS.
 const int kRetryMax = 6;
 
@@ -121,16 +116,6 @@
   return State::STATE_NONE;
 }
 
-// Returns true if |printer_status|.reasons contains |reason|.
-bool ContainsReason(const printing::PrinterStatus printer_status,
-                    PrinterReason::Reason reason) {
-  return std::find_if(printer_status.reasons.begin(),
-                      printer_status.reasons.end(),
-                      [&reason](const PrinterReason& r) {
-                        return r.reason == reason;
-                      }) != printer_status.reasons.end();
-}
-
 // Returns true if |job|.state_reasons contains |reason|
 bool JobContainsReason(const ::printing::CupsJob& job,
                        base::StringPiece reason) {
@@ -158,15 +143,6 @@
   return chromeos::CupsPrintJob::ErrorCode::NO_ERROR;
 }
 
-// Check if the job should timeout.  Returns true if the job has timed out.
-bool DidTimeout(const printing::CupsJob& job,
-                chromeos::CupsPrintJob* print_job) {
-  // Check to see if we should time out.
-  base::TimeDelta time_waiting =
-      base::Time::Now() - base::Time::FromTimeT(job.processing_started);
-  return time_waiting > base::TimeDelta::FromSeconds(kConnectingTimeout);
-}
-
 // Update the current printed page.  Returns true of the page has been updated.
 bool UpdateCurrentPage(const printing::CupsJob& job,
                        chromeos::CupsPrintJob* print_job) {
@@ -195,17 +171,7 @@
   bool pages_updated = false;
   switch (job.state) {
     case ::printing::CupsJob::PROCESSING:
-      if (ContainsReason(printer_status, PrinterReason::CONNECTING_TO_DEVICE)) {
-        if (DidTimeout(job, print_job)) {
-          LOG(WARNING) << "Connecting to printer timed out";
-          print_job->set_state(chromeos::CupsPrintJob::State::STATE_ERROR);
-          print_job->set_error_code(
-              chromeos::CupsPrintJob::ErrorCode::PRINTER_UNREACHABLE);
-          print_job->set_expired(true);
-        }
-      } else {
-        pages_updated = UpdateCurrentPage(job, print_job);
-      }
+      pages_updated = UpdateCurrentPage(job, print_job);
       break;
     case ::printing::CupsJob::COMPLETED:
       DCHECK_GE(job.current_pages, print_job->total_page_number());
@@ -476,12 +442,7 @@
           NotifyJobStateUpdate(print_job);
         }
 
-        if (print_job->expired()) {
-          // Job needs to be forcibly cancelled.
-          RecordJobResult(TIMEOUT_CANCEL);
-          FinishPrintJob(print_job);
-          // Beware, print_job was removed from jobs_ and deleted.
-        } else if (print_job->PipelineDead()) {
+        if (print_job->PipelineDead()) {
           RecordJobResult(FILTER_FAILED);
           FinishPrintJob(print_job);
         } else if (print_job->IsJobFinished()) {
diff --git a/chrome/browser/component_updater/sw_reporter_installer_win.cc b/chrome/browser/component_updater/sw_reporter_installer_win.cc
index 900a761..ae1e9f9 100644
--- a/chrome/browser/component_updater/sw_reporter_installer_win.cc
+++ b/chrome/browser/component_updater/sw_reporter_installer_win.cc
@@ -93,7 +93,7 @@
   int current_failure_run = 0;
   bool last_result = false;
   while (tokenizer.GetNext()) {
-    if (tokenizer.token() == L"0") {
+    if (tokenizer.token_piece() == L"0") {
       ++failure_count;
       ++current_failure_run;
       last_result = false;
diff --git a/chrome/browser/component_updater/third_party_module_list_component_installer_win.cc b/chrome/browser/component_updater/third_party_module_list_component_installer_win.cc
index bd1dc33d..0741121 100644
--- a/chrome/browser/component_updater/third_party_module_list_component_installer_win.cc
+++ b/chrome/browser/component_updater/third_party_module_list_component_installer_win.cc
@@ -19,7 +19,6 @@
 #include "base/values.h"
 #include "base/version.h"
 #include "chrome/browser/conflicts/module_database_win.h"
-#include "chrome/browser/conflicts/third_party_conflicts_manager_win.h"
 #include "components/component_updater/component_updater_paths.h"
 #include "content/public/browser/browser_thread.h"
 
diff --git a/chrome/browser/conflicts/module_database_win.cc b/chrome/browser/conflicts/module_database_win.cc
index 42d7fd04..6ad367cb 100644
--- a/chrome/browser/conflicts/module_database_win.cc
+++ b/chrome/browser/conflicts/module_database_win.cc
@@ -13,12 +13,6 @@
 #include "base/win/windows_version.h"
 #include "chrome/browser/conflicts/module_database_observer_win.h"
 
-#if defined(GOOGLE_CHROME_BUILD)
-#include "chrome/browser/conflicts/third_party_conflicts_manager_win.h"
-#include "chrome/common/chrome_features.h"
-#include "components/prefs/pref_registry_simple.h"
-#endif
-
 namespace {
 
 // Document the assumptions made on the ProcessType enum in order to convert
@@ -55,7 +49,11 @@
   AddObserver(&third_party_metrics_);
 
 #if defined(GOOGLE_CHROME_BUILD)
-  MaybeInitializeThirdPartyConflictsManager();
+  if (base::win::GetVersion() >= base::win::VERSION_WIN10) {
+    third_party_conflicts_manager_ =
+        std::make_unique<ThirdPartyConflictsManager>(this);
+    AddObserver(third_party_conflicts_manager_.get());
+  }
 #endif
 }
 
@@ -174,15 +172,6 @@
   module_inspector_.IncreaseInspectionPriority();
 }
 
-#if defined(GOOGLE_CHROME_BUILD)
-// static
-void ModuleDatabase::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
-  // Register the pref used to disable the Incompatible Applications warning
-  // using group policy.
-  registry->RegisterBooleanPref(prefs::kThirdPartyBlockingEnabled, true);
-}
-#endif
-
 // static
 uint32_t ModuleDatabase::ProcessTypeToBit(content::ProcessType process_type) {
   uint32_t bit_index =
@@ -269,23 +258,3 @@
       observer->OnNewModuleFound(module.first, module.second);
   }
 }
-
-#if defined(GOOGLE_CHROME_BUILD)
-void ModuleDatabase::MaybeInitializeThirdPartyConflictsManager() {
-  // Early exit if disabled via group policy.
-  const PrefService::Preference* third_party_blocking_enabled_pref =
-      g_browser_process->local_state()->FindPreference(
-          prefs::kThirdPartyBlockingEnabled);
-  if (third_party_blocking_enabled_pref->IsManaged() &&
-      !third_party_blocking_enabled_pref->GetValue()->GetBool())
-    return;
-
-  if (base::FeatureList::IsEnabled(
-          features::kIncompatibleApplicationsWarning) &&
-      base::win::GetVersion() >= base::win::VERSION_WIN10) {
-    third_party_conflicts_manager_ =
-        std::make_unique<ThirdPartyConflictsManager>(this);
-    AddObserver(third_party_conflicts_manager_.get());
-  }
-}
-#endif
diff --git a/chrome/browser/conflicts/module_database_win.h b/chrome/browser/conflicts/module_database_win.h
index 80bb624..e9583721 100644
--- a/chrome/browser/conflicts/module_database_win.h
+++ b/chrome/browser/conflicts/module_database_win.h
@@ -19,13 +19,12 @@
 #include "chrome/browser/conflicts/third_party_metrics_recorder_win.h"
 #include "content/public/common/process_type.h"
 
-class ModuleDatabaseObserver;
-
 #if defined(GOOGLE_CHROME_BUILD)
-class PrefRegistrySimple;
-class ThirdPartyConflictsManager;
+#include "chrome/browser/conflicts/third_party_conflicts_manager_win.h"
 #endif
 
+class ModuleDatabaseObserver;
+
 namespace base {
 class FilePath;
 }
@@ -116,11 +115,9 @@
   void IncreaseInspectionPriority();
 
 #if defined(GOOGLE_CHROME_BUILD)
-  static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
-
   // Accessor for the third party conflicts manager. This is exposed so that the
   // manager can be wired up to the ThirdPartyModuleListComponentInstaller.
-  // Returns null if the tracking of incompatible applications is disabled.
+  // Returns returns null on Windows 8.1 and lower.
   ThirdPartyConflictsManager* third_party_conflicts_manager() {
     return third_party_conflicts_manager_.get();
   }
@@ -173,15 +170,6 @@
   // OnNewModuleFound().
   void NotifyLoadedModules(ModuleDatabaseObserver* observer);
 
-#if defined(GOOGLE_CHROME_BUILD)
-  // Initializes the ThirdPartyConflictsManager, which controls the warning of
-  // incompatible applications that injects into Chrome.
-  // The manager is not initialized if it is disabled via a base::Feature or a
-  // group policy. Note that it is also not initialized on Windows version
-  // 8.1 and less.
-  void MaybeInitializeThirdPartyConflictsManager();
-#endif
-
   // The task runner to which this object is bound.
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
diff --git a/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc b/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc
index e90c718..a4880258 100644
--- a/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc
+++ b/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc
@@ -11,8 +11,6 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/conflicts/module_database_win.h"
 #include "chrome/common/conflicts/module_watcher_win.h"
-#include "chrome/test/base/scoped_testing_local_state.h"
-#include "chrome/test/base/testing_browser_process.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #include <windows.h>
@@ -31,8 +29,7 @@
 class ModuleEventSinkImplTest : public testing::Test {
  protected:
   ModuleEventSinkImplTest()
-      : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()),
-        module_database_(std::make_unique<ModuleDatabase>(
+      : module_database_(std::make_unique<ModuleDatabase>(
             base::SequencedTaskRunnerHandle::Get())) {}
 
   void CreateModuleSinkImpl() {
@@ -47,7 +44,6 @@
 
   // Must be before |module_database_|.
   base::test::ScopedTaskEnvironment scoped_task_environment_;
-  ScopedTestingLocalState scoped_testing_local_state_;
   std::unique_ptr<ModuleDatabase> module_database_;
   std::unique_ptr<ModuleEventSinkImpl> module_event_sink_impl_;
 
diff --git a/chrome/browser/conflicts/problematic_programs_updater_win.cc b/chrome/browser/conflicts/problematic_programs_updater_win.cc
index 8f9408f..07fa38b 100644
--- a/chrome/browser/conflicts/problematic_programs_updater_win.cc
+++ b/chrome/browser/conflicts/problematic_programs_updater_win.cc
@@ -139,6 +139,9 @@
 
 }  // namespace
 
+const base::Feature kIncompatibleApplicationsWarning{
+    "IncompatibleApplicationsWarning", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // ProblematicProgram ----------------------------------------------------------
 
 ProblematicProgramsUpdater::ProblematicProgram::ProblematicProgram(
@@ -157,14 +160,6 @@
 
 // ProblematicProgramsUpdater --------------------------------------------------
 
-ProblematicProgramsUpdater::ProblematicProgramsUpdater(
-    const ModuleListFilter& module_list_filter,
-    const InstalledPrograms& installed_programs)
-    : module_list_filter_(module_list_filter),
-      installed_programs_(installed_programs) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-}
-
 ProblematicProgramsUpdater::~ProblematicProgramsUpdater() = default;
 
 // static
@@ -174,16 +169,37 @@
 }
 
 // static
-bool ProblematicProgramsUpdater::IsIncompatibleApplicationsWarningEnabled() {
-  return ModuleDatabase::GetInstance() &&
-         ModuleDatabase::GetInstance()->third_party_conflicts_manager();
+std::unique_ptr<ProblematicProgramsUpdater>
+ProblematicProgramsUpdater::MaybeCreate(
+    const ModuleListFilter& module_list_filter,
+    const InstalledPrograms& installed_programs) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  std::unique_ptr<ProblematicProgramsUpdater> instance;
+
+  if (base::FeatureList::IsEnabled(kIncompatibleApplicationsWarning)) {
+    instance.reset(
+        new ProblematicProgramsUpdater(module_list_filter, installed_programs));
+  }
+
+  return instance;
 }
 
 // static
 bool ProblematicProgramsUpdater::HasCachedPrograms() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  return !GetCachedPrograms().empty();
+  if (!base::FeatureList::IsEnabled(kIncompatibleApplicationsWarning))
+    return false;
+
+  std::vector<ProblematicProgram> programs = ConvertToProblematicProgramsVector(
+      *g_browser_process->local_state()
+           ->FindPreference(prefs::kProblematicPrograms)
+           ->GetValue());
+
+  RemoveStaleEntriesAndUpdateCache(&programs);
+
+  return !programs.empty();
 }
 
 // static
@@ -191,7 +207,12 @@
 ProblematicProgramsUpdater::GetCachedPrograms() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  std::vector<ProblematicProgram> programs = ConvertToProblematicProgramsVector(
+  std::vector<ProblematicProgram> programs;
+
+  if (!base::FeatureList::IsEnabled(kIncompatibleApplicationsWarning))
+    return programs;
+
+  programs = ConvertToProblematicProgramsVector(
       *g_browser_process->local_state()
            ->FindPreference(prefs::kProblematicPrograms)
            ->GetValue());
@@ -273,3 +294,9 @@
                               std::move(element.second));
   }
 }
+
+ProblematicProgramsUpdater::ProblematicProgramsUpdater(
+    const ModuleListFilter& module_list_filter,
+    const InstalledPrograms& installed_programs)
+    : module_list_filter_(module_list_filter),
+      installed_programs_(installed_programs) {}
diff --git a/chrome/browser/conflicts/problematic_programs_updater_win.h b/chrome/browser/conflicts/problematic_programs_updater_win.h
index f0f579a..90ffabe3 100644
--- a/chrome/browser/conflicts/problematic_programs_updater_win.h
+++ b/chrome/browser/conflicts/problematic_programs_updater_win.h
@@ -8,19 +8,28 @@
 #include <memory>
 #include <vector>
 
+#include "base/feature_list.h"
 #include "base/macros.h"
 #include "chrome/browser/conflicts/installed_programs_win.h"
 #include "chrome/browser/conflicts/module_database_observer_win.h"
 #include "chrome/browser/conflicts/proto/module_list.pb.h"
+#include "url/gurl.h"
 
 class ModuleListFilter;
 class PrefRegistrySimple;
 
+// A feature that controls whether Chrome warns about incompatible applications.
+extern const base::Feature kIncompatibleApplicationsWarning;
+
 // Maintains a list of problematic programs that are installed on the machine.
 // These programs cause unwanted DLLs to be loaded into Chrome.
 //
 // Because the list is expensive to build, it is cached into the Local State
 // file so that it is available at startup.
+//
+// When kIncompatibleApplicationsWarning is disabled, this class always behaves
+// as-if there are no problematic programs on the computer. This makes it safe
+// to use all of the class' static functions unconditionally.
 class ProblematicProgramsUpdater : public ModuleDatabaseObserver {
  public:
   struct ProblematicProgram {
@@ -37,24 +46,22 @@
     std::unique_ptr<chrome::conflicts::BlacklistAction> blacklist_action;
   };
 
-  // Creates an instance of the updater. |module_list_filter| and
-  // |installed_programs| must outlive the lifetime of this class.
-  ProblematicProgramsUpdater(const ModuleListFilter& module_list_filter,
-                             const InstalledPrograms& installed_programs);
   ~ProblematicProgramsUpdater() override;
 
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
 
-  // Returns true if the tracking of incompatible applications is enabled. The
-  // return value will not change throughout the lifetime of the process.
-  static bool IsIncompatibleApplicationsWarningEnabled();
+  // Creates an instance of the updater. Returns nullptr if the
+  // kIncompatibleApplicationsWarning experiment is disabled.
+  //
+  // |installed_programs| must outlive the lifetime of this class.
+  static std::unique_ptr<ProblematicProgramsUpdater> MaybeCreate(
+      const ModuleListFilter& module_list_filter,
+      const InstalledPrograms& installed_programs);
 
   // Returns true if the cache contains at least one problematic program.
-  // Only call this if IsIncompatibleApplicationsWarningEnabled() returns true.
   static bool HasCachedPrograms();
 
   // Returns all the cached problematic programs.
-  // Only call this if IsIncompatibleApplicationsWarningEnabled() returns true.
   static std::vector<ProblematicProgram> GetCachedPrograms();
 
   // ModuleDatabaseObserver:
@@ -63,6 +70,9 @@
   void OnModuleDatabaseIdle() override;
 
  private:
+  ProblematicProgramsUpdater(const ModuleListFilter& module_list_filter,
+                             const InstalledPrograms& installed_programs);
+
   const ModuleListFilter& module_list_filter_;
 
   const InstalledPrograms& installed_programs_;
diff --git a/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc b/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc
index 719aeaf3..faa57f8 100644
--- a/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc
+++ b/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/test_reg_util_win.h"
 #include "base/win/registry.h"
 #include "chrome/browser/conflicts/module_info_win.h"
@@ -95,6 +96,7 @@
   void SetUp() override {
     ASSERT_NO_FATAL_FAILURE(
         registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
+    scoped_feature_list_.InitAndEnableFeature(kIncompatibleApplicationsWarning);
   }
 
   enum class Option {
@@ -128,10 +130,15 @@
 
  private:
   content::TestBrowserThreadBundle test_browser_thread_bundle_;
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+
   ScopedTestingLocalState scoped_testing_local_state_;
+
   registry_util::RegistryOverrideManager registry_override_manager_;
 
   MockModuleListFilter module_list_filter_;
+
   MockInstalledPrograms installed_programs_;
 
   DISALLOW_COPY_AND_ASSIGN(ProblematicProgramsUpdaterTest);
@@ -147,9 +154,8 @@
 // ProblematicProgramsUpdater doesn't do anything when there is no registered
 // installed programs.
 TEST_F(ProblematicProgramsUpdaterTest, NoProblematicPrograms) {
-  auto problematic_programs_updater =
-      std::make_unique<ProblematicProgramsUpdater>(module_list_filter(),
-                                                   installed_programs());
+  auto problematic_programs_updater = ProblematicProgramsUpdater::MaybeCreate(
+      module_list_filter(), installed_programs());
 
   // Simulate some arbitrary module loading into the process.
   problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
@@ -163,9 +169,8 @@
 TEST_F(ProblematicProgramsUpdaterTest, OneConflict) {
   AddProblematicProgram(dll1_, L"Foo", Option::ADD_REGISTRY_ENTRY);
 
-  auto problematic_programs_updater =
-      std::make_unique<ProblematicProgramsUpdater>(module_list_filter(),
-                                                   installed_programs());
+  auto problematic_programs_updater = ProblematicProgramsUpdater::MaybeCreate(
+      module_list_filter(), installed_programs());
 
   // Simulate the module loading into the process.
   problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
@@ -182,9 +187,8 @@
   AddProblematicProgram(dll1_, L"Foo", Option::ADD_REGISTRY_ENTRY);
   AddProblematicProgram(dll2_, L"Bar", Option::ADD_REGISTRY_ENTRY);
 
-  auto problematic_programs_updater =
-      std::make_unique<ProblematicProgramsUpdater>(module_list_filter(),
-                                                   installed_programs());
+  auto problematic_programs_updater = ProblematicProgramsUpdater::MaybeCreate(
+      module_list_filter(), installed_programs());
 
   // Simulate the module loading into the process.
   problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
@@ -210,9 +214,8 @@
 TEST_F(ProblematicProgramsUpdaterTest, PersistsThroughRestarts) {
   AddProblematicProgram(dll1_, L"Foo", Option::ADD_REGISTRY_ENTRY);
 
-  auto problematic_programs_updater =
-      std::make_unique<ProblematicProgramsUpdater>(module_list_filter(),
-                                                   installed_programs());
+  auto problematic_programs_updater = ProblematicProgramsUpdater::MaybeCreate(
+      module_list_filter(), installed_programs());
 
   // Simulate the module loading into the process.
   problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
@@ -232,9 +235,8 @@
   AddProblematicProgram(dll1_, L"Foo", Option::ADD_REGISTRY_ENTRY);
   AddProblematicProgram(dll2_, L"Bar", Option::NO_REGISTRY_ENTRY);
 
-  auto problematic_programs_updater =
-      std::make_unique<ProblematicProgramsUpdater>(module_list_filter(),
-                                                   installed_programs());
+  auto problematic_programs_updater = ProblematicProgramsUpdater::MaybeCreate(
+      module_list_filter(), installed_programs());
 
   // Simulate the modules loading into the process.
   problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
diff --git a/chrome/browser/conflicts/third_party_conflicts_manager_win.cc b/chrome/browser/conflicts/third_party_conflicts_manager_win.cc
index 08b85293..0f1e100 100644
--- a/chrome/browser/conflicts/third_party_conflicts_manager_win.cc
+++ b/chrome/browser/conflicts/third_party_conflicts_manager_win.cc
@@ -97,8 +97,8 @@
 void ThirdPartyConflictsManager::InitializeProblematicProgramsUpdater() {
   DCHECK(module_list_filter_);
   DCHECK(installed_programs_);
-
-  problematic_programs_updater_ = std::make_unique<ProblematicProgramsUpdater>(
+  problematic_programs_updater_ = ProblematicProgramsUpdater::MaybeCreate(
       *module_list_filter_, *installed_programs_);
-  module_database_->AddObserver(problematic_programs_updater_.get());
+  if (problematic_programs_updater_)
+    module_database_->AddObserver(problematic_programs_updater_.get());
 }
diff --git a/chrome/browser/devtools/devtools_eye_dropper.cc b/chrome/browser/devtools/devtools_eye_dropper.cc
index b7a00c15..e412ed86 100644
--- a/chrome/browser/devtools/devtools_eye_dropper.cc
+++ b/chrome/browser/devtools/devtools_eye_dropper.cc
@@ -70,6 +70,8 @@
       false);
   video_capturer_->SetAutoThrottlingEnabled(false);
   video_capturer_->SetMinSizeChangePeriod(base::TimeDelta());
+  video_capturer_->SetFormat(media::PIXEL_FORMAT_ARGB,
+                             media::COLOR_SPACE_UNSPECIFIED);
   video_capturer_->SetMinCapturePeriod(base::TimeDelta::FromSeconds(1) /
                                        kMaxFrameRate);
 
@@ -324,22 +326,17 @@
     DLOG(ERROR) << "Shared memory mapping failed.";
     return;
   }
-  scoped_refptr<media::VideoFrame> frame = media::VideoFrame::WrapExternalData(
-      info->pixel_format, info->coded_size, info->visible_rect,
-      info->visible_rect.size(), static_cast<uint8_t*>(mapping.get()),
-      buffer_size, info->timestamp);
-  if (!frame)
-    return;
-  frame->AddDestructionObserver(base::BindOnce(
-      [](mojo::ScopedSharedBufferMapping mapping) {}, std::move(mapping)));
 
-  SkBitmap skbitmap;
-  skbitmap.allocN32Pixels(info->visible_rect.width(),
-                          info->visible_rect.height());
-  cc::SkiaPaintCanvas canvas(skbitmap);
-  video_renderer_.Copy(frame, &canvas, media::Context3D());
+  SkImageInfo image_info = SkImageInfo::MakeN32(
+      content_rect.width(), content_rect.height(), kPremul_SkAlphaType);
+  SkPixmap pixmap(image_info, mapping.get(),
+                  media::VideoFrame::RowBytes(media::VideoFrame::kARGBPlane,
+                                              info->pixel_format,
+                                              info->coded_size.width()));
+  frame_.installPixels(pixmap);
+  shared_memory_mapping_ = std::move(mapping);
+  shared_memory_releaser_ = std::move(callbacks);
 
-  frame_ = skbitmap;
   UpdateCursor();
 }
 
diff --git a/chrome/browser/devtools/devtools_eye_dropper.h b/chrome/browser/devtools/devtools_eye_dropper.h
index 9650051..7d5eaef 100644
--- a/chrome/browser/devtools/devtools_eye_dropper.h
+++ b/chrome/browser/devtools/devtools_eye_dropper.h
@@ -55,6 +55,13 @@
   void OnTargetLost(const viz::FrameSinkId& frame_sink_id) override;
   void OnStopped() override;
 
+  // This object keeps the shared memory that backs |frame_| mapped.
+  mojo::ScopedSharedBufferMapping shared_memory_mapping_;
+
+  // This object prevents FrameSinkVideoCapturer from recycling the shared
+  // memory that backs |frame_|.
+  viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr shared_memory_releaser_;
+
   EyeDropperCallback callback_;
   SkBitmap frame_;
   int last_cursor_x_;
diff --git a/chrome/browser/devtools/devtools_file_system_indexer.cc b/chrome/browser/devtools/devtools_file_system_indexer.cc
index 576a6206..4c7ad3ce 100644
--- a/chrome/browser/devtools/devtools_file_system_indexer.cc
+++ b/chrome/browser/devtools/devtools_file_system_indexer.cc
@@ -77,6 +77,8 @@
                           const Time& time);
   vector<FilePath> Search(const string& query);
   void NormalizeVectors();
+  void Reset();
+  void EnsureInitialized();
 
  private:
   FileId GetFileId(const FilePath& file_path);
@@ -148,6 +150,20 @@
 }
 
 Index::Index() : last_file_id_(0) {
+  Reset();
+}
+
+void Index::Reset() {
+  file_ids_.clear();
+  index_.clear();
+  index_times_.clear();
+  is_normalized_.clear();
+  last_file_id_ = 0;
+}
+
+void Index::EnsureInitialized() {
+  if (index_.size() != 0)
+    return;
   index_.resize(kTrigramCount);
   is_normalized_.resize(kTrigramCount);
   std::fill(is_normalized_.begin(), is_normalized_.end(), true);
@@ -155,6 +171,7 @@
 
 Time Index::LastModifiedTimeForFile(const FilePath& file_path) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  EnsureInitialized();
   Time last_modified_time;
   if (index_times_.find(file_path) != index_times_.end())
     last_modified_time = index_times_[file_path];
@@ -165,6 +182,7 @@
                                const vector<Trigram>& index,
                                const Time& time) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  EnsureInitialized();
   FileId file_id = GetFileId(file_path);
   vector<Trigram>::const_iterator it = index.begin();
   for (; it != index.end(); ++it) {
@@ -177,6 +195,7 @@
 
 vector<FilePath> Index::Search(const string& query) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  EnsureInitialized();
   const char* data = query.c_str();
   vector<TrigramChar> trigram_chars;
   trigram_chars.reserve(query.size());
@@ -221,6 +240,7 @@
 
 FileId Index::GetFileId(const FilePath& file_path) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  EnsureInitialized();
   string file_path_str = file_path.AsUTF8Unsafe();
   if (file_ids_.find(file_path) != file_ids_.end())
     return file_ids_[file_path];
@@ -230,6 +250,7 @@
 
 void Index::NormalizeVectors() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  EnsureInitialized();
   for (size_t i = 0; i < kTrigramCount; ++i) {
     if (!is_normalized_[i]) {
       std::sort(index_[i].begin(), index_[i].end());
@@ -409,9 +430,19 @@
   }
 }
 
-DevToolsFileSystemIndexer::DevToolsFileSystemIndexer() {}
+static int g_instance_count = 0;
 
-DevToolsFileSystemIndexer::~DevToolsFileSystemIndexer() {}
+DevToolsFileSystemIndexer::DevToolsFileSystemIndexer() {
+  ++g_instance_count;
+}
+
+DevToolsFileSystemIndexer::~DevToolsFileSystemIndexer() {
+  --g_instance_count;
+  if (!g_instance_count) {
+    impl_task_runner()->PostTask(
+        FROM_HERE, base::BindOnce([]() { g_trigram_index.Get().Reset(); }));
+  }
+}
 
 scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob>
 DevToolsFileSystemIndexer::IndexPath(
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 0070301..0846b6e 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -65,15 +65,19 @@
   // controlled or owner controlled.
   return true;
 }
+#endif
 
-bool IsCrosSettingReadOnly(const std::string& pref_name) {
-  if (chromeos::system::PerUserTimezoneEnabled()) {
-    // System timezone is never directly changable by user.
-    return pref_name == chromeos::kSystemTimezone;
-  }
+bool IsSettingReadOnly(const std::string& pref_name) {
+#if defined(OS_CHROMEOS)
+  // System timezone is never directly changable by the user.
+  if (pref_name == chromeos::kSystemTimezone)
+    return chromeos::system::PerUserTimezoneEnabled();
+  // enable_screen_lock must be changed through the quickUnlockPrivate API.
+  if (pref_name == ::prefs::kEnableAutoScreenLock)
+    return true;
+#endif
   return false;
 }
-#endif
 
 }  // namespace
 
@@ -254,6 +258,7 @@
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[chromeos::kAccountsPrefUsers] =
       settings_api::PrefType::PREF_TYPE_LIST;
+  // kEnableAutoScreenLock is read-only.
   (*s_whitelist)[::prefs::kEnableAutoScreenLock] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[::prefs::kEnableQuickUnlockFingerprint] =
@@ -804,10 +809,8 @@
 }
 
 bool PrefsUtil::IsPrefUserModifiable(const std::string& pref_name) {
-#if defined(OS_CHROMEOS)
-  if (IsCrosSettingReadOnly(pref_name))
+  if (IsSettingReadOnly(pref_name))
     return false;
-#endif
 
   const PrefService::Preference* profile_pref =
       profile_->GetPrefs()->FindPreference(pref_name);
diff --git a/chrome/browser/extensions/context_menu_matcher.cc b/chrome/browser/extensions/context_menu_matcher.cc
index e950cf6..caab407 100644
--- a/chrome/browser/extensions/context_menu_matcher.cc
+++ b/chrome/browser/extensions/context_menu_matcher.cc
@@ -12,6 +12,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/common/context_menu_params.h"
 #include "extensions/browser/extension_registry.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/favicon_size.h"
 #include "ui/gfx/image/image.h"
 
@@ -74,7 +75,10 @@
 
   // If this is the first extension-provided menu item, and there are other
   // items in the menu, and the last item is not a separator add a separator.
-  bool prepend_separator = *index == 0 && menu_model_->GetItemCount();
+  // Separators are not required when the context menu is a touchable app
+  // context menu.
+  const bool prepend_separator = *index == 0 && menu_model_->GetItemCount() &&
+                                 !features::IsTouchableAppContextMenuEnabled();
 
   // Extensions (other than platform apps) are only allowed one top-level slot
   // (and it can't be a radio or checkbox item because we are going to put the
@@ -259,13 +263,17 @@
   int radio_group_id = 1;
   int num_visible_items = 0;
 
+  // Separators are not required when the context menu is a touchable app
+  // context menu.
+  const bool enable_separators = !features::IsTouchableAppContextMenuEnabled();
+
   for (auto i = items.begin(); i != items.end(); ++i) {
     MenuItem* item = *i;
 
     // If last item was of type radio but the current one isn't, auto-insert
     // a separator.  The converse case is handled below.
-    if (last_type == MenuItem::RADIO &&
-        item->type() != MenuItem::RADIO) {
+    if (last_type == MenuItem::RADIO && item->type() != MenuItem::RADIO &&
+        enable_separators) {
       menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
       last_type = MenuItem::SEPARATOR;
     }
@@ -307,11 +315,12 @@
         radio_group_id++;
 
         // Auto-append a separator if needed.
-        menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
+        if (enable_separators)
+          menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
       }
 
       menu_model->AddRadioItem(menu_id, title, radio_group_id);
-    } else if (item->type() == MenuItem::SEPARATOR) {
+    } else if (item->type() == MenuItem::SEPARATOR && enable_separators) {
       menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
     }
     last_type = item->type();
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc
index 74c8c05..a96fbe0 100644
--- a/chrome/browser/infobars/infobars_browsertest.cc
+++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -10,6 +10,7 @@
 #include "base/optional.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "build/buildflag.h"
 #include "chrome/browser/banners/app_banner_infobar_delegate_desktop.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/devtools/devtools_infobar_delegate.h"
@@ -56,6 +57,7 @@
 #include "extensions/browser/test_extension_registry_observer.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/ui_features.h"
 
 #if !defined(OS_CHROMEOS)
 #include "chrome/browser/ui/startup/default_browser_infobar_delegate.h"
@@ -589,7 +591,7 @@
   ShowAndVerifyUi();
 }
 
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) && !BUILDFLAG(MAC_VIEWS_BROWSER)
 IN_PROC_BROWSER_TEST_F(InfoBarUiTest, InvokeUi_session_crashed) {
   ShowAndVerifyUi();
 }
diff --git a/chrome/browser/interstitials/chrome_metrics_helper.cc b/chrome/browser/interstitials/chrome_metrics_helper.cc
index 41585e9b..7b00fc3 100644
--- a/chrome/browser/interstitials/chrome_metrics_helper.cc
+++ b/chrome/browser/interstitials/chrome_metrics_helper.cc
@@ -10,7 +10,6 @@
 #include "chrome/common/buildflags.h"
 #include "components/history/core/browser/history_service.h"
 #include "content/public/browser/web_contents.h"
-#include "extensions/buildflags/buildflags.h"
 
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
 #include "chrome/browser/ssl/captive_portal_metrics_recorder.h"
@@ -26,7 +25,7 @@
           HistoryServiceFactory::GetForProfile(
               Profile::FromBrowserContext(web_contents->GetBrowserContext()),
               ServiceAccessType::EXPLICIT_ACCESS)),
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) || BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
       web_contents_(web_contents),
 #endif
       request_url_(request_url) {
diff --git a/chrome/browser/interstitials/chrome_metrics_helper.h b/chrome/browser/interstitials/chrome_metrics_helper.h
index caed2b8..f51fd64e 100644
--- a/chrome/browser/interstitials/chrome_metrics_helper.h
+++ b/chrome/browser/interstitials/chrome_metrics_helper.h
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "chrome/common/buildflags.h"
 #include "components/security_interstitials/core/metrics_helper.h"
-#include "extensions/buildflags/buildflags.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -39,7 +38,7 @@
   void RecordExtraShutdownMetrics() override;
 
  private:
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) || BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   content::WebContents* web_contents_;
 #endif
   const GURL request_url_;
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 6c03ace2..29f5845b 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -427,12 +427,6 @@
   return globals_;
 }
 
-void IOThread::SetGlobalsForTesting(Globals* globals) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(!globals || !globals_);
-  globals_ = globals;
-}
-
 net_log::ChromeNetLog* IOThread::net_log() {
   return net_log_;
 }
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index 05be580..12e8246 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -165,11 +165,6 @@
   // Can only be called on the IO thread.
   Globals* globals();
 
-  // Allows overriding Globals in tests where IOThread::Init() and
-  // IOThread::CleanUp() are not called.  This allows for injecting mocks into
-  // IOThread global objects.
-  void SetGlobalsForTesting(Globals* globals);
-
   net_log::ChromeNetLog* net_log();
 
   // Handles changing to On The Record mode, discarding confidential data.
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
index 6f39913..b49ae90 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
+++ b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
@@ -388,6 +388,17 @@
   EXPECT_EQ("ok-peerconnection-created", ExecuteJavascript(javascript, tab));
 }
 
+void WebRtcTestBase::SetupPeerconnectionWithConstraintsAndLocalStream(
+    content::WebContents* tab,
+    const std::string& constraints,
+    const std::string& certificate_keygen_algorithm) const {
+  std::string javascript = base::StringPrintf(
+      "preparePeerConnection(%s, %s)", certificate_keygen_algorithm.c_str(),
+      constraints.c_str());
+  EXPECT_EQ("ok-peerconnection-created", ExecuteJavascript(javascript, tab));
+  EXPECT_EQ("ok-added", ExecuteJavascript("addLocalStream()", tab));
+}
+
 std::string WebRtcTestBase::CreateLocalOffer(
     content::WebContents* from_tab) const {
   std::string response = ExecuteJavascript("createLocalOffer({})", from_tab);
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest_base.h b/chrome/browser/media/webrtc/webrtc_browsertest_base.h
index 0c12cbfd..e2f9975 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest_base.h
+++ b/chrome/browser/media/webrtc/webrtc_browsertest_base.h
@@ -147,6 +147,13 @@
   void SetupPeerconnectionWithCertificateWithoutLocalStream(
       content::WebContents* tab,
       const std::string& certificate) const;
+  // Same as |SetupPeerconnectionWithLocalStream| except RTCPeerConnection
+  // constraints are specified.
+  void SetupPeerconnectionWithConstraintsAndLocalStream(
+      content::WebContents* tab,
+      const std::string& constraints,
+      const std::string& certificate_keygen_algorithm =
+          kUseDefaultCertKeygen) const;
 
   void CreateDataChannel(content::WebContents* tab, const std::string& label);
 
diff --git a/chrome/browser/media/webrtc/webrtc_video_display_perf_browsertest.cc b/chrome/browser/media/webrtc/webrtc_video_display_perf_browsertest.cc
index 00bdd82..f87ff67 100644
--- a/chrome/browser/media/webrtc/webrtc_video_display_perf_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_video_display_perf_browsertest.cc
@@ -152,8 +152,12 @@
     const int process_id = base::GetProcId(
         right_tab->GetRenderViewHost()->GetProcess()->GetHandle());
 
-    SetupPeerconnectionWithLocalStream(left_tab);
-    SetupPeerconnectionWithLocalStream(right_tab);
+    const std::string disable_cpu_adaptation_constraint(
+        "{'optional': [{'googCpuOveruseDetection': false}]}");
+    SetupPeerconnectionWithConstraintsAndLocalStream(
+        left_tab, disable_cpu_adaptation_constraint);
+    SetupPeerconnectionWithConstraintsAndLocalStream(
+        right_tab, disable_cpu_adaptation_constraint);
 
     if (!video_codec.empty()) {
       SetDefaultVideoCodec(left_tab, video_codec,
diff --git a/chrome/browser/metrics/BUILD.gn b/chrome/browser/metrics/BUILD.gn
index dac8ad8..5401072 100644
--- a/chrome/browser/metrics/BUILD.gn
+++ b/chrome/browser/metrics/BUILD.gn
@@ -12,4 +12,5 @@
   namespace = "chrome_metrics"
   header_filename = "expired_histograms_array.h"
   major_branch_date_filepath = "//chrome/MAJOR_BRANCH_DATE"
+  milestone_filepath = "//chrome/VERSION"
 }
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index 5e9f503..3d418fcf 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -67,6 +67,18 @@
   return network_context;
 }
 
+network::mojom::NetworkContextPtr
+ProfileNetworkContextService::CreateNetworkContextForPartition(
+    bool in_memory,
+    const base::FilePath& relative_partition_path) {
+  DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
+  network::mojom::NetworkContextPtr network_context;
+  content::GetNetworkService()->CreateNetworkContext(
+      MakeRequest(&network_context),
+      CreateNetworkContextParams(in_memory, relative_partition_path));
+  return network_context;
+}
+
 void ProfileNetworkContextService::SetUpProfileIODataMainContext(
     network::mojom::NetworkContextRequest* network_context_request,
     network::mojom::NetworkContextParamsPtr* network_context_params) {
@@ -125,6 +137,14 @@
 
 network::mojom::NetworkContextParamsPtr
 ProfileNetworkContextService::CreateMainNetworkContextParams() {
+  return CreateNetworkContextParams(profile_->IsOffTheRecord(),
+                                    base::FilePath());
+}
+
+network::mojom::NetworkContextParamsPtr
+ProfileNetworkContextService::CreateNetworkContextParams(
+    bool in_memory,
+    const base::FilePath& relative_partition_path) {
   // TODO(mmenke): Set up parameters here.
   network::mojom::NetworkContextParamsPtr network_context_params =
       CreateDefaultNetworkContextParams();
@@ -136,13 +156,17 @@
   // Always enable the HTTP cache.
   network_context_params->http_cache_enabled = true;
 
+  base::FilePath path = profile_->GetPath();
+  if (!relative_partition_path.empty())
+    path = path.Append(relative_partition_path);
+
   // Configure on-disk storage for non-OTR profiles. OTR profiles just use
   // default behavior (in memory storage, default sizes).
   PrefService* prefs = profile_->GetPrefs();
-  if (!profile_->IsOffTheRecord()) {
+  if (!in_memory) {
     // Configure the HTTP cache path and size.
     base::FilePath base_cache_path;
-    chrome::GetUserCacheDirectory(profile_->GetPath(), &base_cache_path);
+    chrome::GetUserCacheDirectory(path, &base_cache_path);
     base::FilePath disk_cache_dir = prefs->GetFilePath(prefs::kDiskCacheDir);
     if (!disk_cache_dir.empty())
       base_cache_path = disk_cache_dir.Append(base_cache_path.BaseName());
@@ -154,20 +178,26 @@
     // Currently this just contains HttpServerProperties, but that will likely
     // change.
     network_context_params->http_server_properties_path =
-        profile_->GetPath().Append(chrome::kNetworkPersistentStateFilename);
+        path.Append(chrome::kNetworkPersistentStateFilename);
 
-    base::FilePath cookie_path = profile_->GetPath();
+    base::FilePath cookie_path = path;
     cookie_path = cookie_path.Append(chrome::kCookieFilename);
     network_context_params->cookie_path = cookie_path;
 
-    base::FilePath channel_id_path = profile_->GetPath();
+    base::FilePath channel_id_path = path;
     channel_id_path = channel_id_path.Append(chrome::kChannelIDFilename);
     network_context_params->channel_id_path = channel_id_path;
 
-    network_context_params->restore_old_session_cookies =
-        profile_->ShouldRestoreOldSessionCookies();
-    network_context_params->persist_session_cookies =
-        profile_->ShouldPersistSessionCookies();
+    if (relative_partition_path.empty()) {
+      network_context_params->restore_old_session_cookies =
+          profile_->ShouldRestoreOldSessionCookies();
+      network_context_params->persist_session_cookies =
+          profile_->ShouldPersistSessionCookies();
+    } else {
+      // Copy behavior of ProfileImplIOData::InitializeAppRequestContext.
+      network_context_params->restore_old_session_cookies = false;
+      network_context_params->persist_session_cookies = false;
+    }
   }
 
   // NOTE(mmenke): Keep these protocol handlers and
diff --git a/chrome/browser/net/profile_network_context_service.h b/chrome/browser/net/profile_network_context_service.h
index d663b63..eb53691 100644
--- a/chrome/browser/net/profile_network_context_service.h
+++ b/chrome/browser/net/profile_network_context_service.h
@@ -30,6 +30,12 @@
   // SetUpProfileIODataMainContext.
   network::mojom::NetworkContextPtr CreateMainNetworkContext();
 
+  // Create a network context for the given |relative_parition_path|. This is
+  // only used when the network service is enabled for now.
+  network::mojom::NetworkContextPtr CreateNetworkContextForPartition(
+      bool in_memory,
+      const base::FilePath& relative_partition_path);
+
   // Initializes |*network_context_params| to set up the ProfileIOData's
   // main URLRequestContext and |*network_context_request| to be one end of a
   // Mojo pipe to be bound to the NetworkContext for that URLRequestContext.
@@ -73,6 +79,13 @@
   // it initializes some class members.
   network::mojom::NetworkContextParamsPtr CreateMainNetworkContextParams();
 
+  // Creates parameters for the NetworkContext. Use |in_memory| instead of
+  // |profile_->IsOffTheRecord()| because sometimes normal profiles want off the
+  // record partitions (e.g. for webview tag).
+  network::mojom::NetworkContextParamsPtr CreateNetworkContextParams(
+      bool in_memory,
+      const base::FilePath& relative_partition_path);
+
   Profile* const profile_;
 
   ProxyConfigMonitor proxy_config_monitor_;
diff --git a/chrome/browser/notifications/notification_launch_id.h b/chrome/browser/notifications/notification_launch_id.h
index 8cc5322c..2a40e64 100644
--- a/chrome/browser/notifications/notification_launch_id.h
+++ b/chrome/browser/notifications/notification_launch_id.h
@@ -16,6 +16,7 @@
   NotificationLaunchId();
   NotificationLaunchId(const NotificationLaunchId& other);
 
+  // |notification_id| and |profile_id| must be UTF8 strings.
   NotificationLaunchId(NotificationHandler::Type notification_type,
                        const std::string& notification_id,
                        const std::string& profile_id,
@@ -48,40 +49,61 @@
     DCHECK(is_valid());
     return notification_type_;
   }
+
   const std::string& notification_id() const {
     DCHECK(is_valid());
     return notification_id_;
   }
+
   const std::string& profile_id() const {
     DCHECK(is_valid());
     return profile_id_;
   }
+
   bool incognito() const {
     DCHECK(is_valid());
     return incognito_;
   }
+
   const GURL& origin_url() const {
     DCHECK(is_valid());
     return origin_url_;
   }
+
   int button_index() const {
     DCHECK(is_valid());
     return button_index_;
   }
+
   bool is_for_context_menu() const {
     DCHECK(is_valid());
     return is_for_context_menu_;
   }
 
  private:
+  // The notification type this launch ID is associated with.
   NotificationHandler::Type notification_type_;
+
+  // The notification id this launch ID is associated with. The string is UTF8.
   std::string notification_id_;
+
+  // The profile id this launch ID is associated with. The string is UTF8.
   std::string profile_id_;
+
+  // A flat indicating if the notification associated with this launch ID is in
+  // incognito mode or not.
   bool incognito_ = false;
+
+  // The original URL this launch ID is associated with.
   GURL origin_url_;
+
+  // The button index this launch ID is associated with.
   int button_index_ = -1;
+
+  // A flag indicating if this launch ID is targeting for context menu or not.
   bool is_for_context_menu_ = false;
 
+  // A flag indicating if this launch ID is valid.
   bool is_valid_ = false;
 };
 
diff --git a/chrome/browser/notifications/notification_platform_bridge_win.cc b/chrome/browser/notifications/notification_platform_bridge_win.cc
index 65a01d49..577e8df 100644
--- a/chrome/browser/notifications/notification_platform_bridge_win.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_win.cc
@@ -748,8 +748,8 @@
     const base::CommandLine& command_line) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  NotificationLaunchId launch_id(
-      command_line.GetSwitchValueASCII(switches::kNotificationLaunchId));
+  NotificationLaunchId launch_id(base::UTF16ToUTF8(
+      command_line.GetSwitchValueNative(switches::kNotificationLaunchId)));
   if (!launch_id.is_valid())
     return false;
 
@@ -773,8 +773,8 @@
 
 // static
 std::string NotificationPlatformBridgeWin::GetProfileIdFromLaunchId(
-    const std::string& launch_id_str) {
-  NotificationLaunchId launch_id(launch_id_str);
+    const base::string16& launch_id_str) {
+  NotificationLaunchId launch_id(base::UTF16ToUTF8(launch_id_str));
   return launch_id.is_valid() ? launch_id.profile_id() : std::string();
 }
 
diff --git a/chrome/browser/notifications/notification_platform_bridge_win.h b/chrome/browser/notifications/notification_platform_bridge_win.h
index fab79ac4..cf2e778 100644
--- a/chrome/browser/notifications/notification_platform_bridge_win.h
+++ b/chrome/browser/notifications/notification_platform_bridge_win.h
@@ -48,7 +48,8 @@
   static bool HandleActivation(const base::CommandLine& command_line);
 
   // Extracts the profile ID from |launch_id_str|.
-  static std::string GetProfileIdFromLaunchId(const std::string& launch_id_str);
+  static std::string GetProfileIdFromLaunchId(
+      const base::string16& launch_id_str);
 
   // Checks if native notification is enabled.
   static bool NativeNotificationEnabled();
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 91b4eb6..a1bb49f 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -272,7 +272,6 @@
 #include "chrome/browser/apps/app_launch_for_metro_restart_win.h"
 #include "chrome/browser/component_updater/sw_reporter_installer_win.h"
 #if defined(GOOGLE_CHROME_BUILD)
-#include "chrome/browser/conflicts/module_database_win.h"
 #include "chrome/browser/conflicts/problematic_programs_updater_win.h"
 #endif  // defined(GOOGLE_CHROME_BUILD)
 #include "chrome/browser/safe_browsing/chrome_cleaner/settings_resetter_win.h"
@@ -467,7 +466,6 @@
   desktop_ios_promotion::RegisterLocalPrefs(registry);
   password_manager::PasswordManager::RegisterLocalPrefs(registry);
 #if defined(GOOGLE_CHROME_BUILD)
-  ModuleDatabase::RegisterLocalStatePrefs(registry);
   ProblematicProgramsUpdater::RegisterLocalStatePrefs(registry);
 #endif  // defined(GOOGLE_CHROME_BUILD)
 #endif
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 98bd3dc6..e7f8c66 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -1482,9 +1482,3 @@
   return base::WrapUnique<net::NetworkDelegate>(
       chrome_network_delegate.release());
 }
-
-void ProfileIOData::SetCookieSettingsForTesting(
-    content_settings::CookieSettings* cookie_settings) {
-  DCHECK(!cookie_settings_.get());
-  cookie_settings_ = cookie_settings;
-}
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index 0fa7a65..176ae1f93 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -460,9 +460,6 @@
       net::HttpTransactionFactory* main_http_factory,
       std::unique_ptr<net::HttpCache::BackendFactory> backend) const;
 
-  void SetCookieSettingsForTesting(
-      content_settings::CookieSettings* cookie_settings);
-
  private:
   class ResourceContext : public content::ResourceContext {
    public:
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 9bbe0de..d6efe0a 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -2417,7 +2417,8 @@
       headers = data_reduction_proxy::chrome_proxy_pass_through_header();
     }
 
-    source_web_contents_->SaveFrameWithHeaders(url, referrer, headers);
+    source_web_contents_->SaveFrameWithHeaders(url, referrer, headers,
+                                               params_.suggested_filename);
   }
 }
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
index 1409c10..085556b 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -553,3 +553,30 @@
 
   DestroyDataReductionProxySettings();
 }
+
+// Verify that the suggested file name is propagated to web contents when save a
+// media file in context menu.
+TEST_F(RenderViewContextMenuPrefsTest, SaveMediaSuggestedFileName) {
+  const base::string16 kTestSuggestedFileName = base::ASCIIToUTF16("test_file");
+  content::ContextMenuParams params = CreateParams(MenuItem::VIDEO);
+  params.suggested_filename = kTestSuggestedFileName;
+  auto menu = std::make_unique<TestRenderViewContextMenu>(
+      web_contents()->GetMainFrame(), params);
+  menu->ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEAVAS, 0 /* event_flags */);
+
+  // Video item should have suggested file name.
+  base::string16 suggested_filename =
+      content::WebContentsTester::For(web_contents())->GetSuggestedFileName();
+  EXPECT_EQ(kTestSuggestedFileName, suggested_filename);
+
+  params = CreateParams(MenuItem::AUDIO);
+  params.suggested_filename = kTestSuggestedFileName;
+  menu = std::make_unique<TestRenderViewContextMenu>(
+      web_contents()->GetMainFrame(), params);
+  menu->ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEAVAS, 0 /* event_flags */);
+
+  // Audio item should have suggested file name.
+  suggested_filename =
+      content::WebContentsTester::For(web_contents())->GetSuggestedFileName();
+  EXPECT_EQ(kTestSuggestedFileName, suggested_filename);
+}
diff --git a/chrome/browser/resource_coordinator/tab_manager_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
index d201205..0210b78 100644
--- a/chrome/browser/resource_coordinator/tab_manager_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
@@ -1294,10 +1294,10 @@
 
   // |ignored_web_contents| is not managed by TabManager, thus will be ignored
   // and shouldn't cause any crash or side effect.
-  WebContents* ignored_web_contents =
-      WebContentsTester::CreateTestWebContents(browser_context(), nullptr);
+  std::unique_ptr<WebContents> ignored_web_contents(
+      WebContentsTester::CreateTestWebContents(browser_context(), nullptr));
   tab_manager_->resource_coordinator_signal_observer_->OnPageAlmostIdle(
-      ignored_web_contents);
+      ignored_web_contents.get());
   EXPECT_TRUE(tab_manager_->IsTabLoadingForTest(contents3_.get()));
   EXPECT_FALSE(tab_manager_->IsNavigationDelayedForTest(nav_handle3_.get()));
 }
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.css b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.css
new file mode 100644
index 0000000..25fe468
--- /dev/null
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.css
@@ -0,0 +1,10 @@
+/* Copyright 2018 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+#value-prop {
+  display: flex;
+  flex-flow: column;
+  font-size: 16px;
+  width: 576px;
+}
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.html
new file mode 100644
index 0000000..566bc9a
--- /dev/null
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.html
@@ -0,0 +1,47 @@
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<!doctype html>
+<html i18n-values="dir:textdirection;lang:language">
+<head>
+<meta charset="utf-8">
+
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://oobe/custom_elements.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/util.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+
+<include src="../login/oobe_buttons.html">
+<include src="../login/oobe_change_picture.html">
+<include src="../login/oobe_dialog.html">
+<include src="assistant_value_prop.html">
+
+<link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
+<link rel="stylesheet" href="../login/oobe_dialog_parameters.css">
+<link rel="stylesheet" href="../login/oobe_flex_layout.css">
+<link rel="stylesheet" href="../login/oobe_text_button.css">
+<link rel="stylesheet" href="../login/oobe_screen.css">
+<link rel="stylesheet" href="assistant_optin.css">
+
+<script src="chrome://resources/js/cr.js"></script>
+<script src="chrome://resources/js/load_time_data.js"></script>
+<script src="chrome://resources/js/util.js"></script>
+<script src="strings.js"></script>
+<script src="assistant_optin.js"></script>
+</head>
+<body>
+
+<div id="value-prop" role="group">
+   <assistant-value-prop-md id="value-prop-md">
+   </assistant-value-prop-md>
+</div>
+
+<script src="chrome://resources/js/i18n_template.js"></script>
+
+</body>
+</html>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.js
new file mode 100644
index 0000000..3ed6a7c4
--- /dev/null
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin.js
@@ -0,0 +1,24 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// <include src="../login/oobe_types.js">
+// <include src="../login/oobe_buttons.js">
+// <include src="../login/oobe_change_picture.js">
+// <include src="../login/oobe_dialog.js">
+// <include src="assistant_value_prop.js">
+
+cr.define('assistantOptin', function() {
+  return {
+
+    // Starts the assistant opt-in flow.
+    Show: function() {
+      $('value-prop-md').locale = loadTimeData.getString('locale');
+      $('value-prop-md').onShow();
+    }
+  };
+});
+
+document.addEventListener('DOMContentLoaded', function() {
+  assistantOptin.Show();
+});
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.css b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.css
new file mode 100644
index 0000000..36d0b2b
--- /dev/null
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.css
@@ -0,0 +1,74 @@
+/* Copyright 2018 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+#value-prop-view {
+  display: block;
+  height: 372px;
+  margin: auto;
+  padding: 0;
+}
+
+.value-prop-loading #retry-button,
+.value-prop-loaded #retry-button,
+.value-prop-error #next-button {
+  display: none;
+}
+
+#loading-container,
+#error-container,
+#view-container {
+  position: absolute;
+  visibility: hidden;
+  width: 480px;
+}
+
+.value-prop-loading-animation #loading-container,
+.value-prop-loaded #view-container,
+.value-prop-error #error-container {
+  visibility: visible;
+}
+
+.value-prop-loading #retry-button,
+.value-prop-loading #next-button,
+.value-prop-loading #skip-button {
+  pointer-events: none;
+}
+
+.title {
+  font-size: 18px;
+}
+
+#subtitle {
+  font-size: 12px;
+  padding: 14px 0 0 0;
+}
+
+#loading-message {
+  color: rgba(0, 0, 0, .54);
+  font-size: 12px;
+  line: 17px;
+  padding: 168px 0 0 0;
+}
+
+paper-progress {
+  --paper-progress-active-color: rgb(66, 133, 244);
+  --paper-progress-container-color: #CEE0FC;
+  --paper-progress-secondary-color: rgb(66, 133, 244);
+  display: block;
+  height: 3px;
+  padding: 4px 0 0 0;
+  width: 100%;
+}
+
+#skip-button {
+  color: rgb(66, 133, 244);
+}
+
+#retry-button {
+  -webkit-margin-end: 18px;
+}
+
+#next-button {
+  -webkit-margin-end: 18px;
+}
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.html
new file mode 100644
index 0000000..e5125ca
--- /dev/null
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.html
@@ -0,0 +1,47 @@
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<dom-module id="assistant-value-prop-md">
+  <template>
+    <link rel="stylesheet" href="../login/oobe_dialog_parameters.css">
+    <link rel="stylesheet" href="../login/oobe_flex_layout.css">
+    <link rel="stylesheet" href="assistant_value_prop.css">
+    <oobe-dialog id="value-prop-dialog" class="value-prop-loading" role="dialog"
+        hide-shadow has-buttons no-footer-padding android>
+      <div class = "header">
+        <div id="view-container">
+          <webview id="value-prop-view"></webview>
+        </div>
+        <div id="error-container">
+          <div class="title"
+            i18n-content="voiceInteractionValuePropLoadErrorTitle"></div>
+          <div id="subtitle">
+            <div i18n-content="voiceInteractionValuePropLoadErrorMessage"></div>
+          </div>
+        </div>
+        <div id="loading-container">
+          <div id="loading-message">
+            <div i18n-content="voiceInteractionValuePropLoading"></div>
+          </div>
+          <paper-progress class="slow" indeterminate></paper-progress>
+        </div>
+      </div>
+      <div class="bottom-buttons flex layout horizontal">
+        <oobe-text-button id="skip-button" android on-tap="onSkipTap_"
+            disabled="[[valuePropButtonsDisabled]]">
+         <div i18n-content="voiceInteractionValuePropSkipButton"></div>
+        </oobe-text-button>
+        <div class="flex"></div>
+        <oobe-text-button id="retry-button" inverse android on-tap="onRetryTap_"
+            disabled="[[valuePropButtonsDisabled]]">
+         <div i18n-content="voiceInteractionValuePropRetryButton"></div>
+        </oobe-text-button>
+        <oobe-text-button id="next-button" inverse android
+            on-tap="onNextTap_" disabled="[[valuePropButtonsDisabled]]">
+         <div i18n-content="voiceInteractionValuePropNextButton"></div>
+        </oobe-text-button>
+      </div>
+    </oobe-dialog>
+  </template>
+</dom-module>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js
new file mode 100644
index 0000000..0735e63
--- /dev/null
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_value_prop.js
@@ -0,0 +1,254 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Polymer element for displaying material design voice
+ * interaction value prop screen.
+ */
+
+Polymer({
+  is: 'assistant-value-prop-md',
+
+  properties: {
+    /**
+     * Buttons are disabled when the value prop content is loading.
+     */
+    valuePropButtonsDisabled: {
+      type: Boolean,
+      value: true,
+    },
+
+    /**
+     * System locale.
+     */
+    locale: {
+      type: String,
+    },
+
+    /**
+     * Default url for local en.
+     */
+    defaultUrl: {
+      type: String,
+      value:
+          'https://www.gstatic.com/opa-chromeos/oobe/en/value_proposition.html',
+    },
+  },
+
+  /**
+   * Whether try to reload with the default url when a 404 error occurred.
+   * @type {boolean}
+   * @private
+   */
+  reloadWithDefaultUrl_: false,
+
+  /**
+   * Whether an error occurs while the webview is loading.
+   * @type: {boolean}
+   * @private
+   */
+  valuePropError_: false,
+
+  /**
+   * Timeout ID for loading animation.
+   * @type {number}
+   * @private
+   */
+  animationTimeout_: null,
+
+  /**
+   * Timeout ID for loading (will fire an error).
+   * @type {number}
+   * @private
+   */
+  loadingTimeout_: null,
+
+  /**
+   * The value prop view object.
+   * @type {Object}
+   * @private
+   */
+  valuePropView_: null,
+
+  /**
+   * Whether the screen has been initialized.
+   * @type {boolean}
+   * @private
+   */
+  initialized_: false,
+
+  /**
+   * Whether the response header has been received for the value prop view
+   * @type: {boolean}
+   * @private
+   */
+  headerReceived_: false,
+
+  /**
+   * On-tap event handler for skip button.
+   *
+   * @private
+   */
+  onSkipTap_: function() {
+    chrome.send('AssistantValuePropScreen.userActed', ['skip-pressed']);
+  },
+
+  /**
+   * On-tap event handler for retry button.
+   *
+   * @private
+   */
+  onRetryTap_: function() {
+    this.reloadValueProp();
+  },
+
+  /**
+   * On-tap event handler for next button.
+   *
+   * @private
+   */
+  onNextTap_: function() {
+    chrome.send('AssistantValuePropScreen.userActed', ['next-pressed']);
+  },
+
+  /**
+   * Add class to the list of classes of root elements.
+   * @param {string} className class to add
+   *
+   * @private
+   */
+  addClass_: function(className) {
+    this.$['value-prop-dialog'].classList.add(className);
+  },
+
+  /**
+   * Remove class to the list of classes of root elements.
+   * @param {string} className class to remove
+   *
+   * @private
+   */
+  removeClass_: function(className) {
+    this.$['value-prop-dialog'].classList.remove(className);
+  },
+
+  /**
+   * Reloads value prop.
+   */
+  reloadValueProp: function() {
+    this.valuePropError_ = false;
+    this.headerReceived_ = false;
+    this.valuePropView_.src = 'https://www.gstatic.com/opa-chromeos/oobe/' +
+        this.locale + '/value_proposition.html';
+
+    window.clearTimeout(this.animationTimeout_);
+    window.clearTimeout(this.loadingTimeout_);
+    this.removeClass_('value-prop-loaded');
+    this.removeClass_('value-prop-error');
+    this.addClass_('value-prop-loading');
+    this.valuePropButtonsDisabled = true;
+
+    this.animationTimeout_ = window.setTimeout(function() {
+      this.addClass_('value-prop-loading-animation');
+    }.bind(this), 500);
+    this.loadingTimeout_ = window.setTimeout(function() {
+      this.onValueViewErrorOccurred();
+    }.bind(this), 5000);
+  },
+
+  /**
+   * Handles event when value prop view cannot be loaded.
+   */
+  onValueViewErrorOccurred: function(details) {
+    this.valuePropError_ = true;
+    window.clearTimeout(this.animationTimeout_);
+    window.clearTimeout(this.loadingTimeout_);
+    this.removeClass_('value-prop-loading-animation');
+    this.removeClass_('value-prop-loading');
+    this.removeClass_('value-prop-loaded');
+    this.addClass_('value-prop-error');
+
+    this.valuePropButtonsDisabled = false;
+    this.$['retry-button'].focus();
+  },
+
+  /**
+   * Handles event when value prop view is loaded.
+   */
+  onValueViewContentLoad: function(details) {
+    if (details == null) {
+      return;
+    }
+    if (this.valuePropError_ || !this.headerReceived_) {
+      return;
+    }
+    if (this.reloadWithDefaultUrl_) {
+      this.valuePropView_.src = this.defaultUrl;
+      this.headerReceived_ = false;
+      this.reloadWithDefaultUrl_ = false;
+      return;
+    }
+
+    window.clearTimeout(this.animationTimeout_);
+    window.clearTimeout(this.loadingTimeout_);
+    this.removeClass_('value-prop-loading-animation');
+    this.removeClass_('value-prop-loading');
+    this.removeClass_('value-prop-error');
+    this.addClass_('value-prop-loaded');
+
+    this.valuePropButtonsDisabled = false;
+    this.$['next-button'].focus();
+  },
+
+  /**
+   * Handles event when webview request headers received.
+   */
+  onValueViewHeadersReceived: function(details) {
+    if (details == null) {
+      return;
+    }
+    this.headerReceived_ = true;
+    if (details.statusCode == '404') {
+      if (details.url != this.defaultUrl) {
+        this.reloadWithDefaultUrl_ = true;
+        return;
+      } else {
+        this.onValueViewErrorOccurred();
+      }
+    }
+    if (details.statusCode != '200') {
+      this.onValueViewErrorOccurred();
+    }
+  },
+
+  /**
+   * Signal from host to show the screen.
+   */
+  onShow: function() {
+    var requestFilter = {urls: ['<all_urls>'], types: ['main_frame']};
+    this.valuePropView_ = this.$['value-prop-view'];
+
+    if (!this.initialized_) {
+      this.valuePropView_.request.onErrorOccurred.addListener(
+          this.onValueViewErrorOccurred.bind(this), requestFilter);
+      this.valuePropView_.request.onHeadersReceived.addListener(
+          this.onValueViewHeadersReceived.bind(this), requestFilter);
+      this.valuePropView_.request.onCompleted.addListener(
+          this.onValueViewContentLoad.bind(this), requestFilter);
+
+      this.valuePropView_.addContentScripts([{
+        name: 'stripLinks',
+        matches: ['<all_urls>'],
+        js: {
+          code: 'document.querySelectorAll(\'a\').forEach(' +
+              'function(anchor){anchor.href=\'javascript:void(0)\';})'
+        },
+        run_at: 'document_end'
+      }]);
+
+      this.initialized_ = true;
+    }
+
+    this.reloadValueProp();
+  },
+});
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index 78dc8f5d..fccd3d7 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -101,8 +101,11 @@
         <include name="IDR_ARC_SUPPORT_ICON_HEADER" file="chromeos/arc_support/images/header.png" type="BINDATA" />
       </if>
       <if expr="enable_plugins">
-        <include name="IDR_PDF_INDEX_CSS" file="pdf/index.css" allowexternalscript="true" type="BINDATA" />
+        <!-- Note that resources included here also must be included in
+             print_preview_ui.cc such these resources will be exposed to
+             PDF in print preview. -->
         <include name="IDR_PDF_INDEX_HTML" file="pdf/index.html" allowexternalscript="true" type="BINDATA" />
+        <include name="IDR_PDF_INDEX_CSS" file="pdf/index.css" allowexternalscript="true" type="BINDATA" />
         <include name="IDR_PDF_MAIN_JS" file="pdf/main.js" type="BINDATA" />
         <include name="IDR_PDF_PDF_JS" file="pdf/pdf.js" type="BINDATA" />
         <include name="IDR_PDF_UI_MANAGER_JS" file="pdf/toolbar_manager.js" type="BINDATA" />
@@ -115,7 +118,6 @@
         <include name="IDR_PDF_ZOOM_MANAGER_JS" file="pdf/zoom_manager.js" type="BINDATA" />
         <include name="IDR_PDF_GESTURE_DETECTOR_JS" file="pdf/gesture_detector.js" type="BINDATA" />
         <include name="IDR_PDF_BROWSER_API_JS" file="pdf/browser_api.js" type="BINDATA" />
-        <include name="IDR_PDF_CONTENT_SCRIPT_JS" file="pdf/content_script.js" type="BINDATA" />
         <include name="IDR_PDF_METRICS_JS" file="pdf/metrics.js" type="BINDATA" />
         <include name="IDR_PDF_COORDS_TRANSFORMER_JS" file="pdf/coords_transformer.js" type="BINDATA" />
 
diff --git a/chrome/browser/resources/md_extensions/options_dialog.html b/chrome/browser/resources/md_extensions/options_dialog.html
index 5c5abc51..524b35a 100644
--- a/chrome/browser/resources/md_extensions/options_dialog.html
+++ b/chrome/browser/resources/md_extensions/options_dialog.html
@@ -22,19 +22,26 @@
 
       ExtensionOptions {
         display: block;
-        min-width: 300px;
+        height: 100%;
         overflow: hidden;
       }
 
       dialog {
         --scroll-border: 0;
-        width: fit-content;
         --cr-dialog-body: {
+          height: 100%;
           padding: 0;
         };
 
+        --cr-dialog-wrapper: {
+          height: 100%;
+          max-height: initial;
+          overflow: hidden;
+        };
+
         --cr-dialog-body-container: {
           border: none;
+          height: 100%;
           min-height: initial;
         };
       }
diff --git a/chrome/browser/resources/md_extensions/options_dialog.js b/chrome/browser/resources/md_extensions/options_dialog.js
index d9ecf14..e5dbb639 100644
--- a/chrome/browser/resources/md_extensions/options_dialog.js
+++ b/chrome/browser/resources/md_extensions/options_dialog.js
@@ -37,10 +37,27 @@
       data_: Object,
     },
 
+    /** @private {?Function} */
+    boundResizeListener_: null,
+
     get open() {
       return this.$$('dialog').open;
     },
 
+    /**
+     * Resizes the dialog to the given width/height, taking into account the
+     * window width/height.
+     * @param {number} width
+     * @param {number} height
+     * @private
+     */
+    updateDialogSize_: function(width, height) {
+      const effectiveHeight = Math.min(window.innerHeight, height);
+      const effectiveWidth = Math.min(window.innerWidth, width);
+      this.$.dialog.style.height = effectiveHeight + 'px';
+      this.$.dialog.style.width = effectiveWidth + 'px';
+    },
+
     /** @param {chrome.developerPrivate.ExtensionInfo} data */
     show: function(data) {
       this.data_ = data;
@@ -50,15 +67,25 @@
         this.extensionOptions_.extension = this.data_.id;
         this.extensionOptions_.onclose = this.close.bind(this);
 
+        let preferredSize = null;
         const onSizeChanged = e => {
-          this.extensionOptions_.style.height = e.height + 'px';
-          this.extensionOptions_.style.width = e.width + 'px';
-
-          if (!this.$$('dialog').open)
-            this.$$('dialog').showModal();
+          preferredSize = e;
+          this.updateDialogSize_(preferredSize.width, preferredSize.height);
+          this.$$('dialog').showModal();
+          this.extensionOptions_.onpreferredsizechanged = null;
         };
 
+        this.boundResizeListener_ = () => {
+          this.updateDialogSize_(preferredSize.width, preferredSize.height);
+        };
+
+        // Only handle the 1st instance of 'preferredsizechanged' event, to get
+        // the preferred size.
         this.extensionOptions_.onpreferredsizechanged = onSizeChanged;
+
+        // Add a 'resize' such that the dialog is resized when window size
+        // changes.
+        window.addEventListener('resize', this.boundResizeListener_);
         this.$.body.appendChild(this.extensionOptions_);
       });
     },
@@ -70,6 +97,11 @@
 
     /** @private */
     onClose_: function() {
+      if (this.boundResizeListener_) {
+        window.removeEventListener('resize', this.boundResizeListener_);
+        this.boundResizeListener_ = null;
+      }
+
       const currentPage = extensions.navigation.getCurrentPage();
       // We update the page when the options dialog closes, but only if we're
       // still on the details page. We could be on a different page if the
diff --git a/chrome/browser/resources/pdf/content_script.js b/chrome/browser/resources/pdf/content_script.js
deleted file mode 100644
index dce77e6..0000000
--- a/chrome/browser/resources/pdf/content_script.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2015 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.
-
-// This is to work-around an issue where this extension is not granted
-// permission to access chrome://resources when iframed for print preview.
-// See https://crbug.com/444752.
diff --git a/chrome/browser/resources/pdf/index.html b/chrome/browser/resources/pdf/index.html
index 69c1c39c..dcec3cc 100644
--- a/chrome/browser/resources/pdf/index.html
+++ b/chrome/browser/resources/pdf/index.html
@@ -1,7 +1,6 @@
 <!doctype html>
 <html>
 <head>
-  <base href="chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/">
   <meta charset="utf-8">
   <link rel="import" href="elements/viewer-error-screen/viewer-error-screen.html">
   <link rel="import" href="elements/viewer-page-indicator/viewer-page-indicator.html">
diff --git a/chrome/browser/resources/pdf/manifest.json b/chrome/browser/resources/pdf/manifest.json
index c3d0a80..6e09e51 100644
--- a/chrome/browser/resources/pdf/manifest.json
+++ b/chrome/browser/resources/pdf/manifest.json
@@ -16,11 +16,5 @@
     "application/pdf"
   ],
   "content_security_policy": "script-src 'self' blob: filesystem: chrome://resources; object-src * blob: externalfile: file: filesystem: data:; plugin-types application/x-google-chrome-pdf",
-  "mime_types_handler": "index.html",
-  "web_accessible_resources": [
-    "*.js",
-    "*.html",
-    "*.css",
-    "*.png"
-  ]
+  "mime_types_handler": "index.html"
 }
diff --git a/chrome/browser/resources/pdf/pdf_scripting_api.js b/chrome/browser/resources/pdf/pdf_scripting_api.js
index a133b52..b4ed5b9b 100644
--- a/chrome/browser/resources/pdf/pdf_scripting_api.js
+++ b/chrome/browser/resources/pdf/pdf_scripting_api.js
@@ -231,13 +231,14 @@
  * interface which provides access to various features of the viewer for use
  * by print preview and accessibility.
  * @param {string} src the source URL of the PDF to load initially.
+ * @param {string} baseUrl the base URL of the PDF viewer
  * @return {HTMLIFrameElement} the iframe element containing the PDF viewer.
  */
-function PDFCreateOutOfProcessPlugin(src) {
+function PDFCreateOutOfProcessPlugin(src, baseUrl) {
   var client = new PDFScriptingAPI(window, null);
   var iframe = assertInstanceof(
       window.document.createElement('iframe'), HTMLIFrameElement);
-  iframe.setAttribute('src', 'pdf_preview.html?' + src);
+  iframe.setAttribute('src', baseUrl + '/index.html?' + src);
   // Prevent the frame from being tab-focusable.
   iframe.setAttribute('tabindex', '-1');
 
diff --git a/chrome/browser/resources/print_preview/new/preview_area.html b/chrome/browser/resources/print_preview/new/preview_area.html
index bfdbe31ef..718f60a 100644
--- a/chrome/browser/resources/print_preview/new/preview_area.html
+++ b/chrome/browser/resources/print_preview/new/preview_area.html
@@ -149,7 +149,6 @@
     </print-preview-margin-control-container>
   </template>
   <script src="preview_area.js"></script>
-  <script
-    src="chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_scripting_api.js">
+  <script src="../pdf/pdf_scripting_api.js">
   </script>
 </dom-module>
diff --git a/chrome/browser/resources/print_preview/new/preview_area.js b/chrome/browser/resources/print_preview/new/preview_area.js
index cedc1db..5738b4f 100644
--- a/chrome/browser/resources/print_preview/new/preview_area.js
+++ b/chrome/browser/resources/print_preview/new/preview_area.js
@@ -445,7 +445,7 @@
     assert(!this.plugin_);
     const srcUrl = this.getPreviewUrl_(previewUid, index);
     this.plugin_ = /** @type {print_preview_new.PDFPlugin} */ (
-        PDFCreateOutOfProcessPlugin(srcUrl));
+        PDFCreateOutOfProcessPlugin(srcUrl, 'chrome://print/pdf'));
     this.plugin_.classList.add('preview-area-plugin');
     this.plugin_.setAttribute('aria-live', 'polite');
     this.plugin_.setAttribute('aria-atomic', 'true');
diff --git a/chrome/browser/resources/print_preview/pdf_preview.html b/chrome/browser/resources/print_preview/pdf_preview.html
deleted file mode 100644
index ba3ea35..0000000
--- a/chrome/browser/resources/print_preview/pdf_preview.html
+++ /dev/null
@@ -1 +0,0 @@
-<include src="../pdf/index.html">
\ No newline at end of file
diff --git a/chrome/browser/resources/print_preview/previewarea/preview_area.js b/chrome/browser/resources/print_preview/previewarea/preview_area.js
index a545d6259..bb24c19c 100644
--- a/chrome/browser/resources/print_preview/previewarea/preview_area.js
+++ b/chrome/browser/resources/print_preview/previewarea/preview_area.js
@@ -488,7 +488,7 @@
     createPlugin_: function(srcUrl) {
       assert(!this.plugin_);
       this.plugin_ = /** @type {print_preview.PDFPlugin} */ (
-          PDFCreateOutOfProcessPlugin(srcUrl));
+          PDFCreateOutOfProcessPlugin(srcUrl, 'chrome://print/pdf'));
       this.plugin_.setKeyEventCallback(this.keyEventCallback_);
 
       this.plugin_.setAttribute('class', 'preview-area-plugin');
diff --git a/chrome/browser/resources/print_preview/print_preview_resources.grd b/chrome/browser/resources/print_preview/print_preview_resources.grd
index 4730a65f..a4ebaca 100644
--- a/chrome/browser/resources/print_preview/print_preview_resources.grd
+++ b/chrome/browser/resources/print_preview/print_preview_resources.grd
@@ -149,9 +149,6 @@
       <structure name="IDR_PRINT_PREVIEW_NEW_PREVIEW_AREA_JS"
                  file="new/preview_area.js"
                  type="chrome_html" />
-      <structure name="IDR_PDF_PDF_SCRIPTING_API_JS"
-                 file="../pdf/pdf_scripting_api.js"
-                 type="chrome_html" />
       <structure name="IDR_PRINT_PREVIEW_NEW_HEADER_HTML"
                  file="new/header.html"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/settings/people_page/compiled_resources2.gyp b/chrome/browser/resources/settings/people_page/compiled_resources2.gyp
index 3d6b9bd..3c72741d 100644
--- a/chrome/browser/resources/settings/people_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/people_page/compiled_resources2.gyp
@@ -87,6 +87,7 @@
       'dependencies': [
         '../compiled_resources2.gyp:route',
         '../controls/compiled_resources2.gyp:settings_dropdown_menu',
+        '../controls/compiled_resources2.gyp:settings_toggle_button',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
diff --git a/chrome/browser/resources/settings/people_page/lock_screen.html b/chrome/browser/resources/settings/people_page/lock_screen.html
index df6f253..a68afd0 100644
--- a/chrome/browser/resources/settings/people_page/lock_screen.html
+++ b/chrome/browser/resources/settings/people_page/lock_screen.html
@@ -76,8 +76,9 @@
     </style>
 
     <div>
-      <settings-toggle-button id="screenLockDiv" class="first"
+      <settings-toggle-button id="enableLockScreen" class="first"
           pref="{{prefs.settings.enable_screen_lock}}"
+          on-settings-boolean-control-change="onScreenLockChange_" no-set-pref
           label="$i18n{enableScreenlock}">
       </settings-toggle-button>
 
@@ -198,7 +199,8 @@
 
       <template is="dom-if" if="[[showPasswordPromptDialog_]]" restamp>
         <settings-password-prompt-dialog id="passwordPrompt"
-            on-close="onPasswordPromptDialogClose_" set-modes="{{setModes_}}">
+            on-close="onPasswordPromptDialogClose_" set-modes="{{setModes_}}"
+            auth-token="{{authToken_}}">
         </settings-password-prompt-dialog>
       </template>
 
diff --git a/chrome/browser/resources/settings/people_page/lock_screen.js b/chrome/browser/resources/settings/people_page/lock_screen.js
index bbf51e7..4edb4a4 100644
--- a/chrome/browser/resources/settings/people_page/lock_screen.js
+++ b/chrome/browser/resources/settings/people_page/lock_screen.js
@@ -41,10 +41,8 @@
     prefs: {type: Object},
 
     /**
-     * setModes_ is a partially applied function that stores the previously
-     * entered password. It's defined only when the user has already entered a
-     * valid password.
-     *
+     * setModes_ is a partially applied function that stores the current auth
+     * token. It's defined only when the user has entered a valid password.
      * @type {Object|undefined}
      * @private
      */
@@ -54,6 +52,12 @@
     },
 
     /**
+     * Authentication token provided by password-prompt-dialog.
+     * @private
+     */
+    authToken_: String,
+
+    /**
      * writeUma_ is a function that handles writing uma stats. It may be
      * overridden for tests.
      *
@@ -231,6 +235,20 @@
   },
 
   /**
+   * @param {!Event} event
+   * @private
+   */
+  onScreenLockChange_: function(event) {
+    const target = /** @type {!SettingsToggleButtonElement} */ (event.target);
+    if (!this.authToken_) {
+      console.error('Screen lock changed with expired token.');
+      target.checked = !target.checked;
+      return;
+    }
+    this.setLockScreenEnabled(this.authToken_, target.checked);
+  },
+
+  /**
    * Called when the unlock type has changed.
    * @param {!string} selected The current unlock type.
    * @private
@@ -269,7 +287,7 @@
     else if (!this.$$('#unlockType').disabled)
       cr.ui.focusWithoutInk(assert(this.$$('#unlockType')));
     else
-      cr.ui.focusWithoutInk(assert(this.$$('#screenLockDiv')));
+      cr.ui.focusWithoutInk(assert(this.$$('#enableLockScreen')));
   },
 
   /**
diff --git a/chrome/browser/resources/settings/people_page/lock_state_behavior.js b/chrome/browser/resources/settings/people_page/lock_state_behavior.js
index 3f2f27c..0caa83e 100644
--- a/chrome/browser/resources/settings/people_page/lock_state_behavior.js
+++ b/chrome/browser/resources/settings/people_page/lock_state_behavior.js
@@ -71,4 +71,9 @@
       }
     });
   },
+
+  /** Sets the lock screen enabled state. */
+  setLockScreenEnabled(authToken, enabled) {
+    this.quickUnlockPrivate_.setLockScreenEnabled(authToken, enabled);
+  },
 };
diff --git a/chrome/browser/resources/settings/people_page/password_prompt_dialog.html b/chrome/browser/resources/settings/people_page/password_prompt_dialog.html
index af784ee..3a5e4b6 100644
--- a/chrome/browser/resources/settings/people_page/password_prompt_dialog.html
+++ b/chrome/browser/resources/settings/people_page/password_prompt_dialog.html
@@ -18,8 +18,7 @@
       }
     </style>
 
-    <dialog is="cr-dialog" id="dialog" on-close="onClose_"
-        close-text="$i18n{close}">
+    <dialog is="cr-dialog" id="dialog" close-text="$i18n{close}">
       <div slot="title">$i18n{passwordPromptTitle}</div>
       <div slot="body">
 
diff --git a/chrome/browser/resources/settings/people_page/password_prompt_dialog.js b/chrome/browser/resources/settings/people_page/password_prompt_dialog.js
index f0285c22..00a484c 100644
--- a/chrome/browser/resources/settings/people_page/password_prompt_dialog.js
+++ b/chrome/browser/resources/settings/people_page/password_prompt_dialog.js
@@ -45,10 +45,13 @@
 
     /**
      * Authhentication token used when calling setModes, returned by
-     * quickUnlockPrivate.getAuthToken.
+     * quickUnlockPrivate.getAuthToken. Reflected to lock-screen.
      * @private
      */
-    token_: String,
+    authToken: {
+      type: String,
+      notify: true,
+    },
 
     /**
      * Helper property which marks password as valid/invalid.
@@ -93,14 +96,6 @@
   },
 
   /**
-   * Called whenever the dialog is closed.
-   * @private
-   */
-  onClose_: function() {
-    this.token_ = '';
-  },
-
-  /**
    * Run the account password check.
    * @private
    */
@@ -127,6 +122,7 @@
         return;
       }
 
+      this.authToken = tokenInfo.token;
       this.passwordInvalid_ = false;
 
       // Create the |this.setModes| closure and automatically clear it after
diff --git a/chrome/browser/sessions/better_session_restore_browsertest.cc b/chrome/browser/sessions/better_session_restore_browsertest.cc
index 9f5efc80..2a306139 100644
--- a/chrome/browser/sessions/better_session_restore_browsertest.cc
+++ b/chrome/browser/sessions/better_session_restore_browsertest.cc
@@ -468,6 +468,7 @@
   // Set the startup preference to "continue where I left off" and visit a page
   // which stores a session cookie.
   StoreDataWithPage("session_cookies.html");
+  content::EnsureCookiesFlushed(browser()->profile());
 }
 
 IN_PROC_BROWSER_TEST_F(ContinueWhereILeftOffTest, SessionCookies) {
@@ -696,6 +697,7 @@
 
 IN_PROC_BROWSER_TEST_F(RestartTest, PRE_SessionCookies) {
   StoreDataWithPage("session_cookies.html");
+  content::EnsureCookiesFlushed(browser()->profile());
   Restart();
 }
 
@@ -725,6 +727,7 @@
 
 IN_PROC_BROWSER_TEST_F(RestartTest, PRE_CookiesClearedOnExit) {
   StoreDataWithPage("cookies.html");
+  content::EnsureCookiesFlushed(browser()->profile());
   CookieSettingsFactory::GetForProfile(browser()->profile())
       ->SetDefaultCookieSetting(CONTENT_SETTING_SESSION_ONLY);
   Restart();
@@ -826,6 +829,7 @@
 
 IN_PROC_BROWSER_TEST_F(NoSessionRestoreTest, PRE_PRE_CookiesClearedOnExit) {
   StoreDataWithPage("cookies.html");
+  content::EnsureCookiesFlushed(browser()->profile());
 }
 
 IN_PROC_BROWSER_TEST_F(NoSessionRestoreTest, PRE_CookiesClearedOnExit) {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index dd8b3813..452ba8c 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1756,6 +1756,15 @@
     assert(enable_extensions)
     assert(toolkit_views)
     sources += [
+      "app_list/app_list_controller_impl.cc",
+      "app_list/app_list_controller_impl.h",
+      "app_list/app_sync_ui_state.cc",
+      "app_list/app_sync_ui_state.h",
+      "app_list/app_sync_ui_state_factory.cc",
+      "app_list/app_sync_ui_state_factory.h",
+      "app_list/app_sync_ui_state_observer.h",
+      "app_list/app_sync_ui_state_watcher.cc",
+      "app_list/app_sync_ui_state_watcher.h",
       "app_list/search/launcher_search/launcher_search_icon_image_loader.cc",
       "app_list/search/launcher_search/launcher_search_icon_image_loader.h",
       "app_list/search/launcher_search/launcher_search_icon_image_loader_impl.cc",
@@ -1766,17 +1775,6 @@
       "app_list/search/launcher_search/launcher_search_result.h",
       "ash/accessibility/accessibility_controller_client.cc",
       "ash/accessibility/accessibility_controller_client.h",
-      "ash/app_list/app_list_controller_ash.cc",
-      "ash/app_list/app_list_controller_ash.h",
-      "ash/app_list/app_list_service_ash.cc",
-      "ash/app_list/app_list_service_ash.h",
-      "ash/app_list/app_sync_ui_state_watcher.cc",
-      "ash/app_list/app_sync_ui_state_watcher.h",
-      "ash/app_sync_ui_state.cc",
-      "ash/app_sync_ui_state.h",
-      "ash/app_sync_ui_state_factory.cc",
-      "ash/app_sync_ui_state_factory.h",
-      "ash/app_sync_ui_state_observer.h",
       "ash/ash_shell_init.cc",
       "ash/ash_shell_init.h",
       "ash/ash_util.cc",
@@ -1936,6 +1934,11 @@
       "views/select_file_dialog_extension_factory.h",
       "views/tabs/window_finder_ash.cc",
       "views/touch_uma/touch_uma_ash.cc",
+      "webui/chromeos/assistant_optin/assistant_optin_screen_exit_code.h",
+      "webui/chromeos/assistant_optin/assistant_optin_ui.cc",
+      "webui/chromeos/assistant_optin/assistant_optin_ui.h",
+      "webui/chromeos/assistant_optin/value_prop_screen_handler.cc",
+      "webui/chromeos/assistant_optin/value_prop_screen_handler.h",
       "webui/chromeos/bluetooth_dialog_localized_strings_provider.cc",
       "webui/chromeos/bluetooth_dialog_localized_strings_provider.h",
       "webui/chromeos/bluetooth_pairing_dialog.cc",
@@ -3436,7 +3439,6 @@
       "app_list/app_list_model_builder.h",
       "app_list/app_list_model_updater.h",
       "app_list/app_list_model_updater_delegate.h",
-      "app_list/app_list_service.cc",
       "app_list/app_list_service.h",
       "app_list/app_list_service_impl.cc",
       "app_list/app_list_service_impl.h",
@@ -3458,9 +3460,6 @@
       "app_list/extension_app_model_builder.h",
       "app_list/extension_uninstaller.cc",
       "app_list/extension_uninstaller.h",
-      "app_list/profile_loader.cc",
-      "app_list/profile_loader.h",
-      "app_list/profile_store.h",
       "app_list/search/answer_card/answer_card_contents.cc",
       "app_list/search/answer_card/answer_card_contents.h",
       "app_list/search/answer_card/answer_card_result.cc",
diff --git a/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.cc b/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.cc
index 5e971b2..0a425558 100644
--- a/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.cc
+++ b/chrome/browser/ui/android/content_settings/ads_blocked_infobar_delegate.cc
@@ -50,7 +50,7 @@
 }
 
 int AdsBlockedInfobarDelegate::GetIconId() const {
-  return IDR_ANDROID_INFOBAR_ADS_BLOCKED;
+  return IDR_ANDROID_INFOBAR_BLOCKED_POPUPS;
 }
 
 base::string16 AdsBlockedInfobarDelegate::GetMessageText() const {
diff --git a/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc b/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc
index 2c97bc5..b7f1e16 100644
--- a/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc
+++ b/chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.cc
@@ -6,13 +6,14 @@
 
 #include <stddef.h>
 #include <utility>
+
+#include "chrome/browser/android/android_theme_resources.h"
 #include "chrome/browser/content_settings/chrome_content_settings_utils.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/grit/theme_resources.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
@@ -20,7 +21,6 @@
 #include "components/prefs/pref_service.h"
 #include "ui/base/l10n/l10n_util.h"
 
-
 // static
 void PopupBlockedInfoBarDelegate::Create(content::WebContents* web_contents,
                                          int num_popups) {
@@ -63,7 +63,7 @@
 }
 
 int PopupBlockedInfoBarDelegate::GetIconId() const {
-  return IDR_BLOCKED_POPUPS;
+  return IDR_ANDROID_INFOBAR_BLOCKED_POPUPS;
 }
 
 PopupBlockedInfoBarDelegate*
diff --git a/chrome/browser/ui/ash/app_list/app_list_browsertest.cc b/chrome/browser/ui/app_list/app_list_browsertest.cc
similarity index 79%
rename from chrome/browser/ui/ash/app_list/app_list_browsertest.cc
rename to chrome/browser/ui/app_list/app_list_browsertest.cc
index 6a05b90c..52eb49a 100644
--- a/chrome/browser/ui/ash/app_list/app_list_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_browsertest.cc
@@ -5,8 +5,7 @@
 #include "chrome/browser/apps/app_browsertest_util.h"
 #include "chrome/browser/chromeos/ash_config.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
-#include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h"
-#include "chrome/browser/ui/ash/app_list/app_list_service_ash.h"
+#include "chrome/browser/ui/app_list/app_list_controller_impl.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -20,14 +19,14 @@
 #include "ui/wm/core/window_util.h"
 
 using AppListTest = InProcessBrowserTest;
-using AppListControllerDelegateAshTest = extensions::PlatformAppBrowserTest;
+using AppListControllerDelegateImplTest = extensions::PlatformAppBrowserTest;
 
 // TODO(crbug.com/759779, crbug.com/819386): Add back
 // |ClickingContextMenuDoesNotDismiss|.
 
-// Test AppListControllerDelegateAsh::IsAppOpen for extension apps.
-IN_PROC_BROWSER_TEST_F(AppListControllerDelegateAshTest, IsExtensionAppOpen) {
-  AppListControllerDelegateAsh delegate;
+// Test AppListControllerDelegateImpl::IsAppOpen for extension apps.
+IN_PROC_BROWSER_TEST_F(AppListControllerDelegateImplTest, IsExtensionAppOpen) {
+  AppListControllerDelegateImpl delegate;
   EXPECT_FALSE(delegate.IsAppOpen("fake_extension_app_id"));
 
   base::FilePath extension_path = test_data_dir_.AppendASCII("app");
@@ -46,9 +45,9 @@
   EXPECT_TRUE(delegate.IsAppOpen(extension_app->id()));
 }
 
-// Test AppListControllerDelegateAsh::IsAppOpen for platform apps.
-IN_PROC_BROWSER_TEST_F(AppListControllerDelegateAshTest, IsPlatformAppOpen) {
-  AppListControllerDelegateAsh delegate;
+// Test AppListControllerDelegateImpl::IsAppOpen for platform apps.
+IN_PROC_BROWSER_TEST_F(AppListControllerDelegateImplTest, IsPlatformAppOpen) {
+  AppListControllerDelegateImpl delegate;
   EXPECT_FALSE(delegate.IsAppOpen("fake_platform_app_id"));
 
   const extensions::Extension* app = InstallPlatformApp("minimal");
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc
index 5692c948..a72656ba 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -12,9 +12,9 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
+#include "chrome/browser/ui/app_list/app_list_service_impl.h"
 #include "chrome/browser/ui/app_list/app_list_view_delegate.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
-#include "chrome/browser/ui/ash/app_list/app_list_service_ash.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -28,7 +28,7 @@
   ash::mojom::AppListClientPtr client;
   binding_.Bind(mojo::MakeRequest(&client));
   app_list_controller_->SetClient(std::move(client));
-  AppListServiceAsh::GetInstance()->SetAppListControllerAndClient(
+  AppListServiceImpl::GetInstance()->SetAppListControllerAndClient(
       app_list_controller_.get(), this);
 }
 
@@ -81,11 +81,11 @@
 }
 
 void AppListClientImpl::OnAppListTargetVisibilityChanged(bool visible) {
-  AppListServiceAsh::GetInstance()->set_app_list_target_visible(visible);
+  AppListServiceImpl::GetInstance()->set_app_list_target_visible(visible);
 }
 
 void AppListClientImpl::OnAppListVisibilityChanged(bool visible) {
-  AppListServiceAsh::GetInstance()->set_app_list_visible(visible);
+  AppListServiceImpl::GetInstance()->set_app_list_visible(visible);
 }
 
 void AppListClientImpl::StartVoiceInteractionSession() {
@@ -121,7 +121,7 @@
 }
 
 AppListViewDelegate* AppListClientImpl::GetViewDelegate() {
-  return AppListServiceAsh::GetInstance()->GetViewDelegate();
+  return AppListServiceImpl::GetInstance()->GetViewDelegate();
 }
 
 void AppListClientImpl::FlushMojoForTesting() {
diff --git a/chrome/browser/ui/app_list/app_list_controller_browsertest.cc b/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
index e73f317a..3b59960 100644
--- a/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
@@ -73,7 +73,7 @@
   const std::string title = extension->name();
 
   // Show the app list first, otherwise we won't have a search box to update.
-  service->ShowForProfile(browser()->profile());
+  service->Show();
   service->FlushForTesting();
 
   // Currently the search box is empty, so we have no result.
@@ -125,6 +125,6 @@
   AppListService* service = AppListService::Get();
   EXPECT_TRUE(service->GetCurrentAppListProfile());
 
-  service->ShowForProfile(browser()->profile());
+  service->Show();
   EXPECT_EQ(browser()->profile(), service->GetCurrentAppListProfile());
 }
diff --git a/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc b/chrome/browser/ui/app_list/app_list_controller_impl.cc
similarity index 65%
rename from chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
rename to chrome/browser/ui/app_list/app_list_controller_impl.cc
index a93b7e60..a45c479 100644
--- a/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
+++ b/chrome/browser/ui/app_list/app_list_controller_impl.cc
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h"
+#include "chrome/browser/ui/app_list/app_list_controller_impl.h"
 
 #include <utility>
 
 #include "ash/public/interfaces/app_list.mojom.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/app_list/app_list_service_ash.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -19,30 +18,30 @@
 #include "ui/display/screen.h"
 #include "ui/display/types/display_constants.h"
 
-AppListControllerDelegateAsh::AppListControllerDelegateAsh() {}
+AppListControllerDelegateImpl::AppListControllerDelegateImpl() {}
 
-AppListControllerDelegateAsh::~AppListControllerDelegateAsh() {}
+AppListControllerDelegateImpl::~AppListControllerDelegateImpl() {}
 
-void AppListControllerDelegateAsh::SetAppListController(
+void AppListControllerDelegateImpl::SetAppListController(
     ash::mojom::AppListController* app_list_controller) {
   app_list_controller_ = app_list_controller;
 }
 
-void AppListControllerDelegateAsh::DismissView() {
+void AppListControllerDelegateImpl::DismissView() {
   if (!app_list_controller_)
     return;
   app_list_controller_->DismissAppList();
 }
 
-int64_t AppListControllerDelegateAsh::GetAppListDisplayId() {
+int64_t AppListControllerDelegateImpl::GetAppListDisplayId() {
   return display_id_;
 }
 
-void AppListControllerDelegateAsh::SetAppListDisplayId(int64_t display_id) {
+void AppListControllerDelegateImpl::SetAppListDisplayId(int64_t display_id) {
   display_id_ = display_id;
 }
 
-void AppListControllerDelegateAsh::GetAppInfoDialogBounds(
+void AppListControllerDelegateImpl::GetAppInfoDialogBounds(
     GetAppInfoDialogBoundsCallback callback) {
   if (!app_list_controller_) {
     LOG(ERROR) << "app_list_controller_ is null";
@@ -52,52 +51,50 @@
   app_list_controller_->GetAppInfoDialogBounds(std::move(callback));
 }
 
-bool AppListControllerDelegateAsh::IsAppPinned(const std::string& app_id) {
+bool AppListControllerDelegateImpl::IsAppPinned(const std::string& app_id) {
   return ChromeLauncherController::instance()->IsAppPinned(app_id);
 }
 
-bool AppListControllerDelegateAsh::IsAppOpen(const std::string& app_id) const {
+bool AppListControllerDelegateImpl::IsAppOpen(const std::string& app_id) const {
   return ChromeLauncherController::instance()->IsOpen(ash::ShelfID(app_id));
 }
 
-void AppListControllerDelegateAsh::PinApp(const std::string& app_id) {
+void AppListControllerDelegateImpl::PinApp(const std::string& app_id) {
   ChromeLauncherController::instance()->PinAppWithID(app_id);
 }
 
-void AppListControllerDelegateAsh::UnpinApp(const std::string& app_id) {
+void AppListControllerDelegateImpl::UnpinApp(const std::string& app_id) {
   ChromeLauncherController::instance()->UnpinAppWithID(app_id);
 }
 
-AppListControllerDelegate::Pinnable AppListControllerDelegateAsh::GetPinnable(
+AppListControllerDelegate::Pinnable AppListControllerDelegateImpl::GetPinnable(
     const std::string& app_id) {
   return GetPinnableForAppID(app_id,
                              ChromeLauncherController::instance()->profile());
 }
 
-void AppListControllerDelegateAsh::OnShowChildDialog() {
-}
+void AppListControllerDelegateImpl::OnShowChildDialog() {}
 
-void AppListControllerDelegateAsh::OnCloseChildDialog() {
-}
+void AppListControllerDelegateImpl::OnCloseChildDialog() {}
 
-void AppListControllerDelegateAsh::CreateNewWindow(Profile* profile,
-                                                   bool incognito) {
+void AppListControllerDelegateImpl::CreateNewWindow(Profile* profile,
+                                                    bool incognito) {
   if (incognito)
     chrome::NewEmptyWindow(profile->GetOffTheRecordProfile());
   else
     chrome::NewEmptyWindow(profile);
 }
 
-void AppListControllerDelegateAsh::OpenURL(Profile* profile,
-                                           const GURL& url,
-                                           ui::PageTransition transition,
-                                           WindowOpenDisposition disposition) {
+void AppListControllerDelegateImpl::OpenURL(Profile* profile,
+                                            const GURL& url,
+                                            ui::PageTransition transition,
+                                            WindowOpenDisposition disposition) {
   NavigateParams params(profile, url, transition);
   params.disposition = disposition;
   Navigate(&params);
 }
 
-void AppListControllerDelegateAsh::ActivateApp(
+void AppListControllerDelegateImpl::ActivateApp(
     Profile* profile,
     const extensions::Extension* extension,
     AppListSource source,
@@ -116,7 +113,7 @@
   DismissView();
 }
 
-void AppListControllerDelegateAsh::LaunchApp(
+void AppListControllerDelegateImpl::LaunchApp(
     Profile* profile,
     const extensions::Extension* extension,
     AppListSource source,
@@ -129,7 +126,7 @@
 }
 
 ash::ShelfLaunchSource
-AppListControllerDelegateAsh::AppListSourceToLaunchSource(
+AppListControllerDelegateImpl::AppListSourceToLaunchSource(
     AppListSource source) {
   switch (source) {
     case LAUNCH_FROM_APP_LIST:
diff --git a/chrome/browser/ui/ash/app_list/app_list_controller_ash.h b/chrome/browser/ui/app_list/app_list_controller_impl.h
similarity index 83%
rename from chrome/browser/ui/ash/app_list/app_list_controller_ash.h
rename to chrome/browser/ui/app_list/app_list_controller_impl.h
index bd40d54..3238b47 100644
--- a/chrome/browser/ui/ash/app_list/app_list_controller_ash.h
+++ b/chrome/browser/ui/app_list/app_list_controller_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_APP_LIST_APP_LIST_CONTROLLER_ASH_H_
-#define CHROME_BROWSER_UI_ASH_APP_LIST_APP_LIST_CONTROLLER_ASH_H_
+#ifndef CHROME_BROWSER_UI_APP_LIST_APP_LIST_CONTROLLER_IMPL_H_
+#define CHROME_BROWSER_UI_APP_LIST_APP_LIST_CONTROLLER_IMPL_H_
 
 #include <string>
 
@@ -20,10 +20,10 @@
 }  // namespace mojom
 }  // namespace ash
 
-class AppListControllerDelegateAsh : public AppListControllerDelegate {
+class AppListControllerDelegateImpl : public AppListControllerDelegate {
  public:
-  AppListControllerDelegateAsh();
-  ~AppListControllerDelegateAsh() override;
+  AppListControllerDelegateImpl();
+  ~AppListControllerDelegateImpl() override;
 
   // AppListControllerDelegate overrides:
   void DismissView() override;
@@ -64,7 +64,7 @@
   // Not owned.
   ash::mojom::AppListController* app_list_controller_ = nullptr;
 
-  DISALLOW_COPY_AND_ASSIGN(AppListControllerDelegateAsh);
+  DISALLOW_COPY_AND_ASSIGN(AppListControllerDelegateImpl);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_APP_LIST_APP_LIST_CONTROLLER_ASH_H_
+#endif  // CHROME_BROWSER_UI_APP_LIST_APP_LIST_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/ui/ash/app_list/app_list_interactive_uitest.cc b/chrome/browser/ui/app_list/app_list_interactive_uitest.cc
similarity index 100%
rename from chrome/browser/ui/ash/app_list/app_list_interactive_uitest.cc
rename to chrome/browser/ui/app_list/app_list_interactive_uitest.cc
diff --git a/chrome/browser/ui/app_list/app_list_service.cc b/chrome/browser/ui/app_list/app_list_service.cc
deleted file mode 100644
index 85568513..0000000
--- a/chrome/browser/ui/app_list/app_list_service.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2013 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/app_list/app_list_service.h"
-
-#include <stdint.h>
-
-#include "base/command_line.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/process/process_info.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_registry_simple.h"
-
-namespace {
-
-enum StartupType {
-  COLD_START,
-  WARM_START,
-  WARM_START_FAST,
-};
-
-// For when an app list show request is received via CommandLine. Indicates
-// whether the Profile the app list was previously showing was the SAME, OTHER
-// or NONE with respect to the new Profile to show.
-enum ProfileLoadState {
-  PROFILE_LOADED_SAME,
-  PROFILE_LOADED_OTHER,
-  PROFILE_LOADED_NONE,
-};
-
-base::Time GetOriginalProcessStartTime(const base::CommandLine& command_line) {
-  if (command_line.HasSwitch(switches::kOriginalProcessStartTime)) {
-    std::string start_time_string =
-        command_line.GetSwitchValueASCII(switches::kOriginalProcessStartTime);
-    int64_t remote_start_time;
-    base::StringToInt64(start_time_string, &remote_start_time);
-    return base::Time::FromInternalValue(remote_start_time);
-  }
-
-  return base::CurrentProcessInfo::CreationTime();
-}
-
-StartupType GetStartupType(const base::CommandLine& command_line) {
-  // The presence of kOriginalProcessStartTime implies that another process
-  // has sent us its command line to handle, ie: we are already running.
-  if (command_line.HasSwitch(switches::kOriginalProcessStartTime)) {
-    return command_line.HasSwitch(switches::kFastStart) ?
-        WARM_START_FAST : WARM_START;
-  }
-  return COLD_START;
-}
-
-// The time the process that caused the app list to be shown started. This isn't
-// necessarily the currently executing process as we may be processing a command
-// line given to a short-lived Chrome instance.
-int64_t g_original_process_start_time;
-
-// The type of startup the the current app list show has gone through.
-StartupType g_app_show_startup_type;
-
-// The state of the active app list profile at the most recent launch.
-ProfileLoadState g_profile_load_state;
-
-void RecordFirstPaintTiming() {
-  base::Time start_time(
-      base::Time::FromInternalValue(g_original_process_start_time));
-  base::TimeDelta elapsed = base::Time::Now() - start_time;
-  switch (g_app_show_startup_type) {
-    case COLD_START:
-      DCHECK_EQ(PROFILE_LOADED_NONE, g_profile_load_state);
-      UMA_HISTOGRAM_LONG_TIMES("Startup.AppListFirstPaintColdStart", elapsed);
-      break;
-    case WARM_START:
-      // For warm starts, only record showing the same profile. "NONE" should
-      // only occur in the first 30 seconds after startup. "OTHER" only occurs
-      // for multi-profile cases. In these cases, timings are also affected by
-      // whether or not a profile has been loaded from disk, which makes the
-      // profile load asynchronous and skews results unpredictably.
-      if (g_profile_load_state == PROFILE_LOADED_SAME)
-        UMA_HISTOGRAM_LONG_TIMES("Startup.AppListFirstPaintWarmStart", elapsed);
-      break;
-    case WARM_START_FAST:
-      if (g_profile_load_state == PROFILE_LOADED_SAME) {
-        UMA_HISTOGRAM_LONG_TIMES("Startup.AppListFirstPaintWarmStartFast",
-                                 elapsed);
-      }
-      break;
-  }
-}
-
-void RecordStartupInfo(AppListService* service,
-                       const base::CommandLine& command_line,
-                       Profile* launch_profile) {
-  base::Time start_time = GetOriginalProcessStartTime(command_line);
-  if (start_time.is_null())
-    return;
-
-  base::TimeDelta elapsed = base::Time::Now() - start_time;
-  StartupType startup_type = GetStartupType(command_line);
-  switch (startup_type) {
-    case COLD_START:
-      UMA_HISTOGRAM_LONG_TIMES("Startup.ShowAppListColdStart", elapsed);
-      break;
-    case WARM_START:
-      UMA_HISTOGRAM_LONG_TIMES("Startup.ShowAppListWarmStart", elapsed);
-      break;
-    case WARM_START_FAST:
-      UMA_HISTOGRAM_LONG_TIMES("Startup.ShowAppListWarmStartFast", elapsed);
-      break;
-  }
-
-  g_original_process_start_time = start_time.ToInternalValue();
-  g_app_show_startup_type = startup_type;
-
-  Profile* current_profile = service->GetCurrentAppListProfile();
-  if (!current_profile)
-    g_profile_load_state = PROFILE_LOADED_NONE;
-  else if (current_profile == launch_profile)
-    g_profile_load_state = PROFILE_LOADED_SAME;
-  else
-    g_profile_load_state = PROFILE_LOADED_OTHER;
-
-  service->SetAppListNextPaintCallback(RecordFirstPaintTiming);
-}
-
-}  // namespace
-
-// static
-void AppListService::RegisterPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterInt64Pref(prefs::kLastAppListLaunchPing, 0);
-  registry->RegisterIntegerPref(prefs::kAppListLaunchCount, 0);
-  registry->RegisterInt64Pref(prefs::kLastAppListAppLaunchPing, 0);
-  registry->RegisterIntegerPref(prefs::kAppListAppLaunchCount, 0);
-  registry->RegisterStringPref(prefs::kAppListProfile, std::string());
-  registry->RegisterBooleanPref(prefs::kAppLauncherHasBeenEnabled, false);
-  registry->RegisterIntegerPref(prefs::kAppListEnableMethod,
-                                ENABLE_NOT_RECORDED);
-  registry->RegisterInt64Pref(prefs::kAppListEnableTime, 0);
-}
-
-// static
-bool AppListService::HandleLaunchCommandLine(
-    const base::CommandLine& command_line,
-    Profile* launch_profile) {
-  if (!command_line.HasSwitch(switches::kShowAppList))
-    return false;
-
-  AppListService* service = Get();
-  DCHECK(service);
-  RecordStartupInfo(service, command_line, launch_profile);
-  service->ShowForProfile(launch_profile);
-  return true;
-}
diff --git a/chrome/browser/ui/app_list/app_list_service.h b/chrome/browser/ui/app_list/app_list_service.h
index ff28076..be417455 100644
--- a/chrome/browser/ui/app_list/app_list_service.h
+++ b/chrome/browser/ui/app_list/app_list_service.h
@@ -7,79 +7,19 @@
 
 #include <string>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "ui/gfx/native_widget_types.h"
-
-class AppListControllerDelegate;
-class PrefRegistrySimple;
-class Profile;
-
-namespace base {
-class CommandLine;
-class FilePath;
-}
-
-namespace content {
-struct SpeechRecognitionSessionPreamble;
-}
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 
 class AppListService {
  public:
-  // Source that triggers the app launcher being enabled. This is used for UMA
-  // to track discoverability of the app lancher shortcut after install. Also
-  // used to provide custom install behavior (e.g. "always" enable).
-  enum AppListEnableSource {
-    ENABLE_NOT_RECORDED,       // Indicates app launcher not recently enabled.
-    ENABLE_FOR_APP_INSTALL,    // Triggered by a webstore packaged app install.
-    ENABLE_VIA_WEBSTORE_LINK,  // Triggered by webstore explicitly via API.
-    ENABLE_VIA_COMMAND_LINE,   // Triggered by --enable-app-list.
-    ENABLE_ON_REINSTALL_UNUSED,  // Triggered by Chrome reinstall finding pref.
-                                 // Unused since detecting a reinstall and
-                                 // detecting a pref are mutually exclusive.
-    ENABLE_SHOWN_UNDISCOVERED,  // This overrides a prior ENABLE_FOR_APP_INSTALL
-                                // when the launcher is auto-shown without
-                                // being "discovered" beforehand.
-    ENABLE_NUM_ENABLE_SOURCES
-  };
-
   // Get the AppListService.
   static AppListService* Get();
 
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
-  // Initializes the AppListService, and returns true if |command_line| is for
-  // showing the app list.
-  static bool HandleLaunchCommandLine(const base::CommandLine& command_line,
-                                      Profile* launch_profile);
-
-  // Indicates that |callback| should be called next time the app list is
-  // painted.
-  virtual void SetAppListNextPaintCallback(void (*callback)()) = 0;
-
-  virtual base::FilePath GetProfilePath(
-      const base::FilePath& user_data_dir) = 0;
-  virtual void SetProfilePath(const base::FilePath& profile_path) = 0;
-
   // Show the app list for the profile configured in the user data dir for the
   // current browser process.
   virtual void Show() = 0;
 
-  // Show the app list for the given profile. If it differs from the profile the
-  // app list is currently showing, repopulate the app list and save the new
-  // profile to local prefs as the default app list profile.
-  virtual void ShowForProfile(Profile* requested_profile) = 0;
-
-  // Shows the app list, and reveals the page that contains |extension_id|. This
-  // should only be called for things that show in the app list, and only when
-  // they begin or complete installing. If |start_discovery_tracking| is set,
-  // the app launcher will not actually be shown, but will start tracking UMA
-  // for app launcher discovery.
-  virtual void ShowForAppInstall(Profile* profile,
-                                 const std::string& extension_id,
-                                 bool start_discovery_tracking) = 0;
-
   // Dismiss the app list.
   virtual void DismissAppList() = 0;
 
@@ -92,17 +32,9 @@
   // Returns true if the app list is visible.
   virtual bool IsAppListVisible() const = 0;
 
-  // Enable the app list. What this does specifically will depend on the host
-  // operating system and shell.
-  virtual void EnableAppList(Profile* initial_profile,
-                             AppListEnableSource enable_source) = 0;
-
   // Returns a pointer to the platform specific AppListControllerDelegate.
   virtual AppListControllerDelegate* GetControllerDelegate() = 0;
 
-  // Create a platform-specific shortcut for the app list.
-  virtual void CreateShortcut() = 0;
-
   // Flush pending mojo calls to Ash AppListControllerImpl.
   virtual void FlushForTesting() = 0;
 
@@ -110,9 +42,6 @@
   AppListService() {}
   virtual ~AppListService() {}
 
-  // Do any once off initialization needed for the app list.
-  virtual void Init(Profile* initial_profile) = 0;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(AppListService);
 };
diff --git a/chrome/browser/ui/app_list/app_list_service_disabled_mac.h b/chrome/browser/ui/app_list/app_list_service_disabled_mac.h
deleted file mode 100644
index 9a3fdea..0000000
--- a/chrome/browser/ui/app_list/app_list_service_disabled_mac.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_APP_LIST_APP_LIST_SERVICE_DISABLED_MAC_H_
-#define CHROME_BROWSER_UI_APP_LIST_APP_LIST_SERVICE_DISABLED_MAC_H_
-
-class Profile;
-
-// Register the handler to accept connections from the old app_list shim, and
-// perform |action|; e.g. to open chrome://apps rather than the deleted App
-// Launcher UI.
-void InitAppsPageLegacyShimHandler(void (*action)(Profile*));
-
-#endif  // CHROME_BROWSER_UI_APP_LIST_APP_LIST_SERVICE_DISABLED_MAC_H_
diff --git a/chrome/browser/ui/app_list/app_list_service_impl.cc b/chrome/browser/ui/app_list/app_list_service_impl.cc
index b19ea5a..8ecc0f0 100644
--- a/chrome/browser/ui/app_list/app_list_service_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_service_impl.cc
@@ -9,277 +9,73 @@
 #include <string>
 #include <utility>
 
+#include "ash/app_list/app_list_controller_impl.h"
+#include "ash/app_list/model/search/search_model.h"
+#include "ash/shell.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/files/file_path.h"
 #include "base/location.h"
+#include "base/memory/singleton.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string16.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_shutdown.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_attributes_entry.h"
-#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/app_list/app_list_client_impl.h"
 #include "chrome/browser/ui/app_list/app_list_view_delegate.h"
-#include "chrome/browser/ui/app_list/profile_loader.h"
-#include "chrome/browser/ui/app_list/profile_store.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/session_util.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_switches.h"
-
-namespace {
-
-const int kDiscoverabilityTimeoutMinutes = 60;
-
-void SendAppListAppLaunch(int count) {
-  UMA_HISTOGRAM_CUSTOM_COUNTS(
-      "Apps.AppListDailyAppLaunches", count, 1, 1000, 50);
-  if (count > 0)
-    UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppToday", 1, 2);
-}
-
-void SendAppListLaunch(int count) {
-  UMA_HISTOGRAM_CUSTOM_COUNTS(
-      "Apps.AppListDailyLaunches", count, 1, 1000, 50);
-  if (count > 0)
-    UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppListToday", 1, 2);
-}
-
-bool SendDailyEventFrequency(
-    const char* last_ping_pref,
-    const char* count_pref,
-    void (*send_callback)(int count)) {
-  PrefService* local_state = g_browser_process->local_state();
-
-  base::Time now = base::Time::Now();
-  base::Time last = base::Time::FromInternalValue(local_state->GetInt64(
-      last_ping_pref));
-  int days = (now - last).InDays();
-  if (days > 0) {
-    send_callback(local_state->GetInteger(count_pref));
-    local_state->SetInt64(
-        last_ping_pref,
-        (last + base::TimeDelta::FromDays(days)).ToInternalValue());
-    local_state->SetInteger(count_pref, 0);
-    return true;
-  }
-  return false;
-}
-
-void RecordDailyEventFrequency(
-    const char* last_ping_pref,
-    const char* count_pref,
-    void (*send_callback)(int count)) {
-  if (!g_browser_process)
-    return;  // In a unit test.
-
-  PrefService* local_state = g_browser_process->local_state();
-  if (!local_state)
-    return;  // In a unit test.
-
-  int count = local_state->GetInteger(count_pref);
-  local_state->SetInteger(count_pref, count + 1);
-  if (SendDailyEventFrequency(last_ping_pref, count_pref, send_callback)) {
-    local_state->SetInteger(count_pref, 1);
-  }
-}
-
-class ProfileStoreImpl : public ProfileStore {
- public:
-  explicit ProfileStoreImpl(ProfileManager* profile_manager)
-      : profile_manager_(profile_manager),
-        weak_factory_(this) {
-  }
-
-  void AddProfileObserver(ProfileAttributesStorage::Observer* observer)
-      override {
-    profile_manager_->GetProfileAttributesStorage().AddObserver(observer);
-  }
-
-  void LoadProfileAsync(const base::FilePath& path,
-                        base::Callback<void(Profile*)> callback) override {
-    profile_manager_->CreateProfileAsync(
-        path,
-        base::Bind(&ProfileStoreImpl::OnProfileCreated,
-                   weak_factory_.GetWeakPtr(),
-                   callback),
-        base::string16(),
-        std::string(),
-        std::string());
-  }
-
-  void OnProfileCreated(base::Callback<void(Profile*)> callback,
-                        Profile* profile,
-                        Profile::CreateStatus status) {
-    switch (status) {
-      case Profile::CREATE_STATUS_CREATED:
-        break;
-      case Profile::CREATE_STATUS_INITIALIZED:
-        callback.Run(profile);
-        break;
-      case Profile::CREATE_STATUS_LOCAL_FAIL:
-      case Profile::CREATE_STATUS_REMOTE_FAIL:
-      case Profile::CREATE_STATUS_CANCELED:
-        break;
-      case Profile::MAX_CREATE_STATUS:
-        NOTREACHED();
-        break;
-    }
-  }
-
-  Profile* GetProfileByPath(const base::FilePath& path) override {
-    DCHECK(!IsProfileLocked(path));
-    return profile_manager_->GetProfileByPath(path);
-  }
-
-  base::FilePath GetUserDataDir() override {
-    return profile_manager_->user_data_dir();
-  }
-
-  std::string GetLastUsedProfileName() override {
-    return profile_manager_->GetLastUsedProfileName();
-  }
-
-  bool IsProfileSupervised(const base::FilePath& profile_path) override {
-    ProfileAttributesEntry* entry = nullptr;
-    bool has_entry = g_browser_process->profile_manager()->
-        GetProfileAttributesStorage().
-        GetProfileAttributesWithPath(profile_path, &entry);
-    return has_entry && entry->IsSupervised();
-  }
-
-  bool IsProfileLocked(const base::FilePath& profile_path) override {
-    ProfileAttributesEntry* entry = nullptr;
-    bool has_entry = g_browser_process->profile_manager()->
-        GetProfileAttributesStorage().
-        GetProfileAttributesWithPath(profile_path, &entry);
-    return has_entry && entry->IsSigninRequired();
-  }
-
- private:
-  ProfileManager* profile_manager_;
-  base::WeakPtrFactory<ProfileStoreImpl> weak_factory_;
-};
-
-void RecordAppListDiscoverability(PrefService* local_state,
-                                  bool is_startup_check) {
-  // Since this task may be delayed, ensure it does not interfere with shutdown
-  // when they unluckily coincide.
-  if (browser_shutdown::IsTryingToQuit())
-    return;
-
-  int64_t enable_time_value = local_state->GetInt64(prefs::kAppListEnableTime);
-  if (enable_time_value == 0)
-    return;  // Already recorded or never enabled.
-
-  base::Time app_list_enable_time =
-      base::Time::FromInternalValue(enable_time_value);
-  if (is_startup_check) {
-    // When checking at startup, only clear and record the "timeout" case,
-    // otherwise wait for a timeout.
-    base::TimeDelta time_remaining =
-        app_list_enable_time +
-        base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes) -
-        base::Time::Now();
-    if (time_remaining > base::TimeDelta()) {
-      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-          FROM_HERE, base::Bind(&RecordAppListDiscoverability,
-                                base::Unretained(local_state), false),
-          time_remaining);
-      return;
-    }
-  }
-
-  local_state->SetInt64(prefs::kAppListEnableTime, 0);
-
-  AppListService::AppListEnableSource enable_source =
-      static_cast<AppListService::AppListEnableSource>(
-          local_state->GetInteger(prefs::kAppListEnableMethod));
-  if (enable_source == AppListService::ENABLE_FOR_APP_INSTALL) {
-    base::TimeDelta time_taken = base::Time::Now() - app_list_enable_time;
-    // This means the user "discovered" the app launcher naturally, after it was
-    // enabled on the first app install. Record how long it took to discover.
-    // Note that the last bucket is essentially "not discovered": subtract 1
-    // minute to account for clock inaccuracy.
-    UMA_HISTOGRAM_CUSTOM_TIMES(
-        "Apps.AppListTimeToDiscover",
-        time_taken,
-        base::TimeDelta::FromSeconds(1),
-        base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes - 1),
-        10 /* bucket_count */);
-  }
-  UMA_HISTOGRAM_ENUMERATION("Apps.AppListHowEnabled",
-                            enable_source,
-                            AppListService::ENABLE_NUM_ENABLE_SOURCES);
-}
-
-// Checks whether a profile name is valid for the app list. Returns false if the
-// name is empty, or represents a guest profile.
-bool IsValidProfileName(const std::string& profile_name) {
-  if (profile_name.empty())
-    return false;
-
-  return
-      profile_name != base::FilePath(chrome::kGuestProfileDir).AsUTF8Unsafe() &&
-      profile_name != base::FilePath(chrome::kSystemProfileDir).AsUTF8Unsafe();
-}
-
-}  // namespace
-
-void AppListServiceImpl::RecordAppListLaunch() {
-  RecordDailyEventFrequency(prefs::kLastAppListLaunchPing,
-                            prefs::kAppListLaunchCount,
-                            &SendAppListLaunch);
-  RecordAppListDiscoverability(local_state_, false);
-}
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 
 // static
-void AppListServiceImpl::RecordAppListAppLaunch() {
-  RecordDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
-                            prefs::kAppListAppLaunchCount,
-                            &SendAppListAppLaunch);
-}
-
-// static
-void AppListServiceImpl::SendAppListStats() {
-  if (!g_browser_process || g_browser_process->IsShuttingDown())
-    return;
-
-  SendDailyEventFrequency(prefs::kLastAppListLaunchPing,
-                          prefs::kAppListLaunchCount,
-                          &SendAppListLaunch);
-  SendDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
-                          prefs::kAppListAppLaunchCount,
-                          &SendAppListAppLaunch);
+AppListServiceImpl* AppListServiceImpl::GetInstance() {
+  return base::Singleton<AppListServiceImpl,
+                         base::LeakySingletonTraits<AppListServiceImpl>>::get();
 }
 
 AppListServiceImpl::AppListServiceImpl()
-    : profile_store_(
-          new ProfileStoreImpl(g_browser_process->profile_manager())),
-      command_line_(*base::CommandLine::ForCurrentProcess()),
+    : command_line_(*base::CommandLine::ForCurrentProcess()),
       local_state_(g_browser_process->local_state()),
-      profile_loader_(new ProfileLoader(profile_store_.get())),
-      weak_factory_(this) {
-  profile_store_->AddProfileObserver(this);
-}
+      weak_factory_(this) {}
 
-AppListServiceImpl::AppListServiceImpl(
-    const base::CommandLine& command_line,
-    PrefService* local_state,
-    std::unique_ptr<ProfileStore> profile_store)
-    : profile_store_(std::move(profile_store)),
-      command_line_(command_line),
+AppListServiceImpl::AppListServiceImpl(const base::CommandLine& command_line,
+                                       PrefService* local_state)
+    : command_line_(command_line),
       local_state_(local_state),
-      profile_loader_(new ProfileLoader(profile_store_.get())),
-      weak_factory_(this) {
-  profile_store_->AddProfileObserver(this);
+      weak_factory_(this) {}
+
+AppListServiceImpl::~AppListServiceImpl() = default;
+
+void AppListServiceImpl::SetAppListControllerAndClient(
+    ash::mojom::AppListController* app_list_controller,
+    AppListClientImpl* app_list_client) {
+  app_list_controller_ = app_list_controller;
+  controller_delegate_.SetAppListController(app_list_controller);
+  app_list_client_ = app_list_client;
 }
 
-AppListServiceImpl::~AppListServiceImpl() {}
+ash::mojom::AppListController* AppListServiceImpl::GetAppListController() {
+  return app_list_controller_;
+}
+
+app_list::SearchModel* AppListServiceImpl::GetSearchModelFromAsh() {
+  DCHECK(!ash_util::IsRunningInMash());
+  return ash::Shell::HasInstance()
+             ? ash::Shell::Get()->app_list_controller()->search_model()
+             : nullptr;
+}
 
 AppListViewDelegate* AppListServiceImpl::GetViewDelegate() {
   if (!view_delegate_)
@@ -289,151 +85,47 @@
   return view_delegate_.get();
 }
 
-void AppListServiceImpl::SetAppListNextPaintCallback(void (*callback)()) {}
-
-void AppListServiceImpl::Init(Profile* initial_profile) {}
-
-base::FilePath AppListServiceImpl::GetProfilePath(
-    const base::FilePath& user_data_dir) {
-  return user_data_dir.AppendASCII(GetProfileName());
+AppListControllerDelegate* AppListServiceImpl::GetControllerDelegate() {
+  return &controller_delegate_;
 }
 
-void AppListServiceImpl::SetProfilePath(const base::FilePath& profile_path) {
-  local_state_->SetString(
-      prefs::kAppListProfile,
-      profile_path.BaseName().MaybeAsASCII());
-}
-
-void AppListServiceImpl::CreateShortcut() {}
-
-std::string AppListServiceImpl::GetProfileName() {
-  std::string app_list_profile =
-      local_state_->GetString(prefs::kAppListProfile);
-  if (IsValidProfileName(app_list_profile))
-    return app_list_profile;
-
-  // If the user has no profile preference for the app launcher, default to the
-  // last browser profile used.
-  app_list_profile = profile_store_->GetLastUsedProfileName();
-  if (IsValidProfileName(app_list_profile))
-    return app_list_profile;
-
-  // If the last profile used was invalid (ie, guest profile), use the initial
-  // profile.
-  return chrome::kInitialProfile;
-}
-
-void AppListServiceImpl::OnProfileWillBeRemoved(
-    const base::FilePath& profile_path) {
-  // We need to watch for profile removal to keep kAppListProfile updated, for
-  // the case that the deleted profile is being used by the app list.
-  std::string app_list_last_profile = local_state_->GetString(
-      prefs::kAppListProfile);
-  if (profile_path.BaseName().MaybeAsASCII() != app_list_last_profile)
-    return;
-
-  // Switch the app list over to a valid profile.
-  // Before ProfileAttributesStorage::RemoveProfile() calls this function,
-  // ProfileManager::ScheduleProfileForDeletion() will have checked to see if
-  // the deleted profile was also "last used", and updated that setting with
-  // something valid.
-  local_state_->SetString(prefs::kAppListProfile,
-                          local_state_->GetString(prefs::kProfileLastUsed));
-
-  // If the app list was never shown, there won't be a |view_delegate_| yet.
-  if (!view_delegate_)
-    return;
-
-  // The Chrome AppListViewDelegate now needs its profile cleared, because:
-  //  1. it has many references to the profile and can't be profile-keyed, and
-  //  2. the last used profile might not be loaded yet.
-  //    - this loading is sometimes done by the ProfileManager asynchronously,
-  //      so the app list can't just switch to that.
-  // Only Mac supports showing the app list with a NULL profile, so tear down
-  // the view.
-  DestroyAppList();
-  view_delegate_->SetProfile(NULL);
+Profile* AppListServiceImpl::GetCurrentAppListProfile() {
+  return ChromeLauncherController::instance()->profile();
 }
 
 void AppListServiceImpl::Show() {
-  profile_loader_->LoadProfileInvalidatingOtherLoads(
-      GetProfilePath(profile_store_->GetUserDataDir()),
-      base::Bind(&AppListServiceImpl::ShowForProfile,
-                 weak_factory_.GetWeakPtr()));
-}
-
-void AppListServiceImpl::ShowForAppInstall(Profile* profile,
-                                           const std::string& extension_id,
-                                           bool start_discovery_tracking) {
-  if (start_discovery_tracking) {
-    CreateForProfile(profile);
-  } else {
-    // Check if the app launcher has not yet been shown ever. Since this will
-    // show it, if discoverability UMA hasn't yet been recorded, it needs to be
-    // counted as undiscovered.
-    if (local_state_->GetInt64(prefs::kAppListEnableTime) != 0) {
-      local_state_->SetInteger(prefs::kAppListEnableMethod,
-                               ENABLE_SHOWN_UNDISCOVERED);
-    }
-    ShowForProfile(profile);
-  }
-  if (extension_id.empty())
-    return;  // Nothing to highlight. Only used in tests.
-
-  // The only way an install can happen is with the profile already loaded. So,
-  // ShowForProfile() can never be asynchronous, and the model is guaranteed to
-  // exist after a show.
-  DCHECK(view_delegate_->GetModelUpdater());
-  view_delegate_->GetModelUpdater()->HighlightItemInstalledFromUI(extension_id);
-}
-
-void AppListServiceImpl::EnableAppList(Profile* initial_profile,
-                                       AppListEnableSource enable_source) {
-  SetProfilePath(initial_profile->GetPath());
-  // Always allow the webstore "enable" button to re-run the install flow.
-  if (enable_source != AppListService::ENABLE_VIA_WEBSTORE_LINK &&
-      local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled)) {
+  // This may not work correctly if the profile passed in is different from the
+  // one the ash Shell is currently using.
+  if (!app_list_controller_)
     return;
-  }
-
-  local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, true);
-  CreateShortcut();
-
-  // UMA for launcher discoverability.
-  local_state_->SetInt64(prefs::kAppListEnableTime,
-                         base::Time::Now().ToInternalValue());
-  local_state_->SetInteger(prefs::kAppListEnableMethod, enable_source);
-  if (base::ThreadTaskRunnerHandle::IsSet()) {
-    // Ensure a value is recorded if the user "never" shows the app list. Note
-    // there is no message loop in unit tests.
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE, base::Bind(&RecordAppListDiscoverability,
-                              base::Unretained(local_state_), false),
-        base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes));
-  }
+  app_list_controller_->ShowAppList();
 }
 
-void AppListServiceImpl::InvalidatePendingProfileLoads() {
-  profile_loader_->InvalidatePendingProfileLoads();
+void AppListServiceImpl::ShowAndSwitchToState(ash::AppListState state) {
+  if (!app_list_controller_)
+    return;
+  app_list_controller_->ShowAppListAndSwitchToState(state);
 }
 
-void AppListServiceImpl::PerformStartupChecks(Profile* initial_profile) {
-  // Except in rare, once-off cases, this just checks that a pref is "0" and
-  // returns.
-  RecordAppListDiscoverability(local_state_, true);
+void AppListServiceImpl::DismissAppList() {
+  if (!app_list_controller_)
+    return;
+  app_list_controller_->DismissAppList();
+}
 
-  if (command_line_.HasSwitch(app_list::switches::kResetAppListInstallState))
-    local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, false);
+bool AppListServiceImpl::GetTargetVisibility() const {
+  return app_list_target_visible_;
+}
 
-  if (command_line_.HasSwitch(app_list::switches::kEnableAppList))
-    EnableAppList(initial_profile, ENABLE_VIA_COMMAND_LINE);
+bool AppListServiceImpl::IsAppListVisible() const {
+  return app_list_visible_;
+}
 
-  if (!base::ThreadTaskRunnerHandle::IsSet())
-    return;  // In a unit test.
+// static
+AppListService* AppListService::Get() {
+  return AppListServiceImpl::GetInstance();
+}
 
-  // Send app list usage stats after a delay.
-  const int kSendUsageStatsDelay = 5;
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&AppListServiceImpl::SendAppListStats),
-      base::TimeDelta::FromSeconds(kSendUsageStatsDelay));
+void AppListServiceImpl::FlushForTesting() {
+  app_list_client_->FlushMojoForTesting();
 }
diff --git a/chrome/browser/ui/app_list/app_list_service_impl.h b/chrome/browser/ui/app_list/app_list_service_impl.h
index ead6bdc..7f72d58 100644
--- a/chrome/browser/ui/app_list/app_list_service_impl.h
+++ b/chrome/browser/ui/app_list/app_list_service_impl.h
@@ -8,36 +8,42 @@
 #include <memory>
 #include <string>
 
+#include "ash/public/interfaces/app_list.mojom.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/ui/app_list/app_list_controller_impl.h"
 #include "chrome/browser/ui/app_list/app_list_service.h"
-#include "chrome/browser/ui/app_list/profile_loader.h"
 
+class AppListClientImpl;
+class AppListControllerDelegateImpl;
 class AppListViewDelegate;
-class ProfileStore;
+
+namespace app_list {
+class SearchModel;
+}  // namespace app_list
 
 namespace base {
-class FilePath;
+template <typename T>
+struct DefaultSingletonTraits;
 }
 
 namespace test {
 class AppListServiceImplTestApi;
 }
 
-// Parts of the AppListService implementation shared between platforms.
-class AppListServiceImpl : public AppListService,
-                           public ProfileAttributesStorage::Observer {
+// An implementation of AppListService.
+class AppListServiceImpl : public AppListService {
  public:
   ~AppListServiceImpl() override;
 
+  static AppListServiceImpl* GetInstance();
+
   // Constructor used for testing.
   AppListServiceImpl(const base::CommandLine& command_line,
-                     PrefService* local_state,
-                     std::unique_ptr<ProfileStore> profile_store);
+                     PrefService* local_state);
 
   // Lazily create the Chrome AppListViewDelegate and set it to the current user
   // profile.
@@ -47,52 +53,63 @@
   static void RecordAppListAppLaunch();
 
   // AppListService overrides:
-  void SetAppListNextPaintCallback(void (*callback)()) override;
-  void Init(Profile* initial_profile) override;
-  base::FilePath GetProfilePath(const base::FilePath& user_data_dir) override;
-  void SetProfilePath(const base::FilePath& profile_path) override;
+  Profile* GetCurrentAppListProfile() override;
   void Show() override;
-  void ShowForAppInstall(Profile* profile,
-                         const std::string& extension_id,
-                         bool start_discovery_tracking) override;
-  void EnableAppList(Profile* initial_profile,
-                     AppListEnableSource enable_source) override;
-  void CreateShortcut() override;
+  void DismissAppList() override;
+  bool IsAppListVisible() const override;
+  bool GetTargetVisibility() const override;
+  void FlushForTesting() override;
+  AppListControllerDelegate* GetControllerDelegate() override;
+
+  // Shows the app list if it isn't already showing and Switches to |state|,
+  // unless it is |INVALID_STATE| (in which case, opens on the default state).
+  void ShowAndSwitchToState(ash::AppListState state);
+
+  // Updates app list (target) visibility from AppListClientImpl.
+  void set_app_list_visible(bool visible) { app_list_visible_ = visible; }
+  void set_app_list_target_visible(bool visible) {
+    app_list_target_visible_ = visible;
+  }
+
+  // Sets the pointers to the app list controller in Ash, and the app list
+  // client in Chrome.
+  void SetAppListControllerAndClient(
+      ash::mojom::AppListController* app_list_controller,
+      AppListClientImpl* app_list_client);
+
+  // Returns a pointer to control the app list views in ash.
+  ash::mojom::AppListController* GetAppListController();
+
+  // TODO(hejq): Search model migration is not done yet. Chrome still accesses
+  //             it directly in non-mus+ash mode.
+  app_list::SearchModel* GetSearchModelFromAsh();
 
  protected:
   AppListServiceImpl();
 
-  // Create the app list UI, and maintain its state, but do not show it.
-  virtual void CreateForProfile(Profile* requested_profile) = 0;
-
-  // Destroy the app list. Called when the profile that the app list is showing
-  // is being deleted.
-  virtual void DestroyAppList() = 0;
-
-  void InvalidatePendingProfileLoads();
-  ProfileLoader& profile_loader() { return *profile_loader_; }
-  const ProfileLoader& profile_loader() const { return *profile_loader_; }
-
   // Perform startup checks shared between desktop implementations of the app
   // list. Currently this checks command line flags to enable or disable the app
   // list, and records UMA stats delayed from a previous Chrome process.
-  void PerformStartupChecks(Profile* initial_profile);
+  void PerformStartupChecks();
 
  private:
   friend class test::AppListServiceImplTestApi;
+  friend struct base::DefaultSingletonTraits<AppListServiceImpl>;
   static void SendAppListStats();
 
   std::string GetProfileName();
 
-  // ProfileAttributesStorage::Observer overrides:
-  void OnProfileWillBeRemoved(const base::FilePath& profile_path) override;
-
-  std::unique_ptr<ProfileStore> profile_store_;
   base::CommandLine command_line_;
   PrefService* local_state_;
-  std::unique_ptr<ProfileLoader> profile_loader_;
   std::unique_ptr<AppListViewDelegate> view_delegate_;
 
+  AppListControllerDelegateImpl controller_delegate_;
+  ash::mojom::AppListController* app_list_controller_ = nullptr;
+  AppListClientImpl* app_list_client_ = nullptr;
+
+  bool app_list_visible_ = false;
+  bool app_list_target_visible_ = false;
+
   base::WeakPtrFactory<AppListServiceImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AppListServiceImpl);
diff --git a/chrome/browser/ui/app_list/app_list_service_impl_browsertest.cc b/chrome/browser/ui/app_list/app_list_service_impl_browsertest.cc
index cfa0b719..955856a8 100644
--- a/chrome/browser/ui/app_list/app_list_service_impl_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_service_impl_browsertest.cc
@@ -27,7 +27,6 @@
  public:
   explicit AppListServiceImplTestApi(AppListServiceImpl* impl) : impl_(impl) {}
 
-  ProfileLoader* profile_loader() { return impl_->profile_loader_.get(); }
   AppListViewDelegate* view_delegate() { return impl_->view_delegate_.get(); }
 
  private:
@@ -58,40 +57,6 @@
   DISALLOW_COPY_AND_ASSIGN(AppListServiceImplBrowserTest);
 };
 
-// Test that showing a loaded profile for the first time is lazy and
-// synchronous. Then tests that showing a second loaded profile without
-// dismissing correctly switches profiles.
-// crbug.com/459649
-IN_PROC_BROWSER_TEST_F(AppListServiceImplBrowserTest,
-                       DISABLED_ShowLoadedProfiles) {
-  PrefService* local_state = g_browser_process->local_state();
-  EXPECT_FALSE(local_state->HasPrefPath(prefs::kAppListProfile));
-
-  // When never shown, profile path should match the last used profile.
-  base::FilePath user_data_dir =
-      g_browser_process->profile_manager()->user_data_dir();
-  EXPECT_EQ(service_->GetProfilePath(user_data_dir),
-            browser()->profile()->GetPath());
-
-  // Just requesting the profile path shouldn't set it.
-  EXPECT_FALSE(local_state->HasPrefPath(prefs::kAppListProfile));
-
-  // The app list service is bound to ChromeLauncherController, which should
-  // always have a profile.
-  EXPECT_TRUE(service_->GetCurrentAppListProfile());
-
-  // Showing the app list for an unspecified profile, uses the loaded profile.
-  service_->Show();
-
-  // Load should be synchronous.
-  EXPECT_FALSE(test_api_->profile_loader()->IsAnyProfileLoading());
-  EXPECT_EQ(service_->GetCurrentAppListProfile(), browser()->profile());
-
-  // ChromeOS doesn't record the app list profile pref, and doesn't do profile
-  // switching.
-  EXPECT_FALSE(local_state->HasPrefPath(prefs::kAppListProfile));
-}
-
 // Tests that the AppListViewDelegate is created lazily.
 IN_PROC_BROWSER_TEST_F(AppListServiceImplBrowserTest, CreatedLazily) {
   EXPECT_FALSE(test_api_->view_delegate());
@@ -106,7 +71,7 @@
   EXPECT_TRUE(service);
 
   // Show the app list to ensure it has loaded a profile.
-  service_->ShowForProfile(browser()->profile());
+  service_->Show();
   AppListModelUpdater* model_updater = test::GetModelUpdater(service);
   EXPECT_TRUE(model_updater);
 
diff --git a/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc b/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc
index 64097d4..5d8891c 100644
--- a/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc
+++ b/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc
@@ -18,7 +18,7 @@
   AppListClientImpl app_list_client;
   AppListService* service = AppListService::Get();
   ASSERT_FALSE(service->IsAppListVisible());
-  service->ShowForProfile(browser()->profile());
+  service->Show();
   app_list_client.FlushMojoForTesting();
   ASSERT_TRUE(service->IsAppListVisible());
   service->DismissAppList();
diff --git a/chrome/browser/ui/app_list/app_list_service_unittest.cc b/chrome/browser/ui/app_list/app_list_service_unittest.cc
deleted file mode 100644
index 5280606..0000000
--- a/chrome/browser/ui/app_list/app_list_service_unittest.cc
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright 2013 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/app_list/app_list_service.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profiles_state.h"
-#include "chrome/browser/ui/app_list/app_list_service_impl.h"
-#include "chrome/browser/ui/app_list/test/fake_profile.h"
-#include "chrome/browser/ui/app_list/test/fake_profile_store.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/pref_service_factory.h"
-#include "components/prefs/testing_pref_store.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/app_list/app_list_switches.h"
-
-class TestingAppListServiceImpl : public AppListServiceImpl {
- public:
-  TestingAppListServiceImpl(const base::CommandLine& command_line,
-                            PrefService* local_state,
-                            std::unique_ptr<ProfileStore> profile_store)
-      : AppListServiceImpl(command_line, local_state, std::move(profile_store)),
-        showing_for_profile_(NULL),
-        destroy_app_list_call_count_(0) {}
-
-  Profile* showing_for_profile() const {
-    return showing_for_profile_;
-  }
-
-  int destroy_app_list_call_count() const {
-    return destroy_app_list_call_count_;
-  }
-
-  void PerformStartupChecks(Profile* profile) {
-    AppListServiceImpl::PerformStartupChecks(profile);
-  }
-
-  // AppListService overrides:
-  Profile* GetCurrentAppListProfile() override {
-    // We don't return showing_for_profile_ here because that is only defined if
-    // the app list is visible.
-    return NULL;
-  }
-
-  void CreateForProfile(Profile* requested_profile) override {}
-
-  void ShowForProfile(Profile* requested_profile) override {
-    showing_for_profile_ = requested_profile;
-    RecordAppListLaunch();
-  }
-
-  void DismissAppList() override { showing_for_profile_ = NULL; }
-
-  bool IsAppListVisible() const override { return !!showing_for_profile_; }
-
-  bool GetTargetVisibility() const override { return IsAppListVisible(); }
-
-  AppListControllerDelegate* GetControllerDelegate() override { return NULL; }
-
-  void FlushForTesting() override {}
-
-  // AppListServiceImpl overrides:
-  void DestroyAppList() override { ++destroy_app_list_call_count_; }
-
- private:
-  Profile* showing_for_profile_;
-  int destroy_app_list_call_count_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestingAppListServiceImpl);
-};
-
-class AppListServiceUnitTest : public testing::Test {
- public:
-  AppListServiceUnitTest() {}
-
-  void SetUp() override {
-    SetupWithCommandLine(base::CommandLine(base::CommandLine::NO_PROGRAM));
-  }
-
- protected:
-  void SetupWithCommandLine(const base::CommandLine& command_line) {
-    user_data_dir_ = base::FilePath(FILE_PATH_LITERAL("udd"));
-    profile1_.reset(
-        new FakeProfile("p1", user_data_dir_.AppendASCII("profile1")));
-    profile2_.reset(
-        new FakeProfile("p2", user_data_dir_.AppendASCII("profile2")));
-    PrefRegistrySimple* pref_registry = new PrefRegistrySimple;
-
-    AppListService::RegisterPrefs(pref_registry);
-    profiles::RegisterPrefs(pref_registry);
-
-    PrefServiceFactory factory;
-    factory.set_user_prefs(base::MakeRefCounted<TestingPrefStore>());
-    local_state_ = factory.Create(pref_registry);
-
-    profile_store_ = new FakeProfileStore(user_data_dir_, local_state_.get());
-    service_.reset(new TestingAppListServiceImpl(
-        command_line, local_state_.get(),
-        std::unique_ptr<ProfileStore>(profile_store_)));
-  }
-
-  void EnableAppList() {
-    service_->EnableAppList(profile1_.get(),
-                            AppListService::ENABLE_VIA_COMMAND_LINE);
-  }
-
-  base::FilePath user_data_dir_;
-  std::unique_ptr<PrefService> local_state_;
-  FakeProfileStore* profile_store_;
-  std::unique_ptr<TestingAppListServiceImpl> service_;
-  std::unique_ptr<FakeProfile> profile1_;
-  std::unique_ptr<FakeProfile> profile2_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppListServiceUnitTest);
-};
-
-TEST_F(AppListServiceUnitTest, EnablingStateIsPersisted) {
-  EXPECT_FALSE(local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled));
-  EnableAppList();
-  EXPECT_TRUE(local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled));
-  EXPECT_EQ(profile1_->GetPath(), user_data_dir_.Append(
-      local_state_->GetFilePath(prefs::kAppListProfile)));
-}
-
-TEST_F(AppListServiceUnitTest, ShowingForProfileLoadsAProfile) {
-  profile_store_->LoadProfile(profile1_.get());
-  EnableAppList();
-  service_->Show();
-  EXPECT_EQ(profile1_.get(), service_->showing_for_profile());
-  EXPECT_TRUE(service_->IsAppListVisible());
-}
-
-TEST_F(AppListServiceUnitTest, RemovedProfileResetsToInitialProfile) {
-  EnableAppList();
-  profile_store_->RemoveProfile(profile1_.get());
-
-  // kAppListProfile should have been cleared, and therefore GetProfilePath
-  // should return the initial profile.
-  EXPECT_EQ("", local_state_->GetString(prefs::kAppListProfile));
-  base::FilePath initial_profile_path =
-      user_data_dir_.AppendASCII(chrome::kInitialProfile);
-  EXPECT_EQ(initial_profile_path,
-            service_->GetProfilePath(profile_store_->GetUserDataDir()));
-}
-
-TEST_F(AppListServiceUnitTest,
-       RemovedProfileResetsToLastUsedProfileIfExists) {
-  local_state_->SetString(prefs::kProfileLastUsed, "last-used");
-  EnableAppList();
-  profile_store_->RemoveProfile(profile1_.get());
-
-  // kAppListProfile should have been set to kProfileLastUsed.
-  EXPECT_EQ("last-used", local_state_->GetString(prefs::kAppListProfile));
-  base::FilePath last_used_profile_path =
-      user_data_dir_.AppendASCII("last-used");
-  EXPECT_EQ(last_used_profile_path,
-            service_->GetProfilePath(profile_store_->GetUserDataDir()));
-
-  // For this test, the AppListViewDelegate is not created because the
-  // app list is never shown, so there is nothing to destroy.
-  EXPECT_EQ(0, service_->destroy_app_list_call_count());
-}
-
-TEST_F(AppListServiceUnitTest, RefusesToLoadGuestAppListProfile) {
-  // Unlikely, but if somehow the user's app_list.profile pref was set to the
-  // guest profile, make sure we refuse to load it (or it would crash).
-  local_state_->SetString(
-      prefs::kAppListProfile,
-      base::FilePath(chrome::kGuestProfileDir).MaybeAsASCII());
-  local_state_->SetString(prefs::kProfileLastUsed, "last-used");
-  base::FilePath last_used_profile_path =
-      user_data_dir_.AppendASCII("last-used");
-  EXPECT_EQ(last_used_profile_path,
-            service_->GetProfilePath(profile_store_->GetUserDataDir()));
-}
-
-TEST_F(AppListServiceUnitTest, RefusesToLoadGuestLastUsedProfile) {
-  // If the user's most recent browser session was a guest session, make sure we
-  // do not open a guest profile in the launcher (which would crash).
-  local_state_->SetString(
-      prefs::kProfileLastUsed,
-      base::FilePath(chrome::kGuestProfileDir).MaybeAsASCII());
-  base::FilePath initial_profile_path =
-      user_data_dir_.AppendASCII(chrome::kInitialProfile);
-  EXPECT_EQ(initial_profile_path,
-            service_->GetProfilePath(profile_store_->GetUserDataDir()));
-}
-
-TEST_F(AppListServiceUnitTest, SwitchingProfilesPersists) {
-  profile_store_->LoadProfile(profile1_.get());
-  profile_store_->LoadProfile(profile2_.get());
-  EnableAppList();
-  service_->SetProfilePath(profile2_->GetPath());
-  service_->Show();
-  EXPECT_EQ(profile2_.get(), service_->showing_for_profile());
-  EXPECT_EQ(profile2_->GetPath(),
-            service_->GetProfilePath(profile_store_->GetUserDataDir()));
-  service_->SetProfilePath(profile1_->GetPath());
-  EXPECT_EQ(profile1_->GetPath(),
-            service_->GetProfilePath(profile_store_->GetUserDataDir()));
-}
-
-TEST_F(AppListServiceUnitTest, EnableViaCommandLineFlag) {
-  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
-  command_line.AppendSwitch(app_list::switches::kEnableAppList);
-  SetupWithCommandLine(command_line);
-  service_->PerformStartupChecks(profile1_.get());
-  EXPECT_TRUE(local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled));
-}
-
-TEST_F(AppListServiceUnitTest, DisableViaCommandLineFlag) {
-  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
-  command_line.AppendSwitch(app_list::switches::kResetAppListInstallState);
-  SetupWithCommandLine(command_line);
-  service_->PerformStartupChecks(profile1_.get());
-  EXPECT_FALSE(local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled));
-}
-
-TEST_F(AppListServiceUnitTest, UMAPrefStates) {
-  EXPECT_FALSE(local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled));
-  EXPECT_EQ(AppListService::ENABLE_NOT_RECORDED,
-            local_state_->GetInteger(prefs::kAppListEnableMethod));
-  EXPECT_EQ(0, local_state_->GetInt64(prefs::kAppListEnableTime));
-
-  service_->EnableAppList(profile1_.get(),
-                          AppListService::ENABLE_FOR_APP_INSTALL);
-
-  // After enable, method and time should be recorded.
-  EXPECT_TRUE(local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled));
-  EXPECT_EQ(AppListService::ENABLE_FOR_APP_INSTALL,
-            local_state_->GetInteger(prefs::kAppListEnableMethod));
-  EXPECT_NE(0, local_state_->GetInt64(prefs::kAppListEnableTime));
-
-  service_->ShowForProfile(profile1_.get());
-
-  // After a regular "show", time should be cleared, so UMA is not re-recorded.
-  EXPECT_EQ(AppListService::ENABLE_FOR_APP_INSTALL,
-            local_state_->GetInteger(prefs::kAppListEnableMethod));
-  EXPECT_EQ(0, local_state_->GetInt64(prefs::kAppListEnableTime));
-
-  // A second enable should be a no-op.
-  service_->EnableAppList(profile1_.get(),
-                          AppListService::ENABLE_FOR_APP_INSTALL);
-  EXPECT_EQ(AppListService::ENABLE_FOR_APP_INSTALL,
-            local_state_->GetInteger(prefs::kAppListEnableMethod));
-  EXPECT_EQ(0, local_state_->GetInt64(prefs::kAppListEnableTime));
-
-  // An app install auto-show here should keep the recorded enable method.
-  service_->ShowForAppInstall(profile1_.get(), "", false);
-  EXPECT_EQ(AppListService::ENABLE_FOR_APP_INSTALL,
-            local_state_->GetInteger(prefs::kAppListEnableMethod));
-
-  // Clear the enable state, so we can enable again.
-  local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, false);
-  service_->EnableAppList(profile1_.get(),
-                          AppListService::ENABLE_FOR_APP_INSTALL);
-
-  EXPECT_EQ(AppListService::ENABLE_FOR_APP_INSTALL,
-            local_state_->GetInteger(prefs::kAppListEnableMethod));
-  EXPECT_NE(0, local_state_->GetInt64(prefs::kAppListEnableTime));
-
-  // An auto-show here should update the enable method to prevent recording it
-  // as ENABLE_FOR_APP_INSTALL.
-  service_->ShowForAppInstall(profile1_.get(), "", false);
-  EXPECT_EQ(AppListService::ENABLE_SHOWN_UNDISCOVERED,
-            local_state_->GetInteger(prefs::kAppListEnableMethod));
-}
diff --git a/chrome/browser/ui/app_list/app_list_util.cc b/chrome/browser/ui/app_list/app_list_util.cc
index 74f0f24..dfa3698 100644
--- a/chrome/browser/ui/app_list/app_list_util.cc
+++ b/chrome/browser/ui/app_list/app_list_util.cc
@@ -5,18 +5,11 @@
 #include "chrome/browser/ui/app_list/app_list_util.h"
 
 #include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_service.h"
 
 bool IsAppLauncherEnabled() {
-#if !BUILDFLAG(ENABLE_APP_LIST)
-  return false;
-#elif defined(OS_CHROMEOS)
+#if defined(OS_CHROMEOS)
   return true;
 #else
-  PrefService* prefs = g_browser_process->local_state();
-  // In some tests, the prefs aren't initialised.
-  return prefs && prefs->GetBoolean(prefs::kAppLauncherHasBeenEnabled);
+  return false;
 #endif
 }
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.cc b/chrome/browser/ui/app_list/app_list_view_delegate.cc
index 6d4e9a8..86e032d 100644
--- a/chrome/browser/ui/app_list/app_list_view_delegate.cc
+++ b/chrome/browser/ui/app_list/app_list_view_delegate.cc
@@ -24,11 +24,11 @@
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state_watcher.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
 #include "chrome/browser/ui/app_list/search/search_controller_factory.h"
 #include "chrome/browser/ui/app_list/search/search_resource_manager.h"
 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
-#include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/ui/ash/app_sync_ui_state.cc b/chrome/browser/ui/app_list/app_sync_ui_state.cc
similarity index 95%
rename from chrome/browser/ui/ash/app_sync_ui_state.cc
rename to chrome/browser/ui/app_list/app_sync_ui_state.cc
index 6ce06111e8..dadc384 100644
--- a/chrome/browser/ui/ash/app_sync_ui_state.cc
+++ b/chrome/browser/ui/app_list/app_sync_ui_state.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/app_sync_ui_state.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state.h"
 
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/pending_extension_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/ui/ash/app_sync_ui_state_factory.h"
-#include "chrome/browser/ui/ash/app_sync_ui_state_observer.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state_factory.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state_observer.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ui/ash/app_sync_ui_state.h b/chrome/browser/ui/app_list/app_sync_ui_state.h
similarity index 94%
rename from chrome/browser/ui/ash/app_sync_ui_state.h
rename to chrome/browser/ui/app_list/app_sync_ui_state.h
index 0e9901b..db16b2d9 100644
--- a/chrome/browser/ui/ash/app_sync_ui_state.h
+++ b/chrome/browser/ui/app_list/app_sync_ui_state.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_APP_SYNC_UI_STATE_H_
-#define CHROME_BROWSER_UI_ASH_APP_SYNC_UI_STATE_H_
+#ifndef CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_H_
+#define CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_H_
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
@@ -91,4 +91,4 @@
   DISALLOW_COPY_AND_ASSIGN(AppSyncUIState);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_APP_SYNC_UI_STATE_H_
+#endif  // CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_H_
diff --git a/chrome/browser/ui/ash/app_sync_ui_state_factory.cc b/chrome/browser/ui/app_list/app_sync_ui_state_factory.cc
similarity index 91%
rename from chrome/browser/ui/ash/app_sync_ui_state_factory.cc
rename to chrome/browser/ui/app_list/app_sync_ui_state_factory.cc
index 71e1ae1..9bc9a00 100644
--- a/chrome/browser/ui/ash/app_sync_ui_state_factory.cc
+++ b/chrome/browser/ui/app_list/app_sync_ui_state_factory.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/app_sync_ui_state_factory.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state_factory.h"
 
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/ui/ash/app_sync_ui_state.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "extensions/browser/extension_registry_factory.h"
 
diff --git a/chrome/browser/ui/ash/app_sync_ui_state_factory.h b/chrome/browser/ui/app_list/app_sync_ui_state_factory.h
similarity index 83%
rename from chrome/browser/ui/ash/app_sync_ui_state_factory.h
rename to chrome/browser/ui/app_list/app_sync_ui_state_factory.h
index 250b62de..e4b5759 100644
--- a/chrome/browser/ui/ash/app_sync_ui_state_factory.h
+++ b/chrome/browser/ui/app_list/app_sync_ui_state_factory.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_APP_SYNC_UI_STATE_FACTORY_H_
-#define CHROME_BROWSER_UI_ASH_APP_SYNC_UI_STATE_FACTORY_H_
+#ifndef CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_FACTORY_H_
+#define CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_FACTORY_H_
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
@@ -33,4 +33,4 @@
   DISALLOW_COPY_AND_ASSIGN(AppSyncUIStateFactory);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_APP_SYNC_UI_STATE_FACTORY_H_
+#endif  // CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_FACTORY_H_
diff --git a/chrome/browser/ui/ash/app_sync_ui_state_observer.h b/chrome/browser/ui/app_list/app_sync_ui_state_observer.h
similarity index 65%
rename from chrome/browser/ui/ash/app_sync_ui_state_observer.h
rename to chrome/browser/ui/app_list/app_sync_ui_state_observer.h
index 361648e8..d13820e 100644
--- a/chrome/browser/ui/ash/app_sync_ui_state_observer.h
+++ b/chrome/browser/ui/app_list/app_sync_ui_state_observer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_APP_SYNC_UI_STATE_OBSERVER_H_
-#define CHROME_BROWSER_UI_ASH_APP_SYNC_UI_STATE_OBSERVER_H_
+#ifndef CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_OBSERVER_H_
+#define CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_OBSERVER_H_
 
 class AppSyncUIStateObserver {
  public:
@@ -14,4 +14,4 @@
   virtual ~AppSyncUIStateObserver() {}
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_APP_SYNC_UI_STATE_OBSERVER_H_
+#endif  // CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_OBSERVER_H_
diff --git a/chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.cc b/chrome/browser/ui/app_list/app_sync_ui_state_watcher.cc
similarity index 88%
rename from chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.cc
rename to chrome/browser/ui/app_list/app_sync_ui_state_watcher.cc
index f45f34f..326a694 100644
--- a/chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.cc
+++ b/chrome/browser/ui/app_list/app_sync_ui_state_watcher.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state_watcher.h"
 
 #include "chrome/browser/ui/app_list/app_list_model_updater.h"
-#include "chrome/browser/ui/ash/app_sync_ui_state.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state.h"
 
 AppSyncUIStateWatcher::AppSyncUIStateWatcher(Profile* profile,
                                              AppListModelUpdater* model_updater)
diff --git a/chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h b/chrome/browser/ui/app_list/app_sync_ui_state_watcher.h
similarity index 76%
rename from chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h
rename to chrome/browser/ui/app_list/app_sync_ui_state_watcher.h
index 8c21e10..fe5a6fb 100644
--- a/chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h
+++ b/chrome/browser/ui/app_list/app_sync_ui_state_watcher.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_APP_LIST_APP_SYNC_UI_STATE_WATCHER_H_
-#define CHROME_BROWSER_UI_ASH_APP_LIST_APP_SYNC_UI_STATE_WATCHER_H_
+#ifndef CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_WATCHER_H_
+#define CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_WATCHER_H_
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "chrome/browser/ui/ash/app_sync_ui_state_observer.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state_observer.h"
 
 class AppListModelUpdater;
 class AppSyncUIState;
@@ -31,4 +31,4 @@
   DISALLOW_COPY_AND_ASSIGN(AppSyncUIStateWatcher);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_APP_LIST_APP_SYNC_UI_STATE_WATCHER_H_
+#endif  // CHROME_BROWSER_UI_APP_LIST_APP_SYNC_UI_STATE_WATCHER_H_
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index c161d57..64d3bfd 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -1457,32 +1457,6 @@
     observer.OnNotificationsEnabledChanged(package_name, enabled);
 }
 
-void ArcAppListPrefs::MaybeShowPackageInAppLauncher(
-    const arc::mojom::ArcPackageInfo& package_info) {
-  // Ignore system packages and auxiliary packages.
-  if (!package_info.sync || package_info.system)
-    return;
-
-  std::unordered_set<std::string> app_ids =
-      GetAppsForPackage(package_info.package_name);
-  for (const auto& app_id : app_ids) {
-    std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = GetApp(app_id);
-    if (!app_info) {
-      NOTREACHED();
-      continue;
-    }
-    if (!app_info->showInLauncher)
-      continue;
-
-    AppListService* service = AppListService::Get();
-    CHECK(service);
-    service->ShowForAppInstall(profile_, app_id, false);
-    last_shown_batch_installation_revision_ =
-        current_batch_installation_revision_;
-    break;
-  }
-}
-
 bool ArcAppListPrefs::IsUnknownPackage(const std::string& package_name) const {
   return !GetPackage(package_name) && sync_service_ &&
          !sync_service_->IsPackageSyncing(package_name);
@@ -1492,18 +1466,9 @@
     arc::mojom::ArcPackageInfoPtr package_info) {
   DCHECK(IsArcAndroidEnabledForProfile(profile_));
 
-  // Ignore packages installed by internal sync.
-  const bool unknown_package = IsUnknownPackage(package_info->package_name);
-
   AddOrUpdatePackagePrefs(prefs_, *package_info);
   for (auto& observer : observer_list_)
     observer.OnPackageInstalled(*package_info);
-
-  if (unknown_package &&
-      current_batch_installation_revision_ !=
-          last_shown_batch_installation_revision_) {
-    MaybeShowPackageInAppLauncher(*package_info);
-  }
 }
 
 void ArcAppListPrefs::OnPackageModified(
@@ -1618,9 +1583,6 @@
 
 void ArcAppListPrefs::OnInstallationStarted(
     const base::Optional<std::string>& package_name) {
-  // Start new batch installation group if this is first installation.
-  if (!installing_packages_count_)
-    ++current_batch_installation_revision_;
   ++installing_packages_count_;
 
   if (package_name.has_value() && default_apps_.HasPackage(*package_name))
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 25a5fe18..d7b7174 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
@@ -402,12 +402,6 @@
                          const std::string& package_name,
                          const std::string& activity);
 
-  // Reveals first app from provided package in app launcher if package is newly
-  // installed by user. If all apps in package are hidden then app list is not
-  // shown.
-  void MaybeShowPackageInAppLauncher(
-      const arc::mojom::ArcPackageInfo& package_info);
-
   // Returns true is specified package is new in the system, was not installed
   // and it is not scheduled to install by sync.
   bool IsUnknownPackage(const std::string& package_name) const;
@@ -490,7 +484,6 @@
   bool default_apps_ready_ = false;
   ArcDefaultAppList default_apps_;
   base::Closure default_apps_ready_callback_;
-  int last_shown_batch_installation_revision_ = -1;
   int current_batch_installation_revision_ = 0;
 
   // TODO (b/70566216): Remove this once fixed.
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
index b3376fc..2232123 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
@@ -9,8 +9,8 @@
 
 #include "ash/app_list/model/search/search_model.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/app_list/app_list_service_impl.h"
 #include "chrome/browser/ui/app_list/chrome_app_list_item.h"
-#include "chrome/browser/ui/ash/app_list/app_list_service_ash.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "extensions/common/constants.h"
 #include "ui/base/models/menu_model.h"
@@ -19,7 +19,7 @@
     : profile_(profile), weak_ptr_factory_(this) {
   // TODO(hejq): remove this when search migration is done.
   if (!ash_util::IsRunningInMash())
-    search_model_ = AppListServiceAsh::GetInstance()->GetSearchModelFromAsh();
+    search_model_ = AppListServiceImpl::GetInstance()->GetSearchModelFromAsh();
 }
 
 ChromeAppListModelUpdater::~ChromeAppListModelUpdater() {}
@@ -30,7 +30,7 @@
     return;
 
   app_list_controller_ =
-      active ? AppListServiceAsh::GetInstance()->GetAppListController()
+      active ? AppListServiceImpl::GetInstance()->GetAppListController()
              : nullptr;
   if (!app_list_controller_)
     return;
diff --git a/chrome/browser/ui/app_list/profile_loader.cc b/chrome/browser/ui/app_list/profile_loader.cc
deleted file mode 100644
index 5dea8aac..0000000
--- a/chrome/browser/ui/app_list/profile_loader.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2013 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/app_list/profile_loader.h"
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "chrome/browser/ui/app_list/profile_store.h"
-#include "components/keep_alive_registry/keep_alive_types.h"
-#include "components/keep_alive_registry/scoped_keep_alive.h"
-
-#if !defined(OS_CHROMEOS)
-#include "chrome/browser/ui/user_manager.h"
-#endif  // !defined(OS_CHROMEOS)
-
-ProfileLoader::ProfileLoader(ProfileStore* profile_store)
-    : profile_store_(profile_store),
-      profile_load_sequence_id_(0),
-      pending_profile_loads_(0),
-      weak_factory_(this) {
-}
-
-ProfileLoader::~ProfileLoader() {
-}
-
-bool ProfileLoader::IsAnyProfileLoading() const {
-  return pending_profile_loads_ > 0;
-}
-
-void ProfileLoader::InvalidatePendingProfileLoads() {
-  ++profile_load_sequence_id_;
-}
-
-void ProfileLoader::LoadProfileInvalidatingOtherLoads(
-    const base::FilePath& profile_file_path,
-    base::Callback<void(Profile*)> callback) {
-  InvalidatePendingProfileLoads();
-
-  if (profile_store_->IsProfileLocked(profile_file_path)) {
-#if !defined(OS_CHROMEOS)
-    UserManager::Show(base::FilePath(),
-                      profiles::USER_MANAGER_SELECT_PROFILE_APP_LAUNCHER);
-#endif  // !defined(OS_CHROMEOS)
-    return;
-  }
-
-  Profile* profile = profile_store_->GetProfileByPath(profile_file_path);
-  if (profile) {
-    callback.Run(profile);
-    return;
-  }
-
-  IncrementPendingProfileLoads();
-  profile_store_->LoadProfileAsync(
-      profile_file_path,
-      base::Bind(&ProfileLoader::OnProfileLoaded,
-                 weak_factory_.GetWeakPtr(),
-                 profile_load_sequence_id_,
-                 callback));
-}
-
-void ProfileLoader::OnProfileLoaded(int profile_load_sequence_id,
-                                    base::Callback<void(Profile*)> callback,
-                                    Profile* profile) {
-  DecrementPendingProfileLoads();
-  if (profile_load_sequence_id == profile_load_sequence_id_)
-    callback.Run(profile);
-}
-
-void ProfileLoader::IncrementPendingProfileLoads() {
-  pending_profile_loads_++;
-  if (pending_profile_loads_ == 1)
-    keep_alive_.reset(new ScopedKeepAlive(KeepAliveOrigin::PROFILE_LOADER,
-                                          KeepAliveRestartOption::DISABLED));
-}
-
-void ProfileLoader::DecrementPendingProfileLoads() {
-  pending_profile_loads_--;
-  if (pending_profile_loads_ == 0)
-    keep_alive_.reset();
-}
diff --git a/chrome/browser/ui/app_list/profile_loader.h b/chrome/browser/ui/app_list/profile_loader.h
deleted file mode 100644
index e5cbd42d..0000000
--- a/chrome/browser/ui/app_list/profile_loader.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2013 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_APP_LIST_PROFILE_LOADER_H_
-#define CHROME_BROWSER_UI_APP_LIST_PROFILE_LOADER_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-
-namespace base {
-class FilePath;
-}
-
-class Profile;
-class ProfileLoaderUnittest;
-class ProfileStore;
-class ScopedKeepAlive;
-
-// This class loads profiles asynchronously and calls the provided callback once
-// the profile is ready. Only the callback for the most recent load request is
-// called, and only if the load was successful.
-//
-// This is useful for loading profiles in response to user interaction where
-// only the latest requested profile is required.
-// TODO(koz): Merge this into AppListServiceImpl.
-class ProfileLoader {
- public:
-  explicit ProfileLoader(ProfileStore* profile_store);
-  ~ProfileLoader();
-
-  bool IsAnyProfileLoading() const;
-  void InvalidatePendingProfileLoads();
-  void LoadProfileInvalidatingOtherLoads(
-      const base::FilePath& profile_file_path,
-      base::Callback<void(Profile*)> callback);
-
- private:
-  friend class ::ProfileLoaderUnittest;
-
-  void OnProfileLoaded(int profile_load_sequence_id,
-                       base::Callback<void(Profile*)> callback,
-                       Profile* profile);
-
-  void IncrementPendingProfileLoads();
-  void DecrementPendingProfileLoads();
-
-  ProfileStore* profile_store_;
-  std::unique_ptr<ScopedKeepAlive> keep_alive_;
-  int profile_load_sequence_id_;
-  int pending_profile_loads_;
-
-  base::WeakPtrFactory<ProfileLoader> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProfileLoader);
-};
-
-#endif  // CHROME_BROWSER_UI_APP_LIST_PROFILE_LOADER_H_
diff --git a/chrome/browser/ui/app_list/profile_loader_unittest.cc b/chrome/browser/ui/app_list/profile_loader_unittest.cc
deleted file mode 100644
index 39cd45d..0000000
--- a/chrome/browser/ui/app_list/profile_loader_unittest.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2013 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/app_list/profile_loader.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_list/test/fake_profile.h"
-#include "chrome/browser/ui/app_list/test/fake_profile_store.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class ProfileLoaderUnittest : public testing::Test {
- public:
-  void SetUp() override {
-    last_callback_result_ = NULL;
-    profile1_.reset(
-        new FakeProfile("p1", base::FilePath(FILE_PATH_LITERAL("profile1"))));
-    profile2_.reset(
-        new FakeProfile("p2", base::FilePath(FILE_PATH_LITERAL("profile2"))));
-
-    profile_store_.reset(new FakeProfileStore(
-        base::FilePath(FILE_PATH_LITERAL("udd")), nullptr));
-    loader_.reset(new ProfileLoader(profile_store_.get()));
-  }
-
-  void StartLoadingProfile(Profile* profile) {
-    loader_->LoadProfileInvalidatingOtherLoads(
-        profile->GetPath(),
-        base::Bind(&ProfileLoaderUnittest::OnProfileLoaded,
-                  base::Unretained(this)));
-  }
-
-  void FinishLoadingProfile(Profile* profile) {
-    profile_store_->LoadProfile(profile);
-  }
-
-  void OnProfileLoaded(Profile* profile) {
-    last_callback_result_ = profile;
-  }
-
-  bool HasKeepAlive() const {
-    return loader_->keep_alive_.get() != NULL;
-  }
-
- protected:
-  std::unique_ptr<ProfileLoader> loader_;
-  std::unique_ptr<FakeProfileStore> profile_store_;
-  std::unique_ptr<FakeProfile> profile1_;
-  std::unique_ptr<FakeProfile> profile2_;
-  Profile* last_callback_result_;
-};
-
-TEST_F(ProfileLoaderUnittest, LoadASecondProfileBeforeTheFirstFinishes) {
-  EXPECT_FALSE(loader_->IsAnyProfileLoading());
-
-  StartLoadingProfile(profile1_.get());
-  EXPECT_TRUE(loader_->IsAnyProfileLoading());
-
-  StartLoadingProfile(profile2_.get());
-  FinishLoadingProfile(profile1_.get());
-  EXPECT_EQ(NULL, last_callback_result_);
-
-  FinishLoadingProfile(profile2_.get());
-  EXPECT_EQ(profile2_.get(), last_callback_result_);
-  EXPECT_FALSE(loader_->IsAnyProfileLoading());
-}
-
-TEST_F(ProfileLoaderUnittest, TestKeepsAliveWhileLoadingProfiles) {
-  EXPECT_FALSE(loader_->IsAnyProfileLoading());
-  EXPECT_FALSE(HasKeepAlive());
-
-  StartLoadingProfile(profile1_.get());
-  EXPECT_TRUE(loader_->IsAnyProfileLoading());
-  EXPECT_TRUE(HasKeepAlive());
-
-  FinishLoadingProfile(profile1_.get());
-  EXPECT_FALSE(loader_->IsAnyProfileLoading());
-  EXPECT_FALSE(HasKeepAlive());
-}
-
-TEST_F(ProfileLoaderUnittest, TestKeepsAliveWhileLoadingMultipleProfiles) {
-  EXPECT_FALSE(loader_->IsAnyProfileLoading());
-  EXPECT_FALSE(HasKeepAlive());
-
-  StartLoadingProfile(profile1_.get());
-  EXPECT_TRUE(loader_->IsAnyProfileLoading());
-  EXPECT_TRUE(HasKeepAlive());
-
-  StartLoadingProfile(profile2_.get());
-  EXPECT_TRUE(loader_->IsAnyProfileLoading());
-  EXPECT_TRUE(HasKeepAlive());
-
-  FinishLoadingProfile(profile1_.get());
-  EXPECT_TRUE(loader_->IsAnyProfileLoading());
-  EXPECT_TRUE(HasKeepAlive());
-
-  FinishLoadingProfile(profile2_.get());
-  EXPECT_FALSE(loader_->IsAnyProfileLoading());
-  EXPECT_FALSE(HasKeepAlive());
-}
-
-TEST_F(ProfileLoaderUnittest, TestInvalidatingCurrentLoad) {
-  EXPECT_FALSE(loader_->IsAnyProfileLoading());
-  EXPECT_FALSE(HasKeepAlive());
-
-  StartLoadingProfile(profile1_.get());
-  loader_->InvalidatePendingProfileLoads();
-  // The profile is still considered loading even though we will do nothing when
-  // it gets here.
-  EXPECT_TRUE(loader_->IsAnyProfileLoading());
-  EXPECT_TRUE(HasKeepAlive());
-
-  FinishLoadingProfile(profile1_.get());
-  EXPECT_EQ(NULL, last_callback_result_);
-}
diff --git a/chrome/browser/ui/app_list/profile_store.h b/chrome/browser/ui/app_list/profile_store.h
deleted file mode 100644
index ca943f58..0000000
--- a/chrome/browser/ui/app_list/profile_store.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2013 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_APP_LIST_PROFILE_STORE_H_
-#define CHROME_BROWSER_UI_APP_LIST_PROFILE_STORE_H_
-
-#include "base/callback_forward.h"
-#include "base/files/file_path.h"
-#include "chrome/browser/profiles/profile_attributes_storage.h"
-
-class Profile;
-
-// Represents something that knows how to load profiles asynchronously.
-class ProfileStore {
- public:
-  virtual ~ProfileStore() {}
-  virtual void AddProfileObserver(
-      ProfileAttributesStorage::Observer* observer) = 0;
-
-  // Loads the profile at |path| and calls |callback| when its done. A NULL
-  // Profile* represents an error.
-  virtual void LoadProfileAsync(const base::FilePath& path,
-                                base::Callback<void(Profile*)> callback) = 0;
-
-  // Returns the profile at |path| if it is already loaded.
-  virtual Profile* GetProfileByPath(const base::FilePath& path) = 0;
-
-  // The user data directory that profiles are stored under in this instance of
-  // Chrome.
-  virtual base::FilePath GetUserDataDir() = 0;
-
-  // The name of the last used profile.
-  virtual std::string GetLastUsedProfileName() = 0;
-
-  // Returns true if the profile at |path| is supervised.
-  virtual bool IsProfileSupervised(const base::FilePath& path) = 0;
-
-  // Returns true if the profile at |path| is locked.
-  virtual bool IsProfileLocked(const base::FilePath& path) = 0;
-};
-
-#endif  // CHROME_BROWSER_UI_APP_LIST_PROFILE_STORE_H_
diff --git a/chrome/browser/ui/app_list/test/fake_profile_store.cc b/chrome/browser/ui/app_list/test/fake_profile_store.cc
deleted file mode 100644
index 83be0ec..0000000
--- a/chrome/browser/ui/app_list/test/fake_profile_store.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2013 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/app_list/test/fake_profile_store.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_constants.h"
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_service.h"
-
-FakeProfileStore::FakeProfileStore(const base::FilePath& user_data_dir,
-                                   PrefService* local_state)
-    : user_data_dir_(user_data_dir), local_state_(local_state) {
-}
-
-FakeProfileStore::~FakeProfileStore() {
-}
-
-void FakeProfileStore::LoadProfile(Profile* profile) {
-  loaded_profiles_[profile->GetPath()] = profile;
-  CallbacksByPath::iterator it = callbacks_.find(profile->GetPath());
-  if (it != callbacks_.end()) {
-    it->second.Run(profile);
-    callbacks_.erase(it);
-  }
-}
-
-void FakeProfileStore::RemoveProfile(Profile* profile) {
-  base::FilePath path(profile->GetPath());
-  for (auto& observer : observer_list_)
-    observer.OnProfileWillBeRemoved(path);
-  loaded_profiles_.erase(path);
-  for (auto& observer : observer_list_)
-    observer.OnProfileWasRemoved(path, base::string16());
-}
-
-void FakeProfileStore::AddProfileObserver(
-    ProfileAttributesStorage::Observer* observer) {
-  observer_list_.AddObserver(observer);
-}
-
-void FakeProfileStore::LoadProfileAsync(
-    const base::FilePath& path,
-    base::Callback<void(Profile*)> callback) {
-  Profile* profile = GetProfileByPath(path);
-  if (profile) {
-    callback.Run(profile);
-    return;
-  }
-  callbacks_[path] = callback;
-}
-
-Profile* FakeProfileStore::GetProfileByPath(
-    const base::FilePath& path) {
-  ProfilesByPath::const_iterator it = loaded_profiles_.find(path);
-  if (it != loaded_profiles_.end())
-    return it->second;
-  return NULL;
-}
-
-base::FilePath FakeProfileStore::GetUserDataDir() {
-  return user_data_dir_;
-}
-
-std::string FakeProfileStore::GetLastUsedProfileName() {
-  std::string profile_name = local_state_->GetString(prefs::kProfileLastUsed);
-
-  if (!profile_name.empty())
-    return profile_name;
-
-  // If there is no last used profile recorded, use the initial profile.
-  return chrome::kInitialProfile;
-}
-
-bool FakeProfileStore::IsProfileSupervised(const base::FilePath& path) {
-  return false;
-}
-
-bool FakeProfileStore::IsProfileLocked(const base::FilePath& path) {
-  return false;
-}
diff --git a/chrome/browser/ui/app_list/test/fake_profile_store.h b/chrome/browser/ui/app_list/test/fake_profile_store.h
deleted file mode 100644
index 06a3c550..0000000
--- a/chrome/browser/ui/app_list/test/fake_profile_store.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2013 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_APP_LIST_TEST_FAKE_PROFILE_STORE_H_
-#define CHROME_BROWSER_UI_APP_LIST_TEST_FAKE_PROFILE_STORE_H_
-
-#include <map>
-
-#include "base/callback.h"
-#include "base/observer_list.h"
-#include "chrome/browser/profiles/profile_attributes_storage.h"
-#include "chrome/browser/ui/app_list/profile_store.h"
-
-class PrefService;
-
-class FakeProfileStore : public ProfileStore {
- public:
-  FakeProfileStore(const base::FilePath& user_data_dir,
-                   PrefService* local_state);
-  ~FakeProfileStore() override;
-
-  void LoadProfile(Profile* profile);
-  void RemoveProfile(Profile* profile);
-
-  // ProfileStore overrides.
-  void AddProfileObserver(ProfileAttributesStorage::Observer* observer)
-      override;
-  void LoadProfileAsync(const base::FilePath& path,
-                        base::Callback<void(Profile*)> callback) override;
-  Profile* GetProfileByPath(const base::FilePath& path) override;
-  base::FilePath GetUserDataDir() override;
-  std::string GetLastUsedProfileName() override;
-  bool IsProfileSupervised(const base::FilePath& path) override;
-  bool IsProfileLocked(const base::FilePath& path) override;
-
- private:
-  base::FilePath user_data_dir_;
-  PrefService* local_state_;
-  typedef std::map<base::FilePath, base::Callback<void(Profile*)> >
-      CallbacksByPath;
-  CallbacksByPath callbacks_;
-  base::ObserverList<ProfileAttributesStorage::Observer> observer_list_;
-  typedef std::map<base::FilePath, Profile*> ProfilesByPath;
-  ProfilesByPath loaded_profiles_;
-};
-
-#endif  // CHROME_BROWSER_UI_APP_LIST_TEST_FAKE_PROFILE_STORE_H_
diff --git a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
deleted file mode 100644
index 8a2b265d..0000000
--- a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2013 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/ash/app_list/app_list_service_ash.h"
-
-#include <string>
-#include <utility>
-
-#include "ash/app_list/app_list_controller_impl.h"
-#include "ash/app_list/model/search/search_model.h"
-#include "ash/shell.h"
-#include "base/files/file_path.h"
-#include "base/memory/singleton.h"
-#include "build/build_config.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_list/app_list_client_impl.h"
-#include "chrome/browser/ui/app_list/app_list_view_delegate.h"
-#include "chrome/browser/ui/ash/ash_util.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
-#include "ui/app_list/app_list_features.h"
-#include "ui/app_list/app_list_switches.h"
-#include "ui/display/display.h"
-#include "ui/display/screen.h"
-
-// static
-AppListServiceAsh* AppListServiceAsh::GetInstance() {
-  return base::Singleton<AppListServiceAsh,
-                         base::LeakySingletonTraits<AppListServiceAsh>>::get();
-}
-
-AppListServiceAsh::AppListServiceAsh() = default;
-AppListServiceAsh::~AppListServiceAsh() = default;
-
-void AppListServiceAsh::SetAppListControllerAndClient(
-    ash::mojom::AppListController* app_list_controller,
-    AppListClientImpl* app_list_client) {
-  app_list_controller_ = app_list_controller;
-  controller_delegate_.SetAppListController(app_list_controller);
-  app_list_client_ = app_list_client;
-}
-
-ash::mojom::AppListController* AppListServiceAsh::GetAppListController() {
-  return app_list_controller_;
-}
-
-app_list::SearchModel* AppListServiceAsh::GetSearchModelFromAsh() {
-  DCHECK(!ash_util::IsRunningInMash());
-  return ash::Shell::HasInstance()
-             ? ash::Shell::Get()->app_list_controller()->search_model()
-             : nullptr;
-}
-
-void AppListServiceAsh::OnProfileWillBeRemoved(
-    const base::FilePath& profile_path) {}
-
-void AppListServiceAsh::ShowAndSwitchToState(ash::AppListState state) {
-  if (!app_list_controller_)
-    return;
-  app_list_controller_->ShowAppListAndSwitchToState(state);
-}
-
-base::FilePath AppListServiceAsh::GetProfilePath(
-    const base::FilePath& user_data_dir) {
-  return ChromeLauncherController::instance()->profile()->GetPath();
-}
-
-void AppListServiceAsh::ShowForProfile(Profile* /*default_profile*/) {
-  // This may not work correctly if the profile passed in is different from the
-  // one the ash Shell is currently using.
-  if (!app_list_controller_)
-    return;
-  app_list_controller_->ShowAppList();
-}
-
-void AppListServiceAsh::ShowForAppInstall(Profile* profile,
-                                          const std::string& extension_id,
-                                          bool start_discovery_tracking) {
-  if (app_list::features::IsFullscreenAppListEnabled())
-    return;
-  ShowAndSwitchToState(ash::AppListState::kStateApps);
-  AppListServiceImpl::ShowForAppInstall(profile, extension_id,
-                                        start_discovery_tracking);
-}
-
-bool AppListServiceAsh::GetTargetVisibility() const {
-  return app_list_target_visible_;
-}
-
-bool AppListServiceAsh::IsAppListVisible() const {
-  return app_list_visible_;
-}
-
-void AppListServiceAsh::FlushForTesting() {
-  app_list_client_->FlushMojoForTesting();
-}
-
-void AppListServiceAsh::DismissAppList() {
-  if (!app_list_controller_)
-    return;
-  app_list_controller_->DismissAppList();
-}
-
-void AppListServiceAsh::EnableAppList(Profile* initial_profile,
-                                      AppListEnableSource enable_source) {}
-
-Profile* AppListServiceAsh::GetCurrentAppListProfile() {
-  return ChromeLauncherController::instance()->profile();
-}
-
-AppListControllerDelegate* AppListServiceAsh::GetControllerDelegate() {
-  return &controller_delegate_;
-}
-
-void AppListServiceAsh::CreateForProfile(Profile* default_profile) {}
-
-void AppListServiceAsh::DestroyAppList() {
-  // On Ash, the app list is torn down whenever it is dismissed, so just ensure
-  // that it is dismissed.
-  DismissAppList();
-}
-
-// static
-AppListService* AppListService::Get() {
-  return AppListServiceAsh::GetInstance();
-}
diff --git a/chrome/browser/ui/ash/app_list/app_list_service_ash.h b/chrome/browser/ui/ash/app_list/app_list_service_ash.h
deleted file mode 100644
index 1ad1508..0000000
--- a/chrome/browser/ui/ash/app_list/app_list_service_ash.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2014 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_ASH_APP_LIST_APP_LIST_SERVICE_ASH_H_
-#define CHROME_BROWSER_UI_ASH_APP_LIST_APP_LIST_SERVICE_ASH_H_
-
-#include <memory>
-#include <string>
-
-#include "ash/public/interfaces/app_list.mojom.h"
-#include "base/macros.h"
-#include "chrome/browser/ui/app_list/app_list_service_impl.h"
-#include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h"
-
-class AppListClientImpl;
-
-namespace app_list {
-class SearchModel;
-}  // namespace app_list
-
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
-class AppListControllerDelegateAsh;
-
-// AppListServiceAsh wraps functionality in ChromeLauncherController and the Ash
-// Shell for showing and hiding the app list on the Ash desktop.
-class AppListServiceAsh : public AppListServiceImpl {
- public:
-  static AppListServiceAsh* GetInstance();
-
-  // Returns a pointer to control the app list views in ash.
-  ash::mojom::AppListController* GetAppListController();
-
-  // TODO(hejq): Search model migration is not done yet. Chrome still accesses
-  //             it directly in non-mus+ash mode.
-  app_list::SearchModel* GetSearchModelFromAsh();
-
-  // ProfileAttributesStorage::Observer overrides:
-  // On ChromeOS this should never happen. On other platforms, there is always a
-  // Non-ash AppListService that is responsible for handling this.
-  // TODO(calamity): Ash shouldn't observe the ProfileAttributesStorage at all.
-  void OnProfileWillBeRemoved(const base::FilePath& profile_path) override;
-
-  // AppListService overrides:
-  bool IsAppListVisible() const override;
-  bool GetTargetVisibility() const override;
-  void FlushForTesting() override;
-
-  // Updates app list (target) visibility from AppListClientImpl.
-  void set_app_list_visible(bool visible) { app_list_visible_ = visible; }
-  void set_app_list_target_visible(bool visible) {
-    app_list_target_visible_ = visible;
-  }
-
-  // Sets the pointers to the app list controller in Ash, and the app list
-  // client in Chrome.
-  void SetAppListControllerAndClient(
-      ash::mojom::AppListController* app_list_controller,
-      AppListClientImpl* app_list_client);
-
- private:
-  friend struct base::DefaultSingletonTraits<AppListServiceAsh>;
-  friend class AppListServiceAshTestApi;
-
-  AppListServiceAsh();
-  ~AppListServiceAsh() override;
-
-  // Shows the app list if it isn't already showing and Switches to |state|,
-  // unless it is |INVALID_STATE| (in which case, opens on the default state).
-  void ShowAndSwitchToState(ash::AppListState state);
-
-  // AppListService overrides:
-  base::FilePath GetProfilePath(const base::FilePath& user_data_dir) override;
-  void ShowForProfile(Profile* default_profile) override;
-  void ShowForAppInstall(Profile* profile,
-                         const std::string& extension_id,
-                         bool start_discovery_tracking) override;
-  void DismissAppList() override;
-  void EnableAppList(Profile* initial_profile,
-                     AppListEnableSource enable_source) override;
-  Profile* GetCurrentAppListProfile() override;
-  AppListControllerDelegate* GetControllerDelegate() override;
-
-  // ApplistServiceImpl overrides:
-  void CreateForProfile(Profile* default_profile) override;
-  void DestroyAppList() override;
-
-  AppListControllerDelegateAsh controller_delegate_;
-  ash::mojom::AppListController* app_list_controller_ = nullptr;
-  AppListClientImpl* app_list_client_ = nullptr;
-
-  bool app_list_visible_ = false;
-  bool app_list_target_visible_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(AppListServiceAsh);
-};
-
-#endif  // CHROME_BROWSER_UI_ASH_APP_LIST_APP_LIST_SERVICE_ASH_H_
diff --git a/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc
index 89915c3b..bd70e63 100644
--- a/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/arc_launcher_context_menu.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/grit/generated_resources.h"
+#include "ui/base/ui_base_features.h"
 
 ArcLauncherContextMenu::ArcLauncherContextMenu(
     ChromeLauncherController* controller,
@@ -40,7 +41,8 @@
   if (!app_is_open) {
     DCHECK(app_info->launchable);
     AddItemWithStringId(MENU_OPEN_NEW, IDS_APP_CONTEXT_MENU_ACTIVATE_ARC);
-    AddSeparator(ui::NORMAL_SEPARATOR);
+    if (!features::IsTouchableAppContextMenuEnabled())
+      AddSeparator(ui::NORMAL_SEPARATOR);
   }
 
   if (!app_id.has_shelf_group_id() && app_info->launchable)
@@ -48,5 +50,6 @@
 
   if (app_is_open)
     AddItemWithStringId(MENU_CLOSE, IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
-  AddSeparator(ui::NORMAL_SEPARATOR);
+  if (!features::IsTouchableAppContextMenuEnabled())
+    AddSeparator(ui::NORMAL_SEPARATOR);
 }
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index d2611dc..38e1254 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -4,6 +4,10 @@
 
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 
+#include <algorithm>
+#include <set>
+#include <utility>
+
 #include "ash/multi_profile_uma.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/remote_shelf_item_delegate.h"
@@ -26,11 +30,11 @@
 #include "chrome/browser/prefs/pref_service_syncable_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/app_list/app_list_service_impl.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/app_list/app_list_service_ash.h"
-#include "chrome/browser/ui/ash/app_sync_ui_state.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
 #include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h"
@@ -514,7 +518,7 @@
   }
 
   if (window->IsActive() && allow_minimize &&
-      !AppListServiceAsh::GetInstance()->GetTargetVisibility()) {
+      !AppListServiceImpl::GetInstance()->GetTargetVisibility()) {
     window->Minimize();
     return ash::SHELF_ACTION_WINDOW_MINIMIZED;
   }
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index 0f2a592..3b1d5e79 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -18,7 +18,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/app_icon_loader_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
-#include "chrome/browser/ui/ash/app_sync_ui_state_observer.h"
+#include "chrome/browser/ui/app_list/app_sync_ui_state_observer.h"
 #include "chrome/browser/ui/ash/launcher/launcher_app_updater.h"
 #include "chrome/browser/ui/ash/launcher/settings_window_observer.h"
 #include "components/prefs/pref_change_registrar.h"
diff --git a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
index faf951b..35ad49d 100644
--- a/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/extension_launcher_context_menu.cc
@@ -21,6 +21,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "content/public/common/context_menu_params.h"
 #include "extensions/browser/extension_prefs.h"
+#include "ui/base/ui_base_features.h"
 
 namespace {
 
@@ -53,7 +54,8 @@
                           ? IDS_APP_LIST_CONTEXT_MENU_NEW_TAB
                           : IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW;
       AddItemWithStringId(MENU_OPEN_NEW, string_id);
-      AddSeparator(ui::NORMAL_SEPARATOR);
+      if (!features::IsTouchableAppContextMenuEnabled())
+        AddSeparator(ui::NORMAL_SEPARATOR);
     }
 
     AddPinMenu();
@@ -63,7 +65,8 @@
 
     if (!controller()->IsPlatformApp(item().id) &&
         item().type == ash::TYPE_PINNED_APP) {
-      AddSeparator(ui::NORMAL_SEPARATOR);
+      if (!features::IsTouchableAppContextMenuEnabled())
+        AddSeparator(ui::NORMAL_SEPARATOR);
       if (extensions::util::IsNewBookmarkAppsEnabled()) {
         // With bookmark apps enabled, hosted apps launch in a window by
         // default. This menu item is re-interpreted as a single, toggle-able
@@ -98,14 +101,16 @@
   } else if (controller()->IsOpen(item().id)) {
     AddItemWithStringId(MENU_CLOSE, IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
   }
-  AddSeparator(ui::NORMAL_SEPARATOR);
+  if (!features::IsTouchableAppContextMenuEnabled())
+    AddSeparator(ui::NORMAL_SEPARATOR);
   if (item().type == ash::TYPE_PINNED_APP || item().type == ash::TYPE_APP) {
     const extensions::MenuItem::ExtensionKey app_key(item().id.app_id);
     if (!app_key.empty()) {
       int index = 0;
       extension_items_->AppendExtensionItems(app_key, base::string16(), &index,
                                              false);  // is_action_menu
-      AddSeparator(ui::NORMAL_SEPARATOR);
+      if (!features::IsTouchableAppContextMenuEnabled())
+        AddSeparator(ui::NORMAL_SEPARATOR);
     }
   }
 }
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 68b755d..6647784 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -1864,7 +1864,7 @@
   EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
 }
 
-// Flaky on Chrome OS only. TODO(https://crbug.com/823396) fix it.
+// Flaky on Chrome OS only. TODO(https://crbug.com/823043) fix it.
 #if defined(OS_CHROMEOS)
 #define MAYBE_WindowOpenClose2 DISABLED_WindowOpenClose2
 #else
diff --git a/chrome/browser/ui/cocoa/download/md_download_item_view.mm b/chrome/browser/ui/cocoa/download/md_download_item_view.mm
index 2e257af..9167ebe 100644
--- a/chrome/browser/ui/cocoa/download/md_download_item_view.mm
+++ b/chrome/browser/ui/cocoa/download/md_download_item_view.mm
@@ -79,6 +79,15 @@
 }
 }  // namespace
 
+@interface MDDownloadItemButton : MDHoverButton
+@end
+
+@implementation MDDownloadItemButton
+- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent*)event {
+  return YES;
+}
+@end
+
 @interface MDDownloadItemMenuButton : MDHoverButton
 @end
 
@@ -243,7 +252,7 @@
     const NSRect buttonRect = NSMakeRect(kButtonXInset, kButtonYInset,
                                          NSWidth(bounds) - kButtonXInset * 2,
                                          NSHeight(bounds) - kButtonYInset * 2);
-    base::scoped_nsobject<MDHoverButton> button([[MDHoverButton alloc]
+    base::scoped_nsobject<MDHoverButton> button([[MDDownloadItemButton alloc]
         initWithFrame:[self cr_localizedRect:buttonRect]]);
     button_ = button;
     button_.imagePosition = NSImageOnly;
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 696ec685..077dd20 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -100,10 +100,6 @@
 #include "chrome/browser/printing/print_dialog_cloud.h"
 #endif
 
-#if BUILDFLAG(ENABLE_APP_LIST)
-#include "chrome/browser/ui/app_list/app_list_service.h"
-#endif
-
 using content::BrowserThread;
 using content::ChildProcessSecurityPolicy;
 
@@ -276,6 +272,7 @@
 
 void ShowUserManagerOnStartup(const base::CommandLine& command_line) {
 #if !defined(OS_CHROMEOS)
+  // TODO(crbug/821659): Clean up the desktop UserManager webui.
   profiles::UserManagerAction action =
       command_line.HasSwitch(switches::kShowAppList) ?
           profiles::USER_MANAGER_SELECT_PROFILE_APP_LAUNCHER :
@@ -960,7 +957,7 @@
   if (command_line.HasSwitch(switches::kNotificationLaunchId)) {
     std::string profile_id =
         NotificationPlatformBridgeWin::GetProfileIdFromLaunchId(
-            command_line.GetSwitchValueASCII(switches::kNotificationLaunchId));
+            command_line.GetSwitchValueNative(switches::kNotificationLaunchId));
     if (!profile_id.empty()) {
       return user_data_dir.Append(
           base::FilePath(base::UTF8ToUTF16(profile_id)));
@@ -968,13 +965,6 @@
   }
 #endif  // defined(OS_WIN)
 
-#if BUILDFLAG(ENABLE_APP_LIST)
-  // If we are showing the app list then chrome isn't shown so load the app
-  // list's profile rather than chrome's.
-  if (command_line.HasSwitch(switches::kShowAppList))
-    return AppListService::Get()->GetProfilePath(user_data_dir);
-#endif
-
   return g_browser_process->profile_manager()->GetLastUsedProfileDir(
       user_data_dir);
 }
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
index c146cdf7..fe6b7334 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -679,8 +679,6 @@
     // Check if there are any incompatible applications cached from the last
     // Chrome run.
     has_incompatible_applications =
-        ProblematicProgramsUpdater::
-            IsIncompatibleApplicationsWarningEnabled() &&
         ProblematicProgramsUpdater::HasCachedPrograms();
   }
 #endif
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
index d5176c8..3cb3555 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
 
+#include "build/build_config.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -14,6 +15,9 @@
 // Test is Flaky on Windows see crbug.com/600201.
 #if defined(OS_WIN)
 #define MAYBE_InactiveSeparatorColor DISABLED_InactiveSeparatorColor
+#elif defined(OS_MACOSX)
+// Widget activation doesn't work on Mac: https://crbug.com/823543
+#define MAYBE_InactiveSeparatorColor DISABLED_InactiveSeparatorColor
 #else
 #define MAYBE_InactiveSeparatorColor InactiveSeparatorColor
 #endif
diff --git a/chrome/browser/ui/views/hover_button.cc b/chrome/browser/ui/views/hover_button.cc
index 60b8447..3ded50b3 100644
--- a/chrome/browser/ui/views/hover_button.cc
+++ b/chrome/browser/ui/views/hover_button.cc
@@ -221,8 +221,7 @@
 }
 
 void HoverButton::SetSubtitleElideBehavior(gfx::ElideBehavior elide_behavior) {
-  DCHECK(subtitle_);
-  if (!subtitle_->text().empty())
+  if (subtitle_ && !subtitle_->text().empty())
     subtitle_->SetElideBehavior(elide_behavior);
 }
 
@@ -360,7 +359,8 @@
 }
 
 void HoverButton::SetSubtitleColor(SkColor color) {
-  subtitle_->SetEnabledColor(color);
+  if (subtitle_)
+    subtitle_->SetEnabledColor(color);
 }
 
 void HoverButton::OnMenuButtonClicked(MenuButton* source,
diff --git a/chrome/browser/ui/views/hover_button_unittest.cc b/chrome/browser/ui/views/hover_button_unittest.cc
index 5851b003..fb8a108 100644
--- a/chrome/browser/ui/views/hover_button_unittest.cc
+++ b/chrome/browser/ui/views/hover_button_unittest.cc
@@ -153,3 +153,12 @@
     EXPECT_EQ(expected, base::UTF8ToUTF16(accessible_name));
   }
 }
+
+// Tests that setting the style and the subtitle elide behavior don't lead to a
+// crash for a HoverButton with an empty subtitle.
+TEST_F(HoverButtonTest, SetStyleAndSubtitleElideBehavior) {
+  HoverButton button(nullptr, CreateIcon(), base::ASCIIToUTF16("Test title"),
+                     base::string16());
+  button.SetStyle(HoverButton::STYLE_PROMINENT);
+  button.SetSubtitleElideBehavior(gfx::ELIDE_EMAIL);
+}
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view_browsertest.cc b/chrome/browser/ui/views/location_bar/location_icon_view_browsertest.cc
index 47e5db45..b04a7e4 100644
--- a/chrome/browser/ui/views/location_bar/location_icon_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/location_icon_view_browsertest.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/views/scoped_macviews_browser_mode.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
 #include "ui/views/animation/test/ink_drop_host_view_test_api.h"
 
@@ -30,6 +31,7 @@
   LocationIconView* icon_view() const { return icon_view_.get(); }
 
  private:
+  test::ScopedMacViewsBrowserMode views_mode_{true};
   LocationBarView* location_bar_;
 
   std::unique_ptr<LocationIconView> icon_view_;
diff --git a/chrome/browser/ui/views/media_router/web_contents_display_observer_view_unittest.cc b/chrome/browser/ui/views/media_router/web_contents_display_observer_view_unittest.cc
index a5daf4d..dd900e8 100644
--- a/chrome/browser/ui/views/media_router/web_contents_display_observer_view_unittest.cc
+++ b/chrome/browser/ui/views/media_router/web_contents_display_observer_view_unittest.cc
@@ -7,6 +7,7 @@
 
 #include "chrome/browser/ui/views/media_router/web_contents_display_observer_view.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/browser/web_contents.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display.h"
@@ -48,8 +49,9 @@
 
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
+    web_contents_.reset(CreateTestWebContents());
     display_observer_ = std::make_unique<TestWebContentsDisplayObserverView>(
-        CreateTestWebContents(),
+        web_contents_.get(),
         base::BindRepeating(&MockCallback::OnDisplayChanged,
                             base::Unretained(&callback_)),
         display1_);
@@ -57,10 +59,12 @@
 
   void TearDown() override {
     display_observer_.reset();
+    web_contents_.reset();
     ChromeRenderViewHostTestHarness::TearDown();
   }
 
  protected:
+  std::unique_ptr<content::WebContents> web_contents_;
   std::unique_ptr<TestWebContentsDisplayObserverView> display_observer_;
   MockCallback callback_;
   const display::Display display1_;
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index 6067862..0ee06544 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -45,9 +45,8 @@
 };
 
 OverlayWindowViews::OverlayWindowViews() {
-  // TODO(apacible): Change window type to TYPE_WINDOW_FRAMELESS. It is
-  // temporarily TYPE_WINDOW for resizing purposes.
-  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
+  views::Widget::InitParams params(
+      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.bounds = CalculateAndUpdateBounds();
   params.keep_on_top = true;
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc b/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc
index c67312c5..78dc260 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc
@@ -397,8 +397,14 @@
   EXPECT_TRUE(VerifyVisibleCount(1u));
 }
 
+class BrowserActionsContainerOverflowViewsTest
+    : public BrowserActionsContainerOverflowTest {
+ private:
+  test::ScopedMacViewsBrowserMode views_mode_{true};
+};
+
 // Test drag and drop between the overflow container and the main container.
-IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest,
+IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowViewsTest,
                        TestOverflowDragging) {
   LoadExtensions();
 
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index b64af50..944bd9d7b 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -134,6 +134,7 @@
 #include "base/sys_info.h"
 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.h"
 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_factory.h"
+#include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h"
 #include "chrome/browser/ui/webui/chromeos/bluetooth_pairing_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.h"
 #include "chrome/browser/ui/webui/chromeos/cryptohome_ui.h"
@@ -466,6 +467,8 @@
   if (url.host_piece() == chrome::kChromeUISysInternalsHost &&
       SysInternalsUI::IsEnabled())
     return &NewWebUI<SysInternalsUI>;
+  if (url.host_piece() == chrome::kChromeUIAssistantOptInHost)
+    return &NewWebUI<chromeos::AssistantOptInUI>;
 #if !defined(OFFICIAL_BUILD)
   if (!base::SysInfo::IsRunningOnChromeOS()) {
     if (url.host_piece() == chrome::kChromeUIDeviceEmulatorHost)
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_screen_exit_code.h b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_screen_exit_code.h
new file mode 100644
index 0000000..d406d9e
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_screen_exit_code.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_ASSISTANT_OPTIN_ASSISTANT_OPTIN_SCREEN_EXIT_CODE_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_ASSISTANT_OPTIN_ASSISTANT_OPTIN_SCREEN_EXIT_CODE_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+namespace chromeos {
+
+enum class AssistantOptInScreenExitCode {
+  VALUE_PROP_SKIPPED = 0,
+  VALUE_PROP_ACCEPTED = 1,
+  EXIT_CODES_COUNT
+};
+
+using OnAssistantOptInScreenExitCallback =
+    base::OnceCallback<void(AssistantOptInScreenExitCode exit_code)>;
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_ASSISTANT_OPTIN_ASSISTANT_OPTIN_SCREEN_EXIT_CODE_H_
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
new file mode 100644
index 0000000..fe56a24e
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
@@ -0,0 +1,124 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/webui/chromeos/assistant_optin/value_prop_screen_handler.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/browser_resources.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+namespace chromeos {
+
+namespace {
+
+bool is_active = false;
+
+constexpr int kAssistantOptInDialogWidth = 576;
+constexpr int kAssistantOptInDialogHeight = 480;
+
+}  // namespace
+
+AssistantOptInUI::AssistantOptInUI(content::WebUI* web_ui)
+    : ui::WebDialogUI(web_ui), weak_factory_(this) {
+  // Set up the chrome://assistant-optin source.
+  content::WebUIDataSource* source =
+      content::WebUIDataSource::Create(chrome::kChromeUIAssistantOptInHost);
+
+  AddScreenHandler(std::make_unique<ValuePropScreenHandler>(
+      base::BindOnce(&AssistantOptInUI::OnExit, weak_factory_.GetWeakPtr())));
+
+  base::DictionaryValue localized_strings;
+  for (auto* handler : screen_handlers_)
+    handler->GetLocalizedStrings(&localized_strings);
+  source->AddLocalizedStrings(localized_strings);
+
+  source->SetJsonPath("strings.js");
+
+  source->AddResourcePath("assistant_optin.js", IDR_ASSISTANT_OPTIN_JS);
+  source->SetDefaultResource(IDR_ASSISTANT_OPTIN_HTML);
+
+  content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
+}
+
+AssistantOptInUI::~AssistantOptInUI() {}
+
+void AssistantOptInUI::AddScreenHandler(
+    std::unique_ptr<BaseWebUIHandler> handler) {
+  screen_handlers_.push_back(handler.get());
+  web_ui()->AddMessageHandler(std::move(handler));
+}
+
+void AssistantOptInUI::OnExit(AssistantOptInScreenExitCode exit_code) {
+  switch (exit_code) {
+    case AssistantOptInScreenExitCode::VALUE_PROP_SKIPPED:
+      // TODO(updowndota) Update the action to use the new Assistant service.
+      GetVoiceInteractionHomeService()->OnAssistantCanceled();
+      CloseDialog(nullptr);
+      break;
+    case AssistantOptInScreenExitCode::VALUE_PROP_ACCEPTED:
+      // TODO(updowndota) Update the action to use the new Assistant service.
+      GetVoiceInteractionHomeService()->OnAssistantAppRequested();
+      CloseDialog(nullptr);
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
+arc::ArcVoiceInteractionArcHomeService*
+AssistantOptInUI::GetVoiceInteractionHomeService() {
+  Profile* const profile = Profile::FromWebUI(web_ui());
+  arc::ArcVoiceInteractionArcHomeService* const home_service =
+      arc::ArcVoiceInteractionArcHomeService::GetForBrowserContext(profile);
+  DCHECK(home_service);
+  return home_service;
+}
+
+// AssistantOptInDialog
+
+// static
+void AssistantOptInDialog::Show() {
+  DCHECK(!is_active);
+  AssistantOptInDialog* dialog = new AssistantOptInDialog();
+  dialog->ShowSystemDialog();
+}
+
+// static
+bool AssistantOptInDialog::IsActive() {
+  return is_active;
+}
+
+AssistantOptInDialog::AssistantOptInDialog()
+    : SystemWebDialogDelegate(GURL(chrome::kChromeUIAssistantOptInURL),
+                              base::string16()) {
+  DCHECK(!is_active);
+  is_active = true;
+}
+
+AssistantOptInDialog::~AssistantOptInDialog() {
+  is_active = false;
+}
+
+void AssistantOptInDialog::GetDialogSize(gfx::Size* size) const {
+  size->SetSize(kAssistantOptInDialogWidth, kAssistantOptInDialogHeight);
+}
+
+std::string AssistantOptInDialog::GetDialogArgs() const {
+  return std::string();
+}
+
+bool AssistantOptInDialog::ShouldShowDialogTitle() const {
+  return false;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h
new file mode 100644
index 0000000..7fff533
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h
@@ -0,0 +1,70 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_ASSISTANT_OPTIN_ASSISTANT_OPTIN_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_ASSISTANT_OPTIN_ASSISTANT_OPTIN_UI_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_screen_exit_code.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
+#include "chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+namespace arc {
+class ArcVoiceInteractionArcHomeService;
+}
+
+namespace chromeos {
+
+// Controller for chrome://assistant-optin/ page.
+class AssistantOptInUI : public ui::WebDialogUI {
+ public:
+  explicit AssistantOptInUI(content::WebUI* web_ui);
+  ~AssistantOptInUI() override;
+
+ private:
+  // Add message handler for optin screens.
+  void AddScreenHandler(std::unique_ptr<BaseWebUIHandler> handler);
+
+  // Called by a screen when user's done with it.
+  void OnExit(AssistantOptInScreenExitCode exit_code);
+
+  // Get ArcHomeService for user action handling.
+  arc::ArcVoiceInteractionArcHomeService* GetVoiceInteractionHomeService();
+
+  std::vector<BaseWebUIHandler*> screen_handlers_;
+  base::WeakPtrFactory<AssistantOptInUI> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantOptInUI);
+};
+
+// Dialog delegate for the assistant optin page.
+class AssistantOptInDialog : public SystemWebDialogDelegate {
+ public:
+  // Shows the assistant optin dialog.
+  static void Show();
+
+  // Returns whether the dialog is being shown.
+  static bool IsActive();
+
+ protected:
+  AssistantOptInDialog();
+  ~AssistantOptInDialog() override;
+
+  // ui::WebDialogDelegate
+  void GetDialogSize(gfx::Size* size) const override;
+  std::string GetDialogArgs() const override;
+  bool ShouldShowDialogTitle() const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AssistantOptInDialog);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_ASSISTANT_OPTIN_ASSISTANT_OPTIN_UI_H_
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/value_prop_screen_handler.cc b/chrome/browser/ui/webui/chromeos/assistant_optin/value_prop_screen_handler.cc
new file mode 100644
index 0000000..e601e28f
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/value_prop_screen_handler.cc
@@ -0,0 +1,66 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/chromeos/assistant_optin/value_prop_screen_handler.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+
+namespace {
+
+const char kJsScreenPath[] = "AssistantValuePropScreen";
+
+constexpr const char kUserActionSkipPressed[] = "skip-pressed";
+constexpr const char kUserActionNextPressed[] = "next-pressed";
+
+}  // namespace
+
+namespace chromeos {
+
+ValuePropScreenHandler::ValuePropScreenHandler(
+    OnAssistantOptInScreenExitCallback callback)
+    : BaseWebUIHandler(), exit_callback_(std::move(callback)) {
+  set_call_js_prefix(kJsScreenPath);
+}
+
+ValuePropScreenHandler::~ValuePropScreenHandler() = default;
+
+void ValuePropScreenHandler::DeclareLocalizedValues(
+    ::login::LocalizedValuesBuilder* builder) {
+  builder->Add("locale", g_browser_process->GetApplicationLocale());
+  builder->Add("voiceInteractionValuePropLoading",
+               IDS_VOICE_INTERACTION_VALUE_PROP_LOADING);
+  builder->Add("voiceInteractionValuePropLoadErrorTitle",
+               IDS_VOICE_INTERACTION_VALUE_PROP_LOAD_ERROR_TITLE);
+  builder->Add("voiceInteractionValuePropLoadErrorMessage",
+               IDS_VOICE_INTERACTION_VALUE_PROP_LOAD_ERROR_MESSAGE);
+  builder->Add("voiceInteractionValuePropSkipButton",
+               IDS_VOICE_INTERACTION_VALUE_PROP_SKIP_BUTTON);
+  builder->Add("voiceInteractionValuePropRetryButton",
+               IDS_VOICE_INTERACTION_VALUE_PROP_RETRY_BUTTON);
+  builder->Add("voiceInteractionValuePropNextButton",
+               IDS_VOICE_INTERACTION_VALUE_PROP_NEXT_BUTTION);
+
+  builder->Add("back", IDS_EULA_BACK_BUTTON);
+  builder->Add("next", IDS_EULA_NEXT_BUTTON);
+}
+
+void ValuePropScreenHandler::RegisterMessages() {
+  AddPrefixedCallback("userActed", &ValuePropScreenHandler::HandleUserAction);
+}
+
+void ValuePropScreenHandler::Initialize() {}
+
+void ValuePropScreenHandler::HandleUserAction(const std::string& action) {
+  DCHECK(exit_callback_);
+  if (action == kUserActionSkipPressed)
+    std::move(exit_callback_)
+        .Run(AssistantOptInScreenExitCode::VALUE_PROP_SKIPPED);
+  else if (action == kUserActionNextPressed)
+    std::move(exit_callback_)
+        .Run(AssistantOptInScreenExitCode::VALUE_PROP_ACCEPTED);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/value_prop_screen_handler.h b/chrome/browser/ui/webui/chromeos/assistant_optin/value_prop_screen_handler.h
new file mode 100644
index 0000000..ec8e0324
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/value_prop_screen_handler.h
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_ASSISTANT_OPTIN_VALUE_PROP_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_ASSISTANT_OPTIN_VALUE_PROP_SCREEN_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_screen_exit_code.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
+
+namespace chromeos {
+
+class ValuePropScreenHandler : public BaseWebUIHandler {
+ public:
+  explicit ValuePropScreenHandler(OnAssistantOptInScreenExitCallback callback);
+  ~ValuePropScreenHandler() override;
+
+  // BaseWebUIHandler:
+  void DeclareLocalizedValues(
+      ::login::LocalizedValuesBuilder* builder) override;
+  void RegisterMessages() override;
+  void Initialize() override;
+
+ private:
+  void HandleUserAction(const std::string& action);
+
+  OnAssistantOptInScreenExitCallback exit_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ValuePropScreenHandler);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_ASSISTANT_OPTIN_VALUE_PROP_SCREEN_HANDLER_H_
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index 60482c6..aab42a4 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -35,6 +35,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/component_extension_resources.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/print_preview_resources.h"
 #include "chrome/grit/print_preview_resources_map.h"
@@ -420,13 +421,92 @@
 }
 
 void SetupPrintPreviewPlugin(content::WebUIDataSource* source) {
-  source->AddResourcePath("pdf_preview.html",
-                          IDR_PRINT_PREVIEW_PDF_PREVIEW_HTML);
+  source->AddResourcePath("pdf/index.html", IDR_PDF_INDEX_HTML);
+  source->AddResourcePath("pdf/index.css", IDR_PDF_INDEX_CSS);
+  source->AddResourcePath("pdf/main.js", IDR_PDF_MAIN_JS);
+  source->AddResourcePath("pdf/pdf.js", IDR_PDF_PDF_JS);
+  source->AddResourcePath("pdf/toolbar_manager.js", IDR_PDF_UI_MANAGER_JS);
+  source->AddResourcePath("pdf/pdf_fitting_type.js",
+                          IDR_PDF_PDF_FITTING_TYPE_JS);
+  source->AddResourcePath("pdf/viewport.js", IDR_PDF_VIEWPORT_JS);
+  source->AddResourcePath("pdf/open_pdf_params_parser.js",
+                          IDR_PDF_OPEN_PDF_PARAMS_PARSER_JS);
+  source->AddResourcePath("pdf/navigator.js", IDR_PDF_NAVIGATOR_JS);
+  source->AddResourcePath("pdf/viewport_scroller.js",
+                          IDR_PDF_VIEWPORT_SCROLLER_JS);
+  source->AddResourcePath("pdf/pdf_scripting_api.js",
+                          IDR_PDF_PDF_SCRIPTING_API_JS);
+  source->AddResourcePath("pdf/zoom_manager.js", IDR_PDF_ZOOM_MANAGER_JS);
+  source->AddResourcePath("pdf/gesture_detector.js",
+                          IDR_PDF_GESTURE_DETECTOR_JS);
+  source->AddResourcePath("pdf/browser_api.js", IDR_PDF_BROWSER_API_JS);
+  source->AddResourcePath("pdf/metrics.js", IDR_PDF_METRICS_JS);
+  source->AddResourcePath("pdf/coords_transformer.js",
+                          IDR_PDF_COORDS_TRANSFORMER_JS);
+
+  source->AddResourcePath("pdf/elements/shared-vars.html",
+                          IDR_PDF_SHARED_VARS_HTML);
+  source->AddResourcePath("pdf/elements/icons.html", IDR_PDF_ICONS_HTML);
+  source->AddResourcePath("pdf/elements/viewer-bookmark/viewer-bookmark.html",
+                          IDR_PDF_VIEWER_BOOKMARK_HTML);
+  source->AddResourcePath("pdf/elements/viewer-bookmark/viewer-bookmark.js",
+                          IDR_PDF_VIEWER_BOOKMARK_JS);
+  source->AddResourcePath(
+      "pdf/elements/viewer-bookmarks-content/viewer-bookmarks-content.html",
+      IDR_PDF_VIEWER_BOOKMARKS_CONTENT_HTML);
+  source->AddResourcePath(
+      "pdf/elements/viewer-bookmarks-content/viewer-bookmarks-content.js",
+      IDR_PDF_VIEWER_BOOKMARKS_CONTENT_JS);
+  source->AddResourcePath(
+      "pdf/elements/viewer-error-screen/viewer-error-screen.html",
+      IDR_PDF_VIEWER_ERROR_SCREEN_HTML);
+  source->AddResourcePath(
+      "pdf/elements/viewer-error-screen/viewer-error-screen.js",
+      IDR_PDF_VIEWER_ERROR_SCREEN_JS);
+  source->AddResourcePath(
+      "pdf/elements/viewer-page-indicator/viewer-page-indicator.html",
+      IDR_PDF_VIEWER_PAGE_INDICATOR_HTML);
+  source->AddResourcePath(
+      "pdf/elements/viewer-page-indicator/viewer-page-indicator.js",
+      IDR_PDF_VIEWER_PAGE_INDICATOR_JS);
+  source->AddResourcePath(
+      "pdf/elements/viewer-page-selector/viewer-page-selector.html",
+      IDR_PDF_VIEWER_PAGE_SELECTOR_HTML);
+  source->AddResourcePath(
+      "pdf/elements/viewer-page-selector/viewer-page-selector.js",
+      IDR_PDF_VIEWER_PAGE_SELECTOR_JS);
+  source->AddResourcePath(
+      "pdf/elements/viewer-password-screen/viewer-password-screen.html",
+      IDR_PDF_VIEWER_PASSWORD_SCREEN_HTML);
+  source->AddResourcePath(
+      "pdf/elements/viewer-password-screen/viewer-password-screen.js",
+      IDR_PDF_VIEWER_PASSWORD_SCREEN_JS);
+  source->AddResourcePath(
+      "pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html",
+      IDR_PDF_VIEWER_PDF_TOOLBAR_HTML);
+  source->AddResourcePath(
+      "pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.js",
+      IDR_PDF_VIEWER_PDF_TOOLBAR_JS);
+  source->AddResourcePath(
+      "pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.html",
+      IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_HTML);
+  source->AddResourcePath(
+      "pdf/elements/viewer-toolbar-dropdown/viewer-toolbar-dropdown.js",
+      IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_JS);
+  source->AddResourcePath(
+      "pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.html",
+      IDR_PDF_VIEWER_ZOOM_BUTTON_HTML);
+  source->AddResourcePath(
+      "pdf/elements/viewer-zoom-toolbar/viewer-zoom-button.js",
+      IDR_PDF_VIEWER_ZOOM_BUTTON_JS);
+  source->AddResourcePath(
+      "pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.html",
+      IDR_PDF_VIEWER_ZOOM_SELECTOR_HTML);
+  source->AddResourcePath(
+      "pdf/elements/viewer-zoom-toolbar/viewer-zoom-toolbar.js",
+      IDR_PDF_VIEWER_ZOOM_SELECTOR_JS);
+
   source->SetRequestFilter(base::BindRepeating(&HandleRequestCallback));
-  source->OverrideContentSecurityPolicyScriptSrc(
-      base::StringPrintf("script-src chrome://resources 'self' 'unsafe-eval' "
-                         "chrome-extension://%s;",
-                         extension_misc::kPdfExtensionId));
   source->OverrideContentSecurityPolicyChildSrc("child-src 'self';");
   source->DisableDenyXFrameOptions();
   source->OverrideContentSecurityPolicyObjectSrc("object-src 'self';");
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index c3d1a1d2..2475c9d 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -230,7 +230,6 @@
 
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
   bool has_incompatible_applications =
-      ProblematicProgramsUpdater::IsIncompatibleApplicationsWarningEnabled() &&
       ProblematicProgramsUpdater::HasCachedPrograms();
   html_source->AddBoolean("showIncompatibleApplications",
                           has_incompatible_applications);
diff --git a/chrome/browser/vr/PRESUBMIT.py b/chrome/browser/vr/PRESUBMIT.py
index 09b5bb6..c3aa1c2 100644
--- a/chrome/browser/vr/PRESUBMIT.py
+++ b/chrome/browser/vr/PRESUBMIT.py
@@ -58,7 +58,7 @@
     cl,
     [
       'luci.chromium.try:linux_optional_gpu_tests_rel',
-      'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel',
+      'luci.chromium.try:mac_optional_gpu_tests_rel',
       'master.tryserver.chromium.win:win_optional_gpu_tests_rel',
       'master.tryserver.chromium.android:android_optional_gpu_tests_rel',
       'master.tryserver.chromium.linux:linux_vr',
diff --git a/chrome/browser/vr/elements/content_element.cc b/chrome/browser/vr/elements/content_element.cc
index 9d0b8b8..45066b9 100644
--- a/chrome/browser/vr/elements/content_element.cc
+++ b/chrome/browser/vr/elements/content_element.cc
@@ -48,17 +48,11 @@
 void ContentElement::Render(UiElementRenderer* renderer,
                             const CameraModel& model) const {
   gfx::RectF copy_rect(0, 0, 1, 1);
-  if (texture_id_) {
-    renderer->DrawTexturedQuad(texture_id_, texture_location_,
-                               model.view_proj_matrix * world_space_transform(),
-                               copy_rect, computed_opacity(), size(),
-                               corner_radius());
-  }
-  if (overlay_texture_id_) {
-    renderer->DrawTexturedQuad(overlay_texture_id_, overlay_texture_location_,
-                               model.view_proj_matrix * world_space_transform(),
-                               copy_rect, computed_opacity(), size(),
-                               corner_radius());
+  if (texture_id_ || overlay_texture_id_) {
+    renderer->DrawTexturedQuad(
+        texture_id_, overlay_texture_id_, texture_location_,
+        model.view_proj_matrix * world_space_transform(), copy_rect,
+        computed_opacity(), size(), corner_radius());
   }
 }
 
diff --git a/chrome/browser/vr/elements/textured_element.cc b/chrome/browser/vr/elements/textured_element.cc
index afb7624..dc3e897 100644
--- a/chrome/browser/vr/elements/textured_element.cc
+++ b/chrome/browser/vr/elements/textured_element.cc
@@ -90,7 +90,7 @@
   gfx::RectF copy_rect(0, 0, drawn_size.width() / texture_size_.width(),
                        drawn_size.height() / texture_size_.height());
   renderer->DrawTexturedQuad(
-      texture_handle_, UiElementRenderer::kTextureLocationLocal,
+      texture_handle_, 0, UiElementRenderer::kTextureLocationLocal,
       model.view_proj_matrix * world_space_transform(), copy_rect,
       computed_opacity(), size(), corner_radius());
 }
diff --git a/chrome/browser/vr/renderers/external_textured_quad_renderer.cc b/chrome/browser/vr/renderers/external_textured_quad_renderer.cc
index 5786b09..36d7359 100644
--- a/chrome/browser/vr/renderers/external_textured_quad_renderer.cc
+++ b/chrome/browser/vr/renderers/external_textured_quad_renderer.cc
@@ -15,15 +15,20 @@
 static constexpr char const* kFragmentShader = OEIE_SHADER(
   precision highp float;
   uniform samplerExternalOES u_Texture;
+  uniform samplerExternalOES u_OverlayTexture;
   uniform vec4 u_CopyRect;
   varying vec2 v_TexCoordinate;
   varying vec2 v_CornerPosition;
   uniform mediump float u_Opacity;
+  uniform mediump float u_OverlayOpacity;
   void main() {
     vec2 scaledTex =
         vec2(u_CopyRect[0] + v_TexCoordinate.x * u_CopyRect[2],
         u_CopyRect[1] + v_TexCoordinate.y * u_CopyRect[3]);
     lowp vec4 color = texture2D(u_Texture, scaledTex);
+    lowp vec4 overlay_color = texture2D(u_OverlayTexture, scaledTex);
+    overlay_color = overlay_color * u_OverlayOpacity;
+    color = mix(color, overlay_color, overlay_color.a);
     float mask = 1.0 - step(1.0, length(v_CornerPosition));
     gl_FragColor = color * u_Opacity * mask;
   }
diff --git a/chrome/browser/vr/renderers/textured_quad_renderer.cc b/chrome/browser/vr/renderers/textured_quad_renderer.cc
index 008eaf2..aedd0c9 100644
--- a/chrome/browser/vr/renderers/textured_quad_renderer.cc
+++ b/chrome/browser/vr/renderers/textured_quad_renderer.cc
@@ -140,10 +140,12 @@
 static constexpr char const* kFragmentShader = SHADER(
   precision highp float;
   uniform sampler2D u_Texture;
+  uniform sampler2D u_OverlayTexture;
   uniform vec4 u_CopyRect;
   varying vec2 v_TexCoordinate;
   varying vec2 v_CornerPosition;
   uniform mediump float u_Opacity;
+  uniform mediump float u_OverlayOpacity;
   void main() {
     vec2 scaledTex =
         vec2(u_CopyRect[0] + v_TexCoordinate.x * u_CopyRect[2],
@@ -174,12 +176,17 @@
   copy_rect_handler_ = glGetUniformLocation(program_handle_, "u_CopyRect");
 
   opacity_handle_ = glGetUniformLocation(program_handle_, "u_Opacity");
+  overlay_opacity_handle_ =
+      glGetUniformLocation(program_handle_, "u_OverlayOpacity");
   texture_handle_ = glGetUniformLocation(program_handle_, "u_Texture");
+  overlay_texture_handle_ =
+      glGetUniformLocation(program_handle_, "u_OverlayTexture");
 }
 
 TexturedQuadRenderer::~TexturedQuadRenderer() = default;
 
 void TexturedQuadRenderer::AddQuad(int texture_data_handle,
+                                   int overlay_texture_data_handle,
                                    const gfx::Transform& model_view_proj_matrix,
                                    const gfx::RectF& copy_rect,
                                    float opacity,
@@ -187,6 +194,7 @@
                                    float corner_radius) {
   QuadData quad;
   quad.texture_data_handle = texture_data_handle;
+  quad.overlay_texture_data_handle = overlay_texture_data_handle;
   quad.model_view_proj_matrix = model_view_proj_matrix;
   quad.copy_rect = copy_rect;
   quad.opacity = opacity;
@@ -200,6 +208,7 @@
     return;
 
   int last_texture = 0;
+  int last_overlay_texture = -1;
   float last_opacity = -1.0f;
   gfx::SizeF last_element_size;
   float last_corner_radius = -1.0f;
@@ -212,9 +221,10 @@
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_);
 
   // Link texture data with texture unit.
-  glActiveTexture(GL_TEXTURE0);
   glUniform1i(texture_handle_, 0);
 
+  glUniform1i(overlay_texture_handle_, 1);
+
   // Set up position attribute.
   glVertexAttribPointer(position_handle_, kPositionDataSize, GL_FLOAT, false,
                         kDataStride, VOID_OFFSET(kPositionDataOffset));
@@ -241,10 +251,19 @@
     // Only change texture ID or opacity when they differ between quads.
     if (last_texture != quad.texture_data_handle) {
       last_texture = quad.texture_data_handle;
+      glActiveTexture(GL_TEXTURE0);
       glBindTexture(TextureType(), last_texture);
       SetTexParameters(TextureType());
     }
 
+    if (last_overlay_texture != quad.overlay_texture_data_handle) {
+      last_overlay_texture = quad.overlay_texture_data_handle;
+      glActiveTexture(GL_TEXTURE1);
+      glBindTexture(TextureType(), last_overlay_texture);
+      SetTexParameters(TextureType());
+      glUniform1f(overlay_opacity_handle_, last_overlay_texture ? 1.0f : 0.0f);
+    }
+
     if (last_opacity != quad.opacity) {
       last_opacity = quad.opacity;
       glUniform1f(opacity_handle_, last_opacity);
diff --git a/chrome/browser/vr/renderers/textured_quad_renderer.h b/chrome/browser/vr/renderers/textured_quad_renderer.h
index 0ea4b89..05c299f 100644
--- a/chrome/browser/vr/renderers/textured_quad_renderer.h
+++ b/chrome/browser/vr/renderers/textured_quad_renderer.h
@@ -24,6 +24,7 @@
   // Enqueues a textured quad for rendering. The GL will ultimately be issued
   // in |Flush|.
   void AddQuad(int texture_data_handle,
+               int overlay_texture_data_handle,
                const gfx::Transform& model_view_proj_matrix,
                const gfx::RectF& copy_rect,
                float opacity,
@@ -44,6 +45,7 @@
  private:
   struct QuadData {
     int texture_data_handle;
+    int overlay_texture_data_handle;
     gfx::Transform model_view_proj_matrix;
     gfx::RectF copy_rect;
     float opacity;
@@ -58,7 +60,9 @@
   GLuint model_view_proj_matrix_handle_;
   GLuint corner_offset_handle_;
   GLuint opacity_handle_;
+  GLuint overlay_opacity_handle_;
   GLuint texture_handle_;
+  GLuint overlay_texture_handle_;
   GLuint copy_rect_handler_;
 
   // Attributes
diff --git a/chrome/browser/vr/test/fake_ui_element_renderer.cc b/chrome/browser/vr/test/fake_ui_element_renderer.cc
index 4714e5c3..800fc77 100644
--- a/chrome/browser/vr/test/fake_ui_element_renderer.cc
+++ b/chrome/browser/vr/test/fake_ui_element_renderer.cc
@@ -13,6 +13,7 @@
 
 void FakeUiElementRenderer::DrawTexturedQuad(
     int texture_data_handle,
+    int overlay_texture_data_handle,
     TextureLocation texture_location,
     const gfx::Transform& view_proj_matrix,
     const gfx::RectF& copy_rect,
diff --git a/chrome/browser/vr/test/fake_ui_element_renderer.h b/chrome/browser/vr/test/fake_ui_element_renderer.h
index 55f92a2..0c69424 100644
--- a/chrome/browser/vr/test/fake_ui_element_renderer.h
+++ b/chrome/browser/vr/test/fake_ui_element_renderer.h
@@ -19,6 +19,7 @@
   float opacity() const { return opacity_; }
 
   void DrawTexturedQuad(int texture_data_handle,
+                        int overlay_texture_data_handle,
                         TextureLocation texture_location,
                         const gfx::Transform& view_proj_matrix,
                         const gfx::RectF& copy_rect,
diff --git a/chrome/browser/vr/testapp/test_keyboard_renderer.cc b/chrome/browser/vr/testapp/test_keyboard_renderer.cc
index e88a7479..d9c46994 100644
--- a/chrome/browser/vr/testapp/test_keyboard_renderer.cc
+++ b/chrome/browser/vr/testapp/test_keyboard_renderer.cc
@@ -51,7 +51,7 @@
 void TestKeyboardRenderer::Draw(const CameraModel& model,
                                 const gfx::Transform& world_space_transform) {
   renderer_->DrawTexturedQuad(
-      texture_handle_, UiElementRenderer::kTextureLocationLocal,
+      texture_handle_, 0, UiElementRenderer::kTextureLocationLocal,
       model.view_proj_matrix * world_space_transform, gfx::RectF(0, 0, 1, 1), 1,
       {drawn_size_.width(), drawn_size_.height()}, 0);
 }
diff --git a/chrome/browser/vr/ui.cc b/chrome/browser/vr/ui.cc
index d8ae6dc3..d69c3d40 100644
--- a/chrome/browser/vr/ui.cc
+++ b/chrome/browser/vr/ui.cc
@@ -387,7 +387,12 @@
   os << std::endl;
   scene_->root_element().DumpHierarchy(std::vector<size_t>(), &os,
                                        include_bindings);
-  LOG(ERROR) << os.str();
+
+  std::stringstream ss(os.str());
+  std::string line;
+  while (std::getline(ss, line, '\n')) {
+    LOG(ERROR) << line;
+  }
 #endif
 }
 
diff --git a/chrome/browser/vr/ui_element_renderer.cc b/chrome/browser/vr/ui_element_renderer.cc
index fbb98d7e..9bb787a 100644
--- a/chrome/browser/vr/ui_element_renderer.cc
+++ b/chrome/browser/vr/ui_element_renderer.cc
@@ -53,6 +53,7 @@
 
 void UiElementRenderer::DrawTexturedQuad(
     int texture_data_handle,
+    int overlay_texture_data_handle,
     TextureLocation texture_location,
     const gfx::Transform& model_view_proj_matrix,
     const gfx::RectF& copy_rect,
@@ -69,8 +70,9 @@
                                        ? external_textured_quad_renderer_.get()
                                        : textured_quad_renderer_.get();
   FlushIfNecessary(renderer);
-  renderer->AddQuad(texture_data_handle, model_view_proj_matrix, copy_rect,
-                    opacity, element_size, corner_radius);
+  renderer->AddQuad(texture_data_handle, overlay_texture_data_handle,
+                    model_view_proj_matrix, copy_rect, opacity, element_size,
+                    corner_radius);
 }
 
 void UiElementRenderer::DrawGradientQuad(
diff --git a/chrome/browser/vr/ui_element_renderer.h b/chrome/browser/vr/ui_element_renderer.h
index 28c37560..b141792 100644
--- a/chrome/browser/vr/ui_element_renderer.h
+++ b/chrome/browser/vr/ui_element_renderer.h
@@ -60,6 +60,7 @@
 
   VIRTUAL_FOR_MOCKS void DrawTexturedQuad(
       int texture_data_handle,
+      int overlay_texture_data_handle,
       TextureLocation texture_location,
       const gfx::Transform& model_view_proj_matrix,
       const gfx::RectF& copy_rect,
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 1eb24b9..5682b22 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -314,12 +314,6 @@
 const base::Feature kImprovedRecoveryComponent{
     "ImprovedRecoveryComponent", base::FEATURE_DISABLED_BY_DEFAULT};
 
-#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
-// A feature that controls whether Chrome warns about incompatible applications.
-const base::Feature kIncompatibleApplicationsWarning{
-    "IncompatibleApplicationsWarning", base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
 #if !defined(OS_ANDROID)
 // Enables Casting a Presentation API-enabled website to a secondary display.
 const base::Feature kLocalScreenCasting{"LocalScreenCasting",
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index f86990b..b64e64dc 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -172,10 +172,6 @@
 
 extern const base::Feature kImprovedRecoveryComponent;
 
-#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
-extern const base::Feature kIncompatibleApplicationsWarning;
-#endif
-
 #if !defined(OS_ANDROID)
 extern const base::Feature kLocalScreenCasting;
 #endif
diff --git a/chrome/common/extensions/api/quick_unlock_private.idl b/chrome/common/extensions/api/quick_unlock_private.idl
index 6ab2cab..dd0394a 100644
--- a/chrome/common/extensions/api/quick_unlock_private.idl
+++ b/chrome/common/extensions/api/quick_unlock_private.idl
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Use the <code>chrome.quickUnlockPrivate</code> API to change tthe active quick
-// unlock modes and to change their respective credentials.
-//
-// Quick unlock only supports unlocking an account that has already been signed
-// in.
-//
-// The quick unlock authentication facilities are not available through this
-// API; they are built directly into the lock screen.
+// Use the <code>chrome.quickUnlockPrivate</code> API to change whether the
+// lock screen is enabled and which modes are allowed (active) for unlocking a
+// Chrome OS device from the lock screen. The API is also used to set quick
+// unlock credentials.
+// Note: The API is named 'quickUnlock' for historical reasons but it should be
+// used for all lock screen settings.
+// Note: This API can not be used to actually unlock the device.
 
 [platforms=("chromeos"),
  implemented_in="chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h"]
@@ -69,6 +68,16 @@
     static void getAuthToken(DOMString accountPassword,
                              TokenResultCallback onComplete);
 
+    // Sets the lock screen enabled state. NOTE: The lock enabled state is
+    // reflected in the settings.enable_screen_lock pref, which can be read
+    // but not written using the settings_private API (which also provides
+    // policy information). This API must be used to change the pref.
+    // |token|: The token returned by $(ref:getAuthToken).
+    // |enabled|: Whether to enable the lock screen.
+    static void setLockScreenEnabled(DOMString token,
+                                     boolean enabled,
+                                     optional VoidResultCallback onComplete);
+
     // Returns the set of quick unlock modes that are available for the user to
     // use. Some quick unlock modes may be disabled by policy.
     static void getAvailableModes(ModesCallback onComplete);
@@ -98,10 +107,10 @@
     // Update the set of quick unlock modes that are currently active/enabled.
     // |token|: The token returned by $(ref:getAuthToken).
     // |modes|: The quick unlock modes that should be active.
-    // |credentials|: The associated credential for each mode. To keep
-    // the credential the same for the associated mode, pass an empty string.
+    // |credentials|: The associated credential for each mode. To keep the
+    //     credential the same for the associated mode, pass an empty string.
     // |onComplete|: Called with true if the quick unlock state was updated,
-    // false otherwise. The update is treated as a single atomic operation.
+    //     false otherwise. The update is treated as a single atomic operation.
     static void setModes(DOMString token,
                          QuickUnlockMode[] modes,
                          DOMString[] credentials,
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 2736e567..3dbe0464 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2097,44 +2097,9 @@
 #endif
 
 #if BUILDFLAG(ENABLE_APP_LIST)
-// The directory in user data dir that contains the profile to be used with the
-// app launcher.
-const char kAppListProfile[] = "app_list.profile";
-
-// The number of times the app launcher was launched since last ping and
-// the time of the last ping.
-const char kAppListLaunchCount[] = "app_list.launch_count";
-const char kLastAppListLaunchPing[] = "app_list.last_launch_ping";
-
-// The number of times the an app was launched from the app launcher since last
-// ping and the time of the last ping.
-const char kAppListAppLaunchCount[] = "app_list.app_launch_count";
-const char kLastAppListAppLaunchPing[] = "app_list.last_app_launch_ping";
-
-// A boolean that tracks whether the user has ever enabled the app launcher.
-const char kAppLauncherHasBeenEnabled[] =
-    "apps.app_launcher.has_been_enabled";
-
-// An enum indicating how the app launcher was enabled. E.g., via webstore, app
-// install, command line, etc. For UMA.
-const char kAppListEnableMethod[] = "app_list.how_enabled";
-
-// The time that the app launcher was enabled. Cleared when UMA is recorded.
-const char kAppListEnableTime[] = "app_list.when_enabled";
-
 // Keeps local state of app list while sync service is not available.
 const char kAppListLocalState[] = "app_list.local_state";
-
-// A dictionary that tracks the Drive app to Chrome app mapping. The key is
-// a Drive app id and the value is the corresponding Chrome app id. The pref
-// is unsynable and used to track local mappings only.
-const char kAppLauncherDriveAppMapping[] =
-    "apps.app_launcher.drive_app_mapping";
-
-// A list of Drive app ids that tracks the uninstallable Drive apps.
-const char kAppLauncherUninstalledDriveApps[] =
-    "apps.app_launcher.uninstalled_drive_apps";
-#endif  // BUILDFLAG(ENABLE_APP_LIST)
+#endif
 
 #if defined(OS_WIN)
 // If set, the user requested to launch the app with this extension id while
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index ec53ec0..d624baf 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -754,17 +754,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_APP_LIST)
-extern const char kAppListProfile[];
-extern const char kLastAppListLaunchPing[];
-extern const char kAppListLaunchCount[];
-extern const char kLastAppListAppLaunchPing[];
-extern const char kAppListAppLaunchCount[];
-extern const char kAppLauncherHasBeenEnabled[];
-extern const char kAppListEnableMethod[];
-extern const char kAppListEnableTime[];
 extern const char kAppListLocalState[];
-extern const char kAppLauncherDriveAppMapping[];
-extern const char kAppLauncherUninstalledDriveApps[];
 #endif  // BUILDFLAG(ENABLE_APP_LIST)
 
 #if defined(OS_WIN)
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 5e3c3929..94844462 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -217,6 +217,8 @@
 const char kChromeUITermsOemURL[] = "chrome://terms/oem";
 const char kChromeUIUserImageHost[] = "userimage";
 const char kChromeUIUserImageURL[] = "chrome://userimage/";
+const char kChromeUIAssistantOptInHost[] = "assistant-optin";
+const char kChromeUIAssistantOptInURL[] = "chrome://assistant-optin/";
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(OS_WIN)
@@ -387,6 +389,7 @@
     kChromeUIPowerHost,
     kChromeUIInternetConfigDialogHost,
     kChromeUIInternetDetailDialogHost,
+    kChromeUIAssistantOptInHost,
 #endif
 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
     kChromeUIDiscardsHost,
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 4c13040..3d601a7 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -213,6 +213,8 @@
 extern const char kChromeUITermsOemURL[];
 extern const char kChromeUIUserImageHost[];
 extern const char kChromeUIUserImageURL[];
+extern const char kChromeUIAssistantOptInHost[];
+extern const char kChromeUIAssistantOptInURL[];
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(OS_WIN)
diff --git a/chrome/installer/linux/BUILD.gn b/chrome/installer/linux/BUILD.gn
index 2ea8f90..fbabd6c3 100644
--- a/chrome/installer/linux/BUILD.gn
+++ b/chrome/installer/linux/BUILD.gn
@@ -49,10 +49,8 @@
 
 # The widevine BUILD.gn only produces shared libraries for x86 and x64
 if (is_chrome_branded && (current_cpu == "x86" || current_cpu == "x64")) {
-  packaging_files_shlibs += [
-    "$root_out_dir/$widevine_cdm_path/libwidevinecdmadapter.so",
-    "$root_out_dir/$widevine_cdm_path/libwidevinecdm.so",
-  ]
+  packaging_files_shlibs +=
+      [ "$root_out_dir/$widevine_cdm_path/libwidevinecdm.so" ]
 }
 
 if (!libcpp_is_static && use_custom_libcxx) {
@@ -370,10 +368,7 @@
     ]
   }
   if (current_cpu == "x86" || current_cpu == "x64") {
-    public_deps += [
-      "//third_party/widevine/cdm:widevinecdm",
-      "//third_party/widevine/cdm:widevinecdmadapter",
-    ]
+    public_deps += [ "//third_party/widevine/cdm" ]
   }
   if (!is_chromeos) {
     public_deps += [ ":rpm_packaging_files" ]
diff --git a/chrome/installer/linux/common/installer.include b/chrome/installer/linux/common/installer.include
index 96996f5d..58b8061 100644
--- a/chrome/installer/linux/common/installer.include
+++ b/chrome/installer/linux/common/installer.include
@@ -194,8 +194,7 @@
   fi
 
   # Widevine CDM.
-  if [ -f "${BUILDDIR}/libwidevinecdmadapter.so" ]; then
-    install -m ${SHLIB_PERMS} -s "${BUILDDIR}/libwidevinecdmadapter.so" "${STAGEDIR}/${INSTALLDIR}/"
+  if [ -f "${BUILDDIR}/libwidevinecdm.so" ]; then
     # No need to strip; libwidevinecdm.so starts out stripped.
     install -m ${SHLIB_PERMS} "${BUILDDIR}/libwidevinecdm.so" "${STAGEDIR}/${INSTALLDIR}/"
   fi
diff --git a/chrome/installer/mac/sign_versioned_dir.sh.in b/chrome/installer/mac/sign_versioned_dir.sh.in
index a9db32c..7a853e3 100644
--- a/chrome/installer/mac/sign_versioned_dir.sh.in
+++ b/chrome/installer/mac/sign_versioned_dir.sh.in
@@ -91,7 +91,6 @@
 helper_app="${versioned_dir}/@MAC_PRODUCT_NAME@ Helper.app"
 app_mode_loader_app="${framework}/Resources/app_mode_loader.app"
 app_mode_loader="${app_mode_loader_app}/Contents/MacOS/app_mode_loader"
-widevine_plugin="${framework}/Libraries/WidevineCdm/_platform_specific/mac_x64/widevinecdmadapter.plugin"
 
 codesign_with_options "${crashpad_handler}" \
                       "${enforcement_flags_helpers}" \
@@ -120,12 +119,6 @@
                       "${enforcement_flags_helpers}" \
                       "${xpc_bundle_id}"
 
-# Only sign widevine plugin if it is present in the bundle.
-# ${enforcement_flags*} are meaningless for dynamic libraries.
-if [[ -f "${widevine_plugin}" ]]; then
-  codesign_with_options "${widevine_plugin}" "" "widevinecdmadapter"
-fi
-
 # The framework is a dylib, so ${enforcement_flags_helpers} are meaningless.
 codesign_with_options "${framework}" "" "com.google.Chrome.framework"
 
@@ -133,10 +126,6 @@
                       "${enforcement_flags_app}" \
                       "com.google.Chrome.helper"
 
-if [[ -f "${widevine_plugin}" ]]; then
-  codesign_display_and_verify "${widevine_plugin}"
-fi
-
 codesign_display_and_verify "${crashpad_handler}" --deep
 codesign_display_and_verify "${app_mode_loader}" --ignore-resources
 codesign_display_and_verify "${notification_service}" --deep
diff --git a/chrome/installer/mini_installer/chrome.release b/chrome/installer/mini_installer/chrome.release
index cc944d6..4a502c6 100644
--- a/chrome/installer/mini_installer/chrome.release
+++ b/chrome/installer/mini_installer/chrome.release
@@ -79,10 +79,8 @@
 WidevineCdm\manifest.json: %(VersionDir)s\WidevineCdm\
 WidevineCdm\_platform_specific\win_x86\widevinecdm.dll: %(VersionDir)s\WidevineCdm\_platform_specific\win_x86\
 WidevineCdm\_platform_specific\win_x86\widevinecdm.dll.sig: %(VersionDir)s\WidevineCdm\_platform_specific\win_x86\
-WidevineCdm\_platform_specific\win_x86\widevinecdmadapter.dll: %(VersionDir)s\WidevineCdm\_platform_specific\win_x86\
 WidevineCdm\_platform_specific\win_x64\widevinecdm.dll: %(VersionDir)s\WidevineCdm\_platform_specific\win_x64\
 WidevineCdm\_platform_specific\win_x64\widevinecdm.dll.sig: %(VersionDir)s\WidevineCdm\_platform_specific\win_x64\
-WidevineCdm\_platform_specific\win_x64\widevinecdmadapter.dll: %(VersionDir)s\WidevineCdm\_platform_specific\win_x64\
 
 #
 # MEI Preload sub dir
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc
index aa26cb1d..85b9e2ea 100644
--- a/chrome/installer/setup/install_worker.cc
+++ b/chrome/installer/setup/install_worker.cc
@@ -890,6 +890,10 @@
 
   AddOsUpgradeWorkItems(installer_state, setup_path, new_version, product,
                         install_list);
+#if defined(GOOGLE_CHROME_BUILD)
+  AddEnterpriseEnrollmentWorkItems(installer_state, setup_path, new_version,
+                                   product, install_list);
+#endif
   AddFirewallRulesWorkItems(installer_state, dist, current_version == nullptr,
                             install_list);
 
@@ -1056,4 +1060,42 @@
   }
 }
 
+#if defined(GOOGLE_CHROME_BUILD)
+void AddEnterpriseEnrollmentWorkItems(const InstallerState& installer_state,
+                                      const base::FilePath& setup_path,
+                                      const base::Version& new_version,
+                                      const Product& product,
+                                      WorkItemList* install_list) {
+  if (!installer_state.system_install())
+    return;
+
+  const HKEY root_key = installer_state.root_key();
+  const base::string16 cmd_key(
+      GetRegCommandKey(product.distribution(), kCmdStoreDMToken));
+
+  if (installer_state.operation() == InstallerState::UNINSTALL) {
+    install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY)
+        ->set_log_message("Removing store DM token command");
+  } else {
+    // Register a command to allow Chrome to request Google Update to run
+    // setup.exe --store-dmtoken=<token>, which will store the specifed token in
+    // the registry.
+    base::CommandLine cmd_line(
+        installer_state.GetInstallerDirectory(new_version)
+            .Append(setup_path.BaseName()));
+    cmd_line.AppendSwitchASCII(switches::kStoreDMToken, "%1");
+    cmd_line.AppendSwitch(switches::kSystemLevel);
+    cmd_line.AppendSwitch(switches::kVerboseLogging);
+    InstallUtil::AppendModeSwitch(&cmd_line);
+
+    AppCommand cmd(cmd_line.GetCommandLineString());
+    // TODO(alito): For now setting this command as web accessible is required
+    // by Google Update.  Could revisit this should Google Update change the
+    // way permissions are handled for commands.
+    cmd.set_is_web_accessible(true);
+    cmd.AddWorkItems(root_key, cmd_key, install_list);
+  }
+}
+#endif
+
 }  // namespace installer
diff --git a/chrome/installer/setup/install_worker.h b/chrome/installer/setup/install_worker.h
index 28b9e249..e145aa1 100644
--- a/chrome/installer/setup/install_worker.h
+++ b/chrome/installer/setup/install_worker.h
@@ -119,6 +119,19 @@
                            const Product& product,
                            WorkItemList* install_list);
 
+#if defined(GOOGLE_CHROME_BUILD)
+// Adds work items to add or remove the "store-dmtoken" command to |product|'s
+// version key. This method is a no-op if this is anything other than
+// system-level Chrome. The command is used when enrolling Chrome browser
+// instances into enterprise management. |new_version| is the version of the
+// product(s) currently being installed -- can be empty on uninstall.
+void AddEnterpriseEnrollmentWorkItems(const InstallerState& installer_state,
+                                      const base::FilePath& setup_path,
+                                      const base::Version& new_version,
+                                      const Product& product,
+                                      WorkItemList* install_list);
+#endif
+
 }  // namespace installer
 
 #endif  // CHROME_INSTALLER_SETUP_INSTALL_WORKER_H_
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index 2f68e9f..74cece8 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -1021,6 +1021,20 @@
         cmd_line.GetSwitchValueNative(
             installer::switches::kSetDisplayVersionValue));
     *exit_code = OverwriteDisplayVersions(registry_product, registry_value);
+#if defined(GOOGLE_CHROME_BUILD)
+  } else if (cmd_line.HasSwitch(installer::switches::kStoreDMToken)) {
+    // Write the specified token to the registry, overwriting any already
+    // existing value.
+    base::string16 token_switch_value =
+        cmd_line.GetSwitchValueNative(installer::switches::kStoreDMToken);
+    base::Optional<std::string> token;
+    if (!(token = installer::DecodeDMTokenSwitchValue(token_switch_value)) ||
+        !installer::StoreDMToken(*token)) {
+      *exit_code = installer::STORE_DMTOKEN_FAILED;
+    } else {
+      *exit_code = installer::STORE_DMTOKEN_SUCCESS;
+    }
+#endif
   } else {
     handled = false;
   }
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc
index 15d0a00..0f1337df 100644
--- a/chrome/installer/setup/setup_util.cc
+++ b/chrome/installer/setup/setup_util.cc
@@ -20,6 +20,7 @@
 #include <string>
 #include <utility>
 
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
@@ -879,4 +880,54 @@
          windows_version >= base::win::VERSION_WIN10_RS1;
 }
 
+base::Optional<std::string> DecodeDMTokenSwitchValue(
+    const base::string16& encoded_token) {
+  if (encoded_token.empty()) {
+    LOG(ERROR) << "Empty DMToken specified on the command line";
+    return base::nullopt;
+  }
+
+  // The token passed on the command line is base64-encoded, but since this is
+  // on Windows, it is passed in as a wide string containing base64 values only.
+  std::string token;
+  if (!base::IsStringASCII(encoded_token) ||
+      !base::Base64Decode(base::UTF16ToASCII(encoded_token), &token)) {
+    LOG(ERROR) << "DMToken passed on the command line is not correctly encoded";
+    return base::nullopt;
+  }
+
+  return token;
+}
+
+bool StoreDMToken(const std::string& token) {
+  DCHECK(install_static::IsSystemInstall());
+
+  std::wstring path;
+  std::wstring name;
+  InstallUtil::GetMachineLevelUserCloudPolicyDMTokenRegistryPath(&path,
+                                                                 &name);
+
+  base::win::RegKey key;
+  LONG result = key.Create(HKEY_LOCAL_MACHINE, path.c_str(),
+                           KEY_WRITE | KEY_WOW64_64KEY);
+  if (result != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to create/open registry key HKLM\\" << path
+               << " for writing result=" << result;
+    return false;
+  }
+
+  result =
+      key.WriteValue(name.c_str(), token.data(),
+                     base::saturated_cast<DWORD>(token.size()), REG_BINARY);
+  if (result != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to write specified DMToken to the registry at HKLM\\"
+               << path << "\\" << name << " result=" << result;
+    return false;
+  }
+
+  VLOG(1) << "Successfully stored specified DMToken in the registry.";
+
+  return true;
+}
+
 }  // namespace installer
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h
index b39be6f..1dde01a 100644
--- a/chrome/installer/setup/setup_util.h
+++ b/chrome/installer/setup/setup_util.h
@@ -14,6 +14,7 @@
 #include <memory>
 #include <vector>
 
+#include "base/optional.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "chrome/installer/util/browser_distribution.h"
@@ -162,6 +163,16 @@
 // tiles.
 bool OsSupportsDarkTextTiles();
 
+// Returns a DM token decoded from the base-64 |encoded_token|, or null in case
+// of a decoding error.  The returned DM token is an opaque binary blob and
+// should not be treated as an ASCII or UTF-8 string.
+base::Optional<std::string> DecodeDMTokenSwitchValue(
+    const base::string16& encoded_token);
+
+// Saves a DM token to a global location on the machine accessible to all
+// install modes of the browser (i.e., stable and all three side-by-side modes).
+bool StoreDMToken(const std::string& token);
+
 }  // namespace installer
 
 #endif  // CHROME_INSTALLER_SETUP_SETUP_UTIL_H_
diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc
index 22e3439..c2ea3ad 100644
--- a/chrome/installer/setup/setup_util_unittest.cc
+++ b/chrome/installer/setup/setup_util_unittest.cc
@@ -10,6 +10,7 @@
 #include <memory>
 #include <string>
 
+#include "base/base64.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -18,6 +19,7 @@
 #include "base/process/launch.h"
 #include "base/process/process_handle.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/histogram_tester.h"
 #include "base/test/test_reg_util_win.h"
 #include "base/test/test_timeouts.h"
@@ -26,12 +28,15 @@
 #include "base/win/registry.h"
 #include "base/win/scoped_handle.h"
 #include "chrome/install_static/install_details.h"
+#include "chrome/install_static/install_util.h"
+#include "chrome/install_static/test/scoped_install_details.h"
 #include "chrome/installer/setup/installer_state.h"
 #include "chrome/installer/setup/setup_constants.h"
 #include "chrome/installer/setup/setup_util.h"
 #include "chrome/installer/util/browser_distribution.h"
 #include "chrome/installer/util/google_update_constants.h"
 #include "chrome/installer/util/installation_state.h"
+#include "chrome/installer/util/install_util.h"
 #include "chrome/installer/util/updating_app_registration_data.h"
 #include "chrome/installer/util/util_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -453,6 +458,44 @@
   EXPECT_FALSE(start_time.is_null());
 }
 
+TEST(SetupUtilTest, DecodeDMTokenSwitchValue) {
+  // Expect false with empty or badly formed base64-encoded string.
+  EXPECT_FALSE(installer::DecodeDMTokenSwitchValue(L""));
+  EXPECT_FALSE(installer::DecodeDMTokenSwitchValue(L"not-ascii\xff"));
+  EXPECT_FALSE(installer::DecodeDMTokenSwitchValue(L"not-base64-string"));
+
+  std::string token("this is a token");
+  std::string encoded;
+  base::Base64Encode(token, &encoded);
+  EXPECT_EQ(token,
+            *installer::DecodeDMTokenSwitchValue(base::UTF8ToUTF16(encoded)));
+}
+
+TEST(SetupUtilTest, StoreDMTokenToRegistry) {
+  install_static::ScopedInstallDetails scoped_install_details(true);
+  registry_util::RegistryOverrideManager registry_override_manager;
+  registry_override_manager.OverrideRegistry(HKEY_LOCAL_MACHINE);
+
+  std::string token("tokens are \0 binary data");
+  EXPECT_TRUE(installer::StoreDMToken(token));
+
+  std::wstring path;
+  std::wstring name;
+  InstallUtil::GetMachineLevelUserCloudPolicyDMTokenRegistryPath(&path, &name);
+  base::win::RegKey key;
+  ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, path.c_str(),
+                                    KEY_QUERY_VALUE | KEY_WOW64_64KEY));
+
+  DWORD size = 64;
+  std::vector<char> raw_value(size);
+  DWORD dtype;
+  ASSERT_EQ(ERROR_SUCCESS,
+            key.ReadValue(name.c_str(), raw_value.data(), &size, &dtype));
+  EXPECT_EQ(REG_BINARY, dtype);
+  ASSERT_EQ(token.length(), size);
+  EXPECT_EQ(0, memcmp(token.data(), raw_value.data(), size));
+}
+
 namespace installer {
 
 class DeleteRegistryKeyPartialTest : public ::testing::Test {
diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc
index d3642b3..cfa3efe2 100644
--- a/chrome/installer/util/install_util.cc
+++ b/chrome/installer/util/install_util.cc
@@ -503,7 +503,7 @@
     bool system_install,
     const BrowserDistribution* dist) {
   DCHECK(dist);
-  base::win::RegKey key;
+  RegKey key;
   base::string16 downgrade_version;
   if (key.Open(system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
                dist->GetStateKey().c_str(),
@@ -538,6 +538,70 @@
   }
 }
 
+// static
+void InstallUtil::GetMachineLevelUserCloudPolicyEnrollmentTokenRegistryPath(
+    std::wstring* key_path,
+    std::wstring* value_name) {
+  // This token applies to all installs on the machine, even though only a
+  // system install can set it.  This is to prevent users from doing a user
+  // install of chrome to get around policies.
+  *key_path = L"SOFTWARE\\Policies\\";
+  install_static::AppendChromeInstallSubDirectory(
+      install_static::InstallDetails::Get().mode(), false /* !include_suffix */,
+      key_path);
+  *value_name = L"MachineLevelUserCloudPolicyEnrollmentToken";
+}
+
+// static
+void InstallUtil::GetMachineLevelUserCloudPolicyDMTokenRegistryPath(
+    std::wstring* key_path,
+    std::wstring* value_name) {
+  // This token applies to all installs on the machine, even though only a
+  // system install can set it.  This is to prevent users from doing a user
+  // install of chrome to get around policies.
+  *key_path = L"SOFTWARE\\";
+  install_static::AppendChromeInstallSubDirectory(
+      install_static::InstallDetails::Get().mode(), false /* !include_suffix */,
+      key_path);
+  key_path->append(L"\\Enrollment");
+  *value_name = L"dmtoken";
+}
+
+// static
+std::wstring InstallUtil::GetMachineLevelUserCloudPolicyEnrollmentToken() {
+  // Because chrome needs to know if machine level user cloud policies must be
+  // initialized even before the entire policy service is brought up, this
+  // helper function exists to directly read the token from the system policies.
+  //
+  // Putting the enrollment token in the system policy area is a convenient
+  // way for administrators to enroll chrome throughout their fleet by pushing
+  // this token via SCCM.
+  // TODO(rogerta): This may not be the best place for the helpers dealing with
+  // the enrollment and/or DM tokens.  See crbug.com/823852 for details.
+  std::wstring key_path;
+  std::wstring value_name;
+  GetMachineLevelUserCloudPolicyEnrollmentTokenRegistryPath(&key_path,
+                                                            &value_name);
+
+  RegKey key;
+  LONG result = key.Open(HKEY_LOCAL_MACHINE, key_path.c_str(), KEY_READ);
+  if (result != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to create registry key HKLM\\" << key_path
+               << " for reading result=" << result;
+    return std::wstring();
+  }
+
+  std::wstring value;
+  result = key.ReadValue(value_name.c_str(), &value);
+  if (result != ERROR_SUCCESS) {
+    LOG(ERROR) << "Unable to read registry value HKLM\\" << key_path
+               << "\\" << value_name << " for writing result=" << result;
+    return std::wstring();
+  }
+
+  return value;
+}
+
 InstallUtil::ProgramCompare::ProgramCompare(const base::FilePath& path_to_match)
     : path_to_match_(path_to_match),
       file_info_() {
diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h
index 7c6bf75..cc67722 100644
--- a/chrome/installer/util/install_util.h
+++ b/chrome/installer/util/install_util.h
@@ -190,6 +190,23 @@
       const BrowserDistribution* dist,
       WorkItemList* list);
 
+  // Returns the registry key path and value name where the enrollment token is
+  // stored for machine level user cloud policies.
+  static void GetMachineLevelUserCloudPolicyEnrollmentTokenRegistryPath(
+      std::wstring* key_path,
+      std::wstring* value_name);
+
+  // Returns the registry key path and value name where the enrollment token is
+  // stored for machine level user cloud policies.
+  static void GetMachineLevelUserCloudPolicyDMTokenRegistryPath(
+      std::wstring* key_path,
+      std::wstring* value_name);
+
+  // Returns the token used to enroll this chrome instance for machine level
+  // user cloud policies.  Returns an empty string if this machine should not
+  // be enrolled.
+  static std::wstring GetMachineLevelUserCloudPolicyEnrollmentToken();
+
   // A predicate that compares the program portion of a command line with a
   // given file path.  First, the file paths are compared directly.  If they do
   // not match, the filesystem is consulted to determine if the paths reference
diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc
index 601fdd05..4ed3469f 100644
--- a/chrome/installer/util/util_constants.cc
+++ b/chrome/installer/util/util_constants.cc
@@ -124,6 +124,9 @@
 // line flag so that we try the launch only once.
 const char kRunAsAdmin[] = "run-as-admin";
 
+// Saves the specified device management token to the registry.
+const char kStoreDMToken[] = "store-dmtoken";
+
 // Combined with --uninstall, signals to setup.exe that this uninstall was
 // triggered by a self-destructing Chrome.
 const char kSelfDestruct[] = "self-destruct";
@@ -188,6 +191,7 @@
 const wchar_t kChromeNewExe[] = L"new_chrome.exe";
 const wchar_t kChromeOldExe[] = L"old_chrome.exe";
 const wchar_t kCmdOnOsUpgrade[] = L"on-os-upgrade";
+const wchar_t kCmdStoreDMToken[] = L"store-dmtoken";
 const wchar_t kEULASentinelFile[] = L"EULA Accepted";
 const wchar_t kInstallBinaryDir[] = L"Application";
 const wchar_t kInstallerDir[] = L"Installer";
diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h
index eaf99442..13232e50 100644
--- a/chrome/installer/util/util_constants.h
+++ b/chrome/installer/util/util_constants.h
@@ -106,8 +106,11 @@
                                                // delete all files that belong
                                                // to old versions of Chrome too
                                                // many times without success.
-
-  MAX_INSTALL_STATUS   = 64,  // When adding a new result, bump this and update
+  STORE_DMTOKEN_FAILED = 64,  // Failed to write the specified DMToken to the
+                              // registry.
+  STORE_DMTOKEN_SUCCESS = 65,  // Writing the specified DMToken to the registry
+                               // succeeded.
+  MAX_INSTALL_STATUS   = 66,  // When adding a new result, bump this and update
                               // the InstallStatus enum in histograms.xml.
 };
 
@@ -172,6 +175,7 @@
 extern const char kRenameChromeExe[];
 extern const char kRemoveChromeRegistration[];
 extern const char kRunAsAdmin[];
+extern const char kStoreDMToken[];
 extern const char kSelfDestruct[];
 extern const char kSystemLevel[];
 extern const char kTriggerActiveSetup[];
@@ -200,6 +204,7 @@
 extern const wchar_t kChromeNewExe[];
 extern const wchar_t kChromeOldExe[];
 extern const wchar_t kCmdOnOsUpgrade[];
+extern const wchar_t kCmdStoreDMToken[];
 extern const wchar_t kEULASentinelFile[];
 extern const wchar_t kInstallBinaryDir[];
 extern const wchar_t kInstallerDir[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b1ddcc55..64021945 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1644,10 +1644,10 @@
         "../browser/extensions/api/vpn_provider/vpn_provider_apitest.cc",
         "../browser/mash_service_registry_browsertest.cc",
         "../browser/signin/chromeos_mirror_account_consistency_browsertest.cc",
+        "../browser/ui/app_list/app_list_browsertest.cc",
         "../browser/ui/app_list/arc/arc_usb_host_permission_browsertest.cc",
         "../browser/ui/app_list/crostini/crostini_installer_view_browsertest.cc",
         "../browser/ui/ash/accelerator_commands_browsertest.cc",
-        "../browser/ui/ash/app_list/app_list_browsertest.cc",
         "../browser/ui/ash/chrome_new_window_client_browsertest.cc",
         "../browser/ui/ash/chrome_screenshot_grabber_browsertest.cc",
         "../browser/ui/ash/keyboard_controller_browsertest.cc",
@@ -1966,7 +1966,7 @@
       # Runtime dependencies.
       data_deps += [
         "//media/cdm/library_cdm/clear_key_cdm",
-        "//third_party/widevine/cdm:widevinecdmadapter",
+        "//third_party/widevine/cdm",
       ]
     }
     if (enable_print_preview) {
@@ -4300,7 +4300,6 @@
   if (enable_app_list) {
     sources += [
       "../browser/ui/app_list/app_context_menu_unittest.cc",
-      "../browser/ui/app_list/app_list_service_unittest.cc",
       "../browser/ui/app_list/app_list_syncable_service_unittest.cc",
       "../browser/ui/app_list/app_list_test_util.cc",
       "../browser/ui/app_list/app_list_test_util.h",
@@ -4310,7 +4309,6 @@
       "../browser/ui/app_list/arc/arc_app_utils_unittest.cc",
       "../browser/ui/app_list/arc/arc_vpn_provider_unittest.cc",
       "../browser/ui/app_list/extension_app_model_builder_unittest.cc",
-      "../browser/ui/app_list/profile_loader_unittest.cc",
       "../browser/ui/app_list/search/answer_card/answer_card_result_unittest.cc",
       "../browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc",
       "../browser/ui/app_list/search/arc/arc_app_data_search_provider_unittest.cc",
@@ -4329,8 +4327,6 @@
       "../browser/ui/app_list/test/fake_app_list_model_updater.h",
       "../browser/ui/app_list/test/fake_profile.cc",
       "../browser/ui/app_list/test/fake_profile.h",
-      "../browser/ui/app_list/test/fake_profile_store.cc",
-      "../browser/ui/app_list/test/fake_profile_store.h",
       "../browser/ui/views/apps/app_info_dialog/app_info_dialog_ash_unittest.cc",
     ]
     deps += [
@@ -4817,7 +4813,7 @@
         "../browser/chromeos/login/test/oobe_base_test.cc",
         "../browser/chromeos/login/test/oobe_base_test.h",
         "../browser/download/notification/download_notification_interactive_uitest.cc",
-        "../browser/ui/ash/app_list/app_list_interactive_uitest.cc",
+        "../browser/ui/app_list/app_list_interactive_uitest.cc",
         "../browser/ui/ash/tab_scrubber_browsertest.cc",
         "../browser/ui/views/apps/chrome_native_app_window_views_aura_ash_interactive_uitest.cc",
         "../browser/ui/webui/chromeos/login/oobe_display_chooser_browsertest.cc",
@@ -5444,7 +5440,7 @@
       deps += [ "//media/cdm:cdm_paths" ]
       data_deps = [
         "//media/cdm/library_cdm/clear_key_cdm",
-        "//third_party/widevine/cdm:widevinecdmadapter",
+        "//third_party/widevine/cdm",
       ]
     }
 
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 0e5668e..8af6ec9 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -274,33 +274,23 @@
         # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2286
         'CorrectEventFiringTest.testClearingAnElementShouldCauseTheOnChangeHandlerToFire',
         'CorrectEventFiringTest.testClickEventsShouldBubble',
-        'CorrectEventFiringTest.testSubmittingFormFromFormElementShouldFireOnSubmitForThatForm',
         'CorrectEventFiringTest.testSendingKeysToAFocusedElementShouldNotBlurThatElement',
-        'CorrectEventFiringTest.testShouldReportTheXAndYCoordinatesWhenClicking',
-        'CorrectEventFiringTest.testShouldEmitClickEventWhenClickingOnATextInputElement',
-        'CorrectEventFiringTest.testSendingKeysToAnElementShouldCauseTheFocusEventToFire',
-        'ElementAttributeTest.testShouldReturnAnAbsoluteUrlWhenGettingHrefAttributeOfAValidAnchorTag',
-        'ElementAttributeTest.testCanRetrieveTheCurrentValueOfATextFormField_emailInput',
-        'ElementAttributeTest.testShouldReturnTrueForPresentBooleanAttributes',
-        'ElementAttributeTest.testShouldReturnValueOfOnClickAttribute',
-        'ElementAttributeTest.testCanRetrieveTheCurrentValueOfATextFormField_textInput',
-        'ElementAttributeTest.testGetAttributeDoesNotReturnAnObjectForSvgProperties',
-        'ElementAttributeTest.testShouldReturnNullForNonPresentBooleanAttributes',
-        'JavascriptEnabledDriverTest.testShouldBeAbleToFindElementAfterJavascriptCausesANewPageToLoad',
-        'JavascriptEnabledDriverTest.testShouldBeAbleToSwitchToFocusedElement',
-        'JavascriptEnabledDriverTest.testShouldBeAbleToDetermineTheLocationOfAnElement',
-        'JavascriptEnabledDriverTest.testIfNoElementHasFocusTheActiveElementIsTheBody',
-        'JavascriptEnabledDriverTest.testShouldWaitForLoadsToCompleteAfterJavascriptCausesANewPageToLoad',
         'FormHandlingTest.testShouldSubmitAFormUsingTheEnterKey',
         'FormHandlingTest.testShouldSubmitAFormUsingTheNewlineLiteral',
-        'FormHandlingTest.testCanClickOnAnExternalSubmitButton',
-        'FormHandlingTest.testShouldBeAbleToUploadTheSameFileTwice',
+        'JavascriptEnabledDriverTest.testShouldBeAbleToFindElementAfterJavascriptCausesANewPageToLoad',
+        'JavascriptEnabledDriverTest.testShouldBeAbleToDetermineTheLocationOfAnElement',
+        'JavascriptEnabledDriverTest.testShouldBeAbleToSwitchToFocusedElement',
+        'JavascriptEnabledDriverTest.testIfNoElementHasFocusTheActiveElementIsTheBody',
+        'JavascriptEnabledDriverTest.testShouldWaitForLoadsToCompleteAfterJavascriptCausesANewPageToLoad',
+        'TypingTest.testShouldBeAbleToUseArrowKeys',
+        'TypingTest.testShouldReportKeyCodeOfArrowKeysUpDownEvents',
+        'TypingTest.testShouldBeAbleToMixUpperAndLowerCaseLetters',
         'BasicMouseInterfaceTest.testDoubleClickThenGet',
         'MiscTest.testShouldReturnTheSourceOfAPage',
         'MiscTest.testClickingShouldNotTrampleWOrHInGlobalScope',
         'MiscTest.testShouldReportTheCurrentUrlCorrectly',
         'MiscTest.testStimulatesStrangeOnloadInteractionInFirefox',
-        'TypingTest.testShouldBeAbleToUseArrowKeys',
+        'ElementAttributeTest.testShouldCorrectlyReportValueOfColspan',
     ]
 )
 _OS_NEGATIVE_FILTER['android:chrome_beta'] = (
@@ -323,11 +313,10 @@
         'TypingTest.testShouldReportKeyCodeOfArrowKeys',
          # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1006
         'ClickScrollingTest.testShouldBeAbleToClickElementThatIsOutOfViewInANestedFrameThatIsOutOfView',
-	 # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2330
-	 'VisibilityTest.testShouldModifyTheVisibilityOfAnElementDynamically',
+         # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2330
+        'VisibilityTest.testShouldModifyTheVisibilityOfAnElementDynamically',
          # Not applicable on ChromeDriverWebViewShell (doesn't support tabs).
         'WindowSwitchingTest.*',
-
         'TakesScreenshotTest.testShouldCaptureScreenshot',
     ]
 )
diff --git a/chrome/test/data/webrtc/peerconnection.js b/chrome/test/data/webrtc/peerconnection.js
index 7d921f2..78bddd5 100644
--- a/chrome/test/data/webrtc/peerconnection.js
+++ b/chrome/test/data/webrtc/peerconnection.js
@@ -69,18 +69,22 @@
  * to be used as parameter to |RTCPeerConnection.generateCertificate|. The
  * resulting certificate will be used by the peer connection. If null, a default
  * certificate is generated by the |RTCPeerConnection| instead.
+ * @param {string} peerConnectionConstraints Unless null, this adds peer
+ * connection constraints when creating new RTCPeerConnection.
  */
-function preparePeerConnection(keygenAlgorithm = null) {
+function preparePeerConnection(
+    keygenAlgorithm = null, peerConnectionConstraints = null) {
   if (gPeerConnection !== null)
     throw failTest('Creating peer connection, but we already have one.');
 
   if (keygenAlgorithm === null) {
-    gPeerConnection = createPeerConnection_(null);
+    gPeerConnection = createPeerConnection_(null, peerConnectionConstraints);
     returnToTest('ok-peerconnection-created');
   } else {
     RTCPeerConnection.generateCertificate(keygenAlgorithm).then(
         function(certificate) {
-          preparePeerConnectionWithCertificate(certificate);
+          preparePeerConnectionWithCertificate(certificate,
+              peerConnectionConstraints);
         },
         function() {
           failTest('Certificate generation failed. keygenAlgorithm: ' +
@@ -94,12 +98,15 @@
  * in this file. Alternatively, see |preparePeerConnection|.
  * @param {!Object} certificate The |RTCCertificate| that will be used by the
  * peer connection.
+ * @param {string} peerConnectionConstraints Unless null, this adds peer
+ * connection constraints when creating new RTCPeerConnection.
  */
-function preparePeerConnectionWithCertificate(certificate) {
+function preparePeerConnectionWithCertificate(
+    certificate, peerConnectionConstraints = null) {
   if (gPeerConnection !== null)
     throw failTest('Creating peer connection, but we already have one.');
   gPeerConnection = createPeerConnection_(
-      {iceServers:[], certificates:[certificate]});
+      {iceServers:[], certificates:[certificate]}, peerConnectionConstraints);
   returnToTest('ok-peerconnection-created');
 }
 
@@ -509,9 +516,10 @@
 // Internals.
 
 /** @private */
-function createPeerConnection_(rtcConfig) {
+function createPeerConnection_(rtcConfig, peerConnectionConstraints) {
   try {
-    peerConnection = new RTCPeerConnection(rtcConfig, {});
+    peerConnection =
+        new RTCPeerConnection(rtcConfig, peerConnectionConstraints);
   } catch (exception) {
     throw failTest('Failed to create peer connection: ' + exception);
   }
diff --git a/chrome/test/data/webui/extensions/extension_options_dialog_test.js b/chrome/test/data/webui/extensions/extension_options_dialog_test.js
index efa80e9..6433c686 100644
--- a/chrome/test/data/webui/extensions/extension_options_dialog_test.js
+++ b/chrome/test/data/webui/extensions/extension_options_dialog_test.js
@@ -48,27 +48,6 @@
             data.name,
             assert(optionsDialog.$$('#icon-and-name-wrapper span'))
                 .textContent.trim());
-
-        var optionEle = optionsDialog.$$('extensionoptions');
-
-        var mockOptions = optionsDialog.extensionOptions_;
-        assertEquals(data.id, mockOptions.extension);
-
-        // Setting the preferred width to something below the min width
-        // changes the width property. But visually, min-width still prevails.
-        mockOptions.onpreferredsizechanged({height: 100, width: 100});
-        assertEquals('100px', optionEle.style.width);
-        var computedStyle = window.getComputedStyle(optionEle);
-        assertEquals('300px', computedStyle.minWidth);
-
-        // Setting the preferred size to between the min and max dimensions
-        // should change the dimensions.
-        mockOptions.onpreferredsizechanged({height: 500, width: 400});
-        assertEquals('500px', optionEle.style.height);
-        assertEquals('400px', optionEle.style.width);
-
-        mockOptions.onclose();
-        assertFalse(isDialogVisible());
       });
     });
   });
diff --git a/chrome/test/data/webui/settings/fake_quick_unlock_private.js b/chrome/test/data/webui/settings/fake_quick_unlock_private.js
index 544d547..be53f27 100644
--- a/chrome/test/data/webui/settings/fake_quick_unlock_private.js
+++ b/chrome/test/data/webui/settings/fake_quick_unlock_private.js
@@ -22,13 +22,14 @@
    */
   function FakeQuickUnlockPrivate() {
     /** @type {!Array<!chrome.quickUnlockPrivate.QuickUnlockMode>} */
-        this.availableModes = [chrome.quickUnlockPrivate.QuickUnlockMode.PIN];
+    this.availableModes = [chrome.quickUnlockPrivate.QuickUnlockMode.PIN];
     /** @type {!Array<!chrome.quickUnlockPrivate.QuickUnlockMode>} */
-        this.activeModes = [];
+    this.activeModes = [];
     /** @type {!Array<string>} */ this.credentials = [];
     /** @type {string} */ this.accountPassword = '';
     /** @type {!chrome.quickUnlockPrivate.CredentialRequirements} */
-        this.credentialRequirements = {minLength: 4, maxLength: 0};
+    this.credentialRequirements = {minLength: 4, maxLength: 0};
+    /** @type {boolean} */ this.lockScreenEnabled = false;
   }
 
   function clearError_() {
@@ -59,6 +60,24 @@
 
     /**
      * @override
+     * @param {string} token
+     * @param {boolean} enabled
+     * @param {function(boolean):void}= onComplete
+     */
+    setLockScreenEnabled: function(token, enabled, onComplete) {
+      if (token != FAKE_TOKEN) {
+        chrome.runtime.lastError = 'Authentication token invalid';
+      } else {
+        // Note: Fake does not set pref value.
+        this.lockScreenEnabled = enabled;
+        clearError_();
+      }
+      if (onComplete)
+        onComplete();
+    },
+
+    /**
+     * @override
      * @param {function(
      *     !Array<!chrome.quickUnlockPrivate.QuickUnlockMode>):void} onComplete
      */
diff --git a/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js b/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
index 9dbb09f5..634e7e23 100644
--- a/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
+++ b/chrome/test/data/webui/settings/quick_unlock_authenticate_browsertest_chromeos.js
@@ -266,6 +266,24 @@
         assertDeepEquals([], quickUnlockPrivateApi.activeModes);
       });
 
+      // Toggling the lock screen preference calls setLockScreenEnabled.
+      test('SetLockScreenEnabled', function() {
+        testElement.authToken_ = quickUnlockPrivateApi.getFakeToken();
+        let toggle = getFromElement('#enableLockScreen');
+        let lockScreenEnabled = toggle.checked;
+        quickUnlockPrivateApi.lockScreenEnabled = lockScreenEnabled;
+
+        MockInteractions.tap(toggle);
+        assertEquals(toggle.checked, !lockScreenEnabled);
+        assertEquals(
+            quickUnlockPrivateApi.lockScreenEnabled, !lockScreenEnabled);
+
+        MockInteractions.tap(toggle);
+        assertEquals(toggle.checked, lockScreenEnabled);
+        assertEquals(
+            quickUnlockPrivateApi.lockScreenEnabled, lockScreenEnabled);
+      });
+
       // The various radio buttons update internal state and do not modify
       // prefs.
       test('TappingButtonsChangesUnderlyingState', function() {
diff --git a/chrome/test/mini_installer/config/chrome_system_installed.prop b/chrome/test/mini_installer/config/chrome_system_installed.prop
index 8622a31..2171fe32 100644
--- a/chrome/test/mini_installer/config/chrome_system_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_system_installed.prop
@@ -26,6 +26,21 @@
       },
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_LOCAL_MACHINE\\$CHROME_UPDATE_REGISTRY_SUBKEY\\Commands\\store-dmtoken": {
+      "condition": "'$CHROME_SHORT_NAME' == 'Chrome'",
+      "exists": "required",
+      "values": {
+        "CommandLine": {
+          "type": "SZ",
+          "data": "\"$PROGRAM_FILES\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --store-dmtoken=%1 --system-level --verbose-logging"
+        },
+        "WebAccessible": {
+          "type": "DWORD",
+          "data": 1
+        }
+      },
+      "wow_key": "KEY_WOW64_32KEY"
+    },
     "HKEY_LOCAL_MACHINE\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
       "exists": "forbidden",
       "wow_key": "KEY_WOW64_32KEY"
diff --git a/chrome/test/mini_installer/config/chrome_system_updated.prop b/chrome/test/mini_installer/config/chrome_system_updated.prop
index 0663428..ee54760 100644
--- a/chrome/test/mini_installer/config/chrome_system_updated.prop
+++ b/chrome/test/mini_installer/config/chrome_system_updated.prop
@@ -26,6 +26,21 @@
       },
       "wow_key": "KEY_WOW64_32KEY"
     },
+    "HKEY_LOCAL_MACHINE\\$CHROME_UPDATE_REGISTRY_SUBKEY\\Commands\\store-dmtoken": {
+      "condition": "'$CHROME_SHORT_NAME' == 'Chrome'",
+      "exists": "required",
+      "values": {
+        "CommandLine": {
+          "type": "SZ",
+          "data": "\"$PROGRAM_FILES\\$CHROME_DIR\\Application\\$NEXT_VERSION_MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --store-dmtoken=%1 --system-level --verbose-logging"
+        },
+        "WebAccessible": {
+          "type": "DWORD",
+          "data": 1
+        }
+      },
+      "wow_key": "KEY_WOW64_32KEY"
+    },
     "HKEY_LOCAL_MACHINE\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
       "exists": "forbidden",
       "wow_key": "KEY_WOW64_32KEY"
diff --git a/chrome/test/vr/auto_bisect.py b/chrome/test/vr/auto_bisect.py
index 2debc78..3b97939 100755
--- a/chrome/test/vr/auto_bisect.py
+++ b/chrome/test/vr/auto_bisect.py
@@ -89,6 +89,14 @@
                            'syncing. This has the potential to accidentally '
                            'delete any uncommitted changes, but can help avoid '
                            'random bisect failures.')
+  parser.add_argument('--num-attempts-before-marking-good', type=int, default=1,
+                      help='The number of times the test will be run before '
+                           'a revision can be marked as good. If all runs are '
+                           'found to be good, then the revision is good, '
+                           'otherwise bad. Overriding this can help when '
+                           'bisecting flaky metrics that fluctuate between '
+                           'good/bad values, but can significantly increase '
+                           'bisect time.')
 
   parser.add_argument_group('swarming arguments')
   parser.add_argument('--swarming-server', required=True,
@@ -129,6 +137,12 @@
   if len(args.dimensions) == 0:
     raise RuntimeError('No swarming dimensions provided')
 
+  # Make sure we're set to run at least one attempt per revision
+  if args.num_attempts_before_marking_good < 1:
+    raise RuntimeError(
+        '--num-attempts-before-marking-good set to invalid value %d' %
+        args.num_attempts_before_marking_good)
+
   return (args, unknown_args)
 
 
@@ -161,6 +175,9 @@
     for pair in args.checkout_overrides:
       for key, val in pair.iteritems():
         print '%s will be synced to revision %s' % (key, val)
+  if args.num_attempts_before_marking_good > 1:
+    print ('Each revision must be found to be good %d times before actually '
+           'being marked as good' % args.num_attempts_before_marking_good)
   print '======'
   print 'The test target %s will be built to %s' % (args.build_target,
                                                     args.build_output_dir)
@@ -320,30 +337,42 @@
     revision: The git revision to sync to and test
     output_dir: The directory to save swarming results to
   """
-  result = GetValueAtRevision(args, unknown_args, revision, output_dir)
+  revision_good = True
+  for attempt in xrange(1, args.num_attempts_before_marking_good + 1):
+    # Only bother syncing and building if this is our first attempt on this
+    # revision.
+    result = GetValueAtRevision(args, unknown_args, revision, output_dir,
+        sync=(attempt == 1))
+    # Regression was an increased value.
+    if args.bad_value > args.good_value:
+      # If we're greater than the provided bad value or between good and bad,
+      # but closer to bad, we're still bad.
+      if (result > args.bad_value or
+          abs(args.bad_value - result) < abs(args.good_value - result)):
+        print '=== Attempt %d found revision that is BAD ===' % attempt
+        revision_good = False
+        break
+      else:
+        print '=== Attempt %d found that revision is GOOD ===' % attempt
+    # Regression was a decreased value.
+    else:
+      # If we're smaller than the provided bad value or between good and bad,
+      # but closer to bad, we're still bad.
+      if (result < args.bad_value or
+          abs(args.bad_value - result) < abs(args.good_value - result)):
+        print '=== Attempt %d found that revision is BAD ===' % attempt
+        revision_good = False
+        break
+      else:
+        print '=== Attempt %d found that revision is GOOD ===' % attempt
+
   output = ""
-  # Regression was an increased value
-  if args.bad_value > args.good_value:
-    # If we're greater than the provided bad value or between good and bad, but
-    # closer to bad, we're still bad
-    if (result > args.bad_value or
-        abs(args.bad_value - result) < abs(args.good_value - result)):
-      print '=== Current revision is BAD ==='
-      output = subprocess.check_output(['git', 'bisect', 'bad'])
-    else:
-      print '=== Current revision is GOOD ==='
-      output = subprocess.check_output(['git', 'bisect', 'good'])
-  # Regression was a decreased value
+  if revision_good:
+    print '=== Current revision is GOOD ==='
+    output = subprocess.check_output(['git', 'bisect', 'good'])
   else:
-    # If we're smaller than the provided bad value or between good and bad, but
-    # closer to bad, we're still bad
-    if (result < args.bad_value or
-        abs(args.bad_value - result) < abs(args.good_value - result)):
-      print '=== Current revision is BAD ==='
-      output = subprocess.check_output(['git', 'bisect', 'bad'])
-    else:
-      print '=== Current revision is GOOD ==='
-      output = subprocess.check_output(['git', 'bisect', 'good'])
+    print '=== Current revision is BAD ==='
+    output = subprocess.check_output(['git', 'bisect', 'bad'])
 
   print output
   if output.startswith('Bisecting:'):
@@ -374,6 +403,16 @@
         'they are not, remove them and try again. If the issue persists, try '
         'running with --reset-before-sync')
 
+  # Ensure that the VR assets are synced to the current revision since it isn't
+  # guaranteed that gclient will handle it properly
+  # TODO(https://crbug.com/823882): Remove this once asset downloading is more
+  # robust in gclient.
+  subprocess.check_output([
+      'python', 'third_party/depot_tools/download_from_google_storage.py',
+      '--bucket', 'chrome-vr-assets',
+      '--recursive',
+      '--directory', 'chrome/browser/resources/vr/assets/google_chrome'])
+
   # Checkout any specified revisions.
   cwd = os.getcwd()
   for override in args.checkout_overrides:
@@ -414,7 +453,7 @@
       subprocess.check_output(['git', 'bisect', 'reset'])
 
 
-def GetValueAtRevision(args, unknown_args, revision, output_dir):
+def GetValueAtRevision(args, unknown_args, revision, output_dir, sync=True):
   """Builds and runs the test at a particular revision.
 
   Args:
@@ -426,7 +465,8 @@
   Returns:
     The value of the story/metric combo at the given revision
   """
-  SyncAndBuild(args, unknown_args, revision)
+  if sync:
+    SyncAndBuild(args, unknown_args, revision)
   RunTestOnSwarming(args, unknown_args, output_dir)
   return GetSwarmingResult(args, unknown_args, output_dir)
 
diff --git a/chrome/tools/build/chromeos/FILES.cfg b/chrome/tools/build/chromeos/FILES.cfg
index 891bb23..13324be 100644
--- a/chrome/tools/build/chromeos/FILES.cfg
+++ b/chrome/tools/build/chromeos/FILES.cfg
@@ -80,17 +80,12 @@
     'filename': 'xdg-settings',
     'buildtype': ['dev', 'official'],
   },
-  # CDM files (each has an adapter and the actual CDM):
+  # CDM files
   {
     'filename': 'libclearkeycdm.so',
     'buildtype': ['dev', 'official'],
   },
   {
-    'filename': 'libwidevinecdmadapter.so',
-    'arch': ['32bit', '64bit', 'arm'],
-    'buildtype': ['official'],
-  },
-  {
     'filename': 'libwidevinecdm.so',
     'arch': ['32bit', '64bit', 'arm'],
     'buildtype': ['official'],
diff --git a/chrome/tools/build/linux/FILES.cfg b/chrome/tools/build/linux/FILES.cfg
index afd951a..18da58cb 100644
--- a/chrome/tools/build/linux/FILES.cfg
+++ b/chrome/tools/build/linux/FILES.cfg
@@ -97,11 +97,6 @@
     'buildtype': ['dev', 'official'],
   },
   {
-    'filename': 'libwidevinecdmadapter.so',
-    'arch': ['32bit', '64bit'],
-    'buildtype': ['official'],
-  },
-  {
     'filename': 'libwidevinecdm.so',
     'arch': ['32bit', '64bit'],
     'buildtype': ['official'],
diff --git a/chrome/tools/build/win/FILES.cfg b/chrome/tools/build/win/FILES.cfg
index b12f5380..848e660 100644
--- a/chrome/tools/build/win/FILES.cfg
+++ b/chrome/tools/build/win/FILES.cfg
@@ -348,11 +348,6 @@
     'buildtype': ['official'],
   },
   {
-    'filename': 'WidevineCdm/_platform_specific/win_x86/widevinecdmadapter.dll',
-    'arch': ['32bit'],
-    'buildtype': ['official'],
-  },
-  {
     'filename': 'WidevineCdm/_platform_specific/win_x64/widevinecdm.dll',
     'arch': ['64bit'],
     'buildtype': ['official'],
@@ -362,11 +357,6 @@
     'arch': ['64bit'],
     'buildtype': ['official'],
   },
-  {
-    'filename': 'WidevineCdm/_platform_specific/win_x64/widevinecdmadapter.dll',
-    'arch': ['64bit'],
-    'buildtype': ['official'],
-  },
   # ANGLE files:
   {
     'filename': 'D3DCompiler_47.dll',
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 4d2a41af..8e74729 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -216,6 +216,8 @@
       "cast_web_view_extension.h",
       "extension_request_protocol_handler.cc",
       "extension_request_protocol_handler.h",
+      "extensions/api/bookmarks/bookmarks_api.cc",
+      "extensions/api/bookmarks/bookmarks_api.h",
       "extensions/api/identity/identity_api.cc",
       "extensions/api/identity/identity_api.h",
       "extensions/cast_display_info_provider.cc",
@@ -239,6 +241,7 @@
 
     deps += [
       "//chromecast/common/extensions_api:api_registration_bundle_generator_registration",
+      "//chromecast/common/extensions_api:api_schema_generator",
       "//components/guest_view/browser",
       "//components/keyed_service/content",
       "//components/pref_registry",
diff --git a/chromecast/browser/extensions/api/bookmarks/bookmarks_api.cc b/chromecast/browser/extensions/api/bookmarks/bookmarks_api.cc
new file mode 100644
index 0000000..5da96ab
--- /dev/null
+++ b/chromecast/browser/extensions/api/bookmarks/bookmarks_api.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/browser/extensions/api/bookmarks/bookmarks_api.h"
+
+namespace extensions {
+namespace cast {
+namespace api {
+
+BookmarksFunction::~BookmarksFunction() {}
+
+void BookmarksFunction::Destruct() const {
+  delete this;
+}
+
+BookmarksStubFunction::~BookmarksStubFunction() {}
+
+ExtensionFunction::ResponseAction BookmarksStubFunction::Run() {
+  return RespondNow(Error("Not implemented"));
+}
+
+ExtensionFunction::ResponseAction BookmarksGetFunction::Run() {
+  return RespondNow(ArgumentList(cast::api::bookmarks::Get::Results::Create(
+      std::vector<cast::api::bookmarks::BookmarkTreeNode>())));
+}
+
+ExtensionFunction::ResponseAction BookmarksGetChildrenFunction::Run() {
+  return RespondNow(
+      ArgumentList(cast::api::bookmarks::GetChildren::Results::Create(
+          std::vector<cast::api::bookmarks::BookmarkTreeNode>())));
+}
+
+ExtensionFunction::ResponseAction BookmarksGetRecentFunction::Run() {
+  return RespondNow(
+      ArgumentList(cast::api::bookmarks::GetRecent::Results::Create(
+          std::vector<cast::api::bookmarks::BookmarkTreeNode>())));
+}
+
+ExtensionFunction::ResponseAction BookmarksGetTreeFunction::Run() {
+  return RespondNow(ArgumentList(cast::api::bookmarks::GetTree::Results::Create(
+      std::vector<cast::api::bookmarks::BookmarkTreeNode>())));
+}
+
+ExtensionFunction::ResponseAction BookmarksGetSubTreeFunction::Run() {
+  return RespondNow(
+      ArgumentList(cast::api::bookmarks::GetSubTree::Results::Create(
+          std::vector<cast::api::bookmarks::BookmarkTreeNode>())));
+}
+
+ExtensionFunction::ResponseAction BookmarksSearchFunction::Run() {
+  return RespondNow(ArgumentList(cast::api::bookmarks::Search::Results::Create(
+      std::vector<cast::api::bookmarks::BookmarkTreeNode>())));
+}
+
+}  // namespace api
+}  // namespace cast
+}  // namespace extensions
diff --git a/chromecast/browser/extensions/api/bookmarks/bookmarks_api.h b/chromecast/browser/extensions/api/bookmarks/bookmarks_api.h
new file mode 100644
index 0000000..a18c21fe
--- /dev/null
+++ b/chromecast/browser/extensions/api/bookmarks/bookmarks_api.h
@@ -0,0 +1,153 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BROWSER_EXTENSIONS_API_BOOKMARKS_BOOKMARKS_API_H_
+#define CHROMECAST_BROWSER_EXTENSIONS_API_BOOKMARKS_BOOKMARKS_API_H_
+
+#include <vector>
+
+#include "chromecast/common/extensions_api/bookmarks.h"
+#include "extensions/browser/extension_function.h"
+
+namespace extensions {
+namespace cast {
+namespace api {
+
+class BookmarksFunction : public ExtensionFunction {
+ public:
+  void Destruct() const override;
+
+ protected:
+  ~BookmarksFunction() override;
+};
+
+class BookmarksStubFunction : public BookmarksFunction {
+ public:
+  ResponseAction Run() override;
+
+ protected:
+  ~BookmarksStubFunction() override;
+};
+
+class BookmarksGetFunction : public BookmarksFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.get", BOOKMARKS_GET)
+
+ protected:
+  ~BookmarksGetFunction() override {}
+
+  ResponseAction Run() override;
+};
+
+class BookmarksGetChildrenFunction : public BookmarksFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.getChildren", BOOKMARKS_GETCHILDREN)
+
+ protected:
+  ~BookmarksGetChildrenFunction() override {}
+
+  ResponseAction Run() override;
+};
+
+class BookmarksGetRecentFunction : public BookmarksFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.getRecent", BOOKMARKS_GETRECENT)
+
+ protected:
+  ~BookmarksGetRecentFunction() override {}
+
+  ResponseAction Run() override;
+};
+
+class BookmarksGetTreeFunction : public BookmarksFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.getTree", BOOKMARKS_GETTREE)
+
+ protected:
+  ~BookmarksGetTreeFunction() override {}
+
+  ResponseAction Run() override;
+};
+
+class BookmarksGetSubTreeFunction : public BookmarksFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.getSubTree", BOOKMARKS_GETSUBTREE)
+
+ protected:
+  ~BookmarksGetSubTreeFunction() override {}
+
+  ResponseAction Run() override;
+};
+
+class BookmarksSearchFunction : public BookmarksFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.search", BOOKMARKS_SEARCH)
+
+ protected:
+  ~BookmarksSearchFunction() override {}
+
+  ResponseAction Run() override;
+};
+
+class BookmarksRemoveFunction : public BookmarksStubFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.remove", BOOKMARKS_REMOVE);
+
+ protected:
+  ~BookmarksRemoveFunction() override {}
+};
+
+class BookmarksRemoveTreeFunction : public BookmarksStubFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.removeTree", BOOKMARKS_REMOVETREE)
+
+ protected:
+  ~BookmarksRemoveTreeFunction() override {}
+};
+
+class BookmarksCreateFunction : public BookmarksStubFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.create", BOOKMARKS_CREATE)
+
+ protected:
+  ~BookmarksCreateFunction() override {}
+};
+
+class BookmarksMoveFunction : public BookmarksStubFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.move", BOOKMARKS_MOVE)
+
+ protected:
+  ~BookmarksMoveFunction() override {}
+};
+
+class BookmarksUpdateFunction : public BookmarksStubFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.update", BOOKMARKS_UPDATE)
+
+ protected:
+  ~BookmarksUpdateFunction() override {}
+};
+
+class BookmarksImportFunction : public BookmarksStubFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.import", BOOKMARKS_IMPORT)
+
+ private:
+  ~BookmarksImportFunction() override {}
+};
+
+class BookmarksExportFunction : public BookmarksStubFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("bookmarks.export", BOOKMARKS_EXPORT)
+
+ private:
+  ~BookmarksExportFunction() override {}
+};
+
+}  // namespace api
+}  // namespace cast
+}  // namespace extensions
+
+#endif  // CHROMECAST_BROWSER_EXTENSIONS_API_BOOKMARKS_BOOKMARKS_API_H_
diff --git a/chromecast/common/BUILD.gn b/chromecast/common/BUILD.gn
index 08f2e6f..8540444 100644
--- a/chromecast/common/BUILD.gn
+++ b/chromecast/common/BUILD.gn
@@ -35,6 +35,7 @@
     deps += [
       "//chromecast/common/extensions_api:api",
       "//chromecast/common/extensions_api:api_registration",
+      "//chromecast/common/extensions_api:api_schema_generator",
       "//chromecast/common/extensions_api:extensions_features",
       "//components/version_info",
       "//extensions:extensions_resources_grd_grit",
diff --git a/chromecast/common/extensions_api/BUILD.gn b/chromecast/common/extensions_api/BUILD.gn
index 3d8d2f7..f5ea3fc 100644
--- a/chromecast/common/extensions_api/BUILD.gn
+++ b/chromecast/common/extensions_api/BUILD.gn
@@ -9,7 +9,10 @@
 assert(enable_extensions,
        "Cannot depend on extensions because enable_extensions=false.")
 
-schema_sources = [ "identity.idl" ]
+schema_sources = [
+  "bookmarks.json",
+  "identity.idl",
+]
 root_namespace = "extensions::cast::api::%(namespace)s"
 
 json_schema_api("api") {
@@ -45,6 +48,7 @@
   provider_class = "CastPermissionFeatureProvider"
   sources = [
     "../../../extensions/common/api/_permission_features.json",
+    "_permission_features.json",
   ]
 }
 
diff --git a/chromecast/common/extensions_api/_api_features.json b/chromecast/common/extensions_api/_api_features.json
index ed747fa..7b672c49 100644
--- a/chromecast/common/extensions_api/_api_features.json
+++ b/chromecast/common/extensions_api/_api_features.json
@@ -18,5 +18,16 @@
     "channel": "dev",
     "contexts": ["blessed_extension"],
     "extension_types": ["platform_app"]
-  }
+  },
+  "bookmarks": [{
+    "dependencies": ["permission:bookmarks"],
+    "contexts": ["blessed_extension"],
+    "default_parent": true
+  }, {
+    "channel": "stable",
+    "contexts": ["webui"],
+    "matches": [
+      "chrome://bookmarks/*"
+    ]
+  }]
 }
diff --git a/chromecast/common/extensions_api/_permission_features.json b/chromecast/common/extensions_api/_permission_features.json
new file mode 100644
index 0000000..7542497c
--- /dev/null
+++ b/chromecast/common/extensions_api/_permission_features.json
@@ -0,0 +1,10 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+{
+  "bookmarks": {
+    "channel": "stable",
+    "extension_types": ["extension", "legacy_packaged_app"]
+  }
+}
diff --git a/chromecast/common/extensions_api/bookmarks.json b/chromecast/common/extensions_api/bookmarks.json
new file mode 100644
index 0000000..bc7d8ff
--- /dev/null
+++ b/chromecast/common/extensions_api/bookmarks.json
@@ -0,0 +1,560 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+[
+  {
+    "namespace": "bookmarks",
+    "description": "Use the <code>chrome.bookmarks</code> API to create, organize, and otherwise manipulate bookmarks. Also see <a href='override'>Override Pages</a>, which you can use to create a custom Bookmark Manager page.",
+    "properties": {
+      "MAX_WRITE_OPERATIONS_PER_HOUR": {
+        "value": 1000000,
+        "deprecated": "Bookmark write operations are no longer limited by Chrome.",
+        "description": ""
+      },
+      "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE": {
+        "value": 1000000,
+        "deprecated": "Bookmark write operations are no longer limited by Chrome.",
+        "description": ""
+      }
+    },
+    "types": [
+      {
+        "id": "BookmarkTreeNodeUnmodifiable",
+        "type": "string",
+        "enum": ["managed"],
+        "description": "Indicates the reason why this node is unmodifiable. The <var>managed</var> value indicates that this node was configured by the system administrator. Omitted if the node can be modified by the user and the extension (default)."
+      },
+      {
+        "id": "BookmarkTreeNode",
+        "type": "object",
+        "description": "A node (either a bookmark or a folder) in the bookmark tree.  Child nodes are ordered within their parent folder.",
+        "properties": {
+          "id": {
+            "type": "string",
+            "minimum": 0,
+            "description": "The unique identifier for the node. IDs are unique within the current profile, and they remain valid even after the browser is restarted."
+          },
+          "parentId": {
+            "type": "string",
+            "minimum": 0,
+            "optional": true,
+            "description": "The <code>id</code> of the parent folder.  Omitted for the root node."
+          },
+          "index": {
+            "type": "integer",
+            "optional": true,
+            "description": "The 0-based position of this node within its parent folder."
+          },
+          "url": {
+            "type": "string",
+            "optional": true,
+            "description": "The URL navigated to when a user clicks the bookmark. Omitted for folders."
+          },
+          "title": {
+            "type": "string",
+            "description": "The text displayed for the node."
+          },
+          "dateAdded": {
+            "type": "number",
+            "optional": true,
+            "description": "When this node was created, in milliseconds since the epoch (<code>new Date(dateAdded)</code>)."
+          },
+          "dateGroupModified": {
+            "type": "number",
+            "optional": true,
+            "description": "When the contents of this folder last changed, in milliseconds since the epoch."
+          },
+          "unmodifiable": {
+            "$ref": "BookmarkTreeNodeUnmodifiable",
+            "optional": true,
+            "description": "Indicates the reason why this node is unmodifiable. The <var>managed</var> value indicates that this node was configured by the system administrator or by the custodian of a supervised user. Omitted if the node can be modified by the user and the extension (default)."
+          },
+          "children": {
+            "type": "array",
+            "optional": true,
+            "items": { "$ref": "BookmarkTreeNode" },
+            "description": "An ordered list of children of this node."
+          }
+        }
+      },
+      {
+        "id": "CreateDetails",
+        "description": "Object passed to the create() function.",
+        "inline_doc": true,
+        "type": "object",
+        "properties": {
+          "parentId": {
+            "type": "string",
+            "serialized_type": "int64",
+            "optional": true,
+            "description": "Defaults to the Other Bookmarks folder."
+          },
+          "index": {
+            "type": "integer",
+            "minimum": 0,
+            "optional": true
+          },
+          "title": {
+            "type": "string",
+            "optional": true
+          },
+          "url": {
+            "type": "string",
+            "optional": true
+          }
+        }
+      }
+    ],
+    "functions": [
+      {
+        "name": "get",
+        "type": "function",
+        "description": "Retrieves the specified BookmarkTreeNode(s).",
+        "parameters": [
+          {
+            "name": "idOrIdList",
+            "description": "A single string-valued id, or an array of string-valued ids",
+            "choices": [
+              {
+                "type": "string",
+                "serialized_type": "int64"
+              },
+              {
+                "type": "array",
+                "items": {
+                  "type": "string",
+                  "serialized_type": "int64"
+                },
+                "minItems": 1
+              }
+            ]
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "results",
+                "type": "array",
+                "items": { "$ref": "BookmarkTreeNode" }
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "getChildren",
+        "type": "function",
+        "description": "Retrieves the children of the specified BookmarkTreeNode id.",
+        "parameters": [
+          {
+            "type": "string",
+            "serialized_type": "int64",
+            "name": "id"
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "results",
+                "type": "array",
+                "items": { "$ref": "BookmarkTreeNode"}
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "getRecent",
+        "type": "function",
+        "description": "Retrieves the recently added bookmarks.",
+        "parameters": [
+          {
+            "type": "integer",
+            "minimum": 1,
+            "name": "numberOfItems",
+            "description": "The maximum number of items to return."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "results",
+                "type": "array",
+                "items": { "$ref": "BookmarkTreeNode" }
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "getTree",
+        "type": "function",
+        "description": "Retrieves the entire Bookmarks hierarchy.",
+        "parameters": [
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "results",
+                "type": "array",
+                "items": { "$ref": "BookmarkTreeNode" }
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "getSubTree",
+        "type": "function",
+        "description": "Retrieves part of the Bookmarks hierarchy, starting at the specified node.",
+        "parameters": [
+          {
+            "type": "string",
+            "serialized_type": "int64",
+            "name": "id",
+            "description": "The ID of the root of the subtree to retrieve."
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "results",
+                "type": "array",
+                "items": { "$ref": "BookmarkTreeNode" }
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "search",
+        "type": "function",
+        "description": "Searches for BookmarkTreeNodes matching the given query. Queries specified with an object produce BookmarkTreeNodes matching all specified properties.",
+        "parameters": [
+          {
+            "name": "query",
+            "description": "Either a string of words and quoted phrases that are matched against bookmark URLs and titles, or an object. If an object, the properties <code>query</code>, <code>url</code>, and <code>title</code> may be specified and bookmarks matching all specified properties will be produced.",
+            "choices": [
+              {
+                "type": "string",
+                "description": "A string of words and quoted phrases that are matched against bookmark URLs and titles."
+              },
+              {
+                "type": "object",
+                "description": "An object specifying properties and values to match when searching. Produces bookmarks matching all properties.",
+                "properties": {
+                  "query": {
+                    "type": "string",
+                    "optional": true,
+                    "description": "A string of words and quoted phrases that are matched against bookmark URLs and titles."
+                  },
+                  "url": {
+                    "type": "string",
+                    "optional": true,
+                    "description": "The URL of the bookmark; matches verbatim. Note that folders have no URL."
+                  },
+                  "title": {
+                    "type": "string",
+                    "optional": true,
+                    "description": "The title of the bookmark; matches verbatim."
+                  }
+                }
+              }
+            ]
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "parameters": [
+              {
+                "name": "results",
+                "type": "array",
+                "items": { "$ref": "BookmarkTreeNode" }
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "create",
+        "type": "function",
+        "description": "Creates a bookmark or folder under the specified parentId.  If url is NULL or missing, it will be a folder.",
+        "parameters": [
+          {
+            "$ref": "CreateDetails",
+            "name": "bookmark"
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "result",
+                "$ref": "BookmarkTreeNode"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "move",
+        "type": "function",
+        "description": "Moves the specified BookmarkTreeNode to the provided location.",
+        "parameters": [
+          {
+            "type": "string",
+            "serialized_type": "int64",
+            "name": "id"
+          },
+          {
+            "type": "object",
+            "name": "destination",
+            "properties": {
+              "parentId": {
+                "type": "string",
+                "optional": true
+              },
+              "index": {
+                "type": "integer",
+                "minimum": 0,
+                "optional": true
+              }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "result",
+                "$ref": "BookmarkTreeNode"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "update",
+        "type": "function",
+        "description": "Updates the properties of a bookmark or folder. Specify only the properties that you want to change; unspecified properties will be left unchanged.  <b>Note:</b> Currently, only 'title' and 'url' are supported.",
+        "parameters": [
+          {
+            "type": "string",
+            "serialized_type": "int64",
+            "name": "id"
+          },
+          {
+            "type": "object",
+            "name": "changes",
+            "properties": {
+              "title": {
+                "type": "string",
+                "optional": true
+              },
+              "url": {
+                "type": "string",
+                "optional": true
+              }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": [
+              {
+                "name": "result",
+                "$ref": "BookmarkTreeNode"
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "name": "remove",
+        "type": "function",
+        "description": "Removes a bookmark or an empty bookmark folder.",
+        "parameters": [
+          {
+            "type": "string",
+            "serialized_type": "int64",
+            "name": "id"
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "removeTree",
+        "type": "function",
+        "description": "Recursively removes a bookmark folder.",
+        "parameters": [
+          {
+            "type": "string",
+            "serialized_type": "int64",
+            "name": "id"
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "import",
+        "type": "function",
+        "description": "Imports bookmarks from a chrome html bookmark file",
+        "nodoc": "true",
+        "parameters": [
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      },
+      {
+        "name": "export",
+        "type": "function",
+        "description": "Exports bookmarks to a chrome html bookmark file",
+        "nodoc": "true",
+        "parameters": [
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "parameters": []
+          }
+        ]
+      }
+    ],
+    "events": [
+      {
+        "name": "onCreated",
+        "type": "function",
+        "description": "Fired when a bookmark or folder is created.",
+        "parameters": [
+          {
+            "type": "string",
+            "name": "id"
+          },
+          {
+            "$ref": "BookmarkTreeNode",
+            "name": "bookmark"
+          }
+        ]
+      },
+      {
+        "name": "onRemoved",
+        "type": "function",
+        "description": "Fired when a bookmark or folder is removed.  When a folder is removed recursively, a single notification is fired for the folder, and none for its contents.",
+        "parameters": [
+          {
+            "type": "string",
+            "name": "id"
+          },
+          {
+            "type": "object",
+            "name": "removeInfo",
+            "properties": {
+              "parentId": { "type": "string" },
+              "index": { "type": "integer" },
+              "node": { "$ref": "BookmarkTreeNode" }
+            }
+          }
+        ]
+      },
+      {
+        "name": "onChanged",
+        "type": "function",
+        "description": "Fired when a bookmark or folder changes.  <b>Note:</b> Currently, only title and url changes trigger this.",
+        "parameters": [
+          {
+            "type": "string",
+            "name": "id"
+          },
+          {
+            "type": "object",
+            "name": "changeInfo",
+            "properties": {
+              "title": { "type": "string" },
+              "url": {
+                "type": "string",
+                "optional": true
+              }
+            }
+          }
+        ]
+      },
+      {
+        "name": "onMoved",
+        "type": "function",
+        "description": "Fired when a bookmark or folder is moved to a different parent folder.",
+        "parameters": [
+          {
+            "type": "string",
+            "name": "id"
+          },
+          {
+            "type": "object",
+            "name": "moveInfo",
+            "properties": {
+              "parentId": { "type": "string" },
+              "index": { "type": "integer" },
+              "oldParentId": { "type": "string" },
+              "oldIndex": { "type": "integer" }
+            }
+          }
+        ]
+      },
+      {
+        "name": "onChildrenReordered",
+        "type": "function",
+        "description": "Fired when the children of a folder have changed their order due to the order being sorted in the UI.  This is not called as a result of a move().",
+        "parameters": [
+          {
+            "type": "string",
+            "name": "id"
+          },
+          {
+            "type": "object",
+            "name": "reorderInfo",
+            "properties": {
+              "childIds": {
+                "type": "array",
+                "items": { "type": "string" }
+              }
+            }
+          }
+        ]
+      },
+      {
+        "name": "onImportBegan",
+        "type": "function",
+        "description": "Fired when a bookmark import session is begun.  Expensive observers should ignore onCreated updates until onImportEnded is fired.  Observers should still handle other notifications immediately.",
+        "parameters": []
+      },
+      {
+        "name": "onImportEnded",
+        "type": "function",
+        "description": "Fired when a bookmark import session is ended.",
+        "parameters": []
+      }
+    ]
+  }
+]
diff --git a/chromecast/media/cma/backend/video/av_sync_video.cc b/chromecast/media/cma/backend/video/av_sync_video.cc
index 0cbacd31..a759bc2 100644
--- a/chromecast/media/cma/backend/video/av_sync_video.cc
+++ b/chromecast/media/cma/backend/video/av_sync_video.cc
@@ -38,6 +38,10 @@
 constexpr base::TimeDelta kAvSyncUpkeepInterval =
     base::TimeDelta::FromMilliseconds(10);
 
+// Time interval between checking playbacks statistics.
+constexpr base::TimeDelta kPlaybackStatisticsCheckInterval =
+    base::TimeDelta::FromSeconds(1);
+
 // When we're in sync (i.e. the apts and vpts difference is
 // < kSoftCorrectionThresholdUs), if the apts and vpts slopes are different by
 // this threshold, we'll reset the video playback rate to be equal to the apts
@@ -190,6 +194,48 @@
   }
 }
 
+void AvSyncVideo::GatherPlaybackStatistics() {
+  int64_t frame_rate_difference =
+      (backend_->video_decoder()->GetCurrentContentRefreshRate() -
+       backend_->video_decoder()->GetOutputRefreshRate()) /
+      1000;
+
+  int64_t expected_dropped_frames_per_second =
+      std::max<int64_t>(frame_rate_difference, 0);
+
+  int64_t expected_repeated_frames_per_second =
+      std::max<int64_t>(-frame_rate_difference, 0);
+
+  int64_t current_time = backend_->MonotonicClockNow();
+  int64_t expected_dropped_frames =
+      std::round(expected_dropped_frames_per_second *
+                 (current_time - last_gather_timestamp_us_) / 1000000);
+
+  int64_t expected_repeated_frames =
+      std::round(expected_repeated_frames_per_second *
+                 (current_time - last_gather_timestamp_us_) / 1000000);
+
+  int64_t dropped_frames = backend_->video_decoder()->GetDroppedFrames();
+  int64_t repeated_frames = backend_->video_decoder()->GetRepeatedFrames();
+
+  int64_t unexpected_dropped_frames =
+      (dropped_frames - last_dropped_frames_) - expected_dropped_frames;
+  int64_t unexpected_repeated_frames =
+      (repeated_frames - last_repeated_frames_) - expected_repeated_frames;
+
+  VLOG_IF(2, unexpected_dropped_frames != 0 || unexpected_repeated_frames != 0)
+      << "Playback diagnostics:"
+      << " CurrentContentRefreshRate="
+      << backend_->video_decoder()->GetCurrentContentRefreshRate()
+      << " OutputRefreshRate="
+      << backend_->video_decoder()->GetOutputRefreshRate()
+      << " unexpected_dropped_frames=" << unexpected_dropped_frames
+      << " unexpected_repeated_frames=" << unexpected_repeated_frames;
+  last_gather_timestamp_us_ = current_time;
+  last_repeated_frames_ = repeated_frames;
+  last_dropped_frames_ = dropped_frames;
+}
+
 void AvSyncVideo::StopAvSync() {
   audio_pts_.reset(
       new WeightedMovingLinearRegression(kLinearRegressionDataLifetimeUs));
@@ -197,12 +243,12 @@
       new WeightedMovingLinearRegression(kLinearRegressionDataLifetimeUs));
   error_.reset(
       new WeightedMovingLinearRegression(kLinearRegressionDataLifetimeUs));
-  timer_.Stop();
+  upkeep_av_sync_timer_.Stop();
+  playback_statistics_timer_.Stop();
 }
 
 void AvSyncVideo::NotifyStart() {
-  timer_.Start(FROM_HERE, kAvSyncUpkeepInterval, this,
-               &AvSyncVideo::UpkeepAvSync);
+  StartAvSync();
 }
 
 void AvSyncVideo::NotifyStop() {
@@ -215,8 +261,19 @@
 }
 
 void AvSyncVideo::NotifyResume() {
-  timer_.Start(FROM_HERE, kAvSyncUpkeepInterval, this,
-               &AvSyncVideo::UpkeepAvSync);
+  StartAvSync();
+}
+
+void AvSyncVideo::StartAvSync() {
+  upkeep_av_sync_timer_.Start(FROM_HERE, kAvSyncUpkeepInterval, this,
+                              &AvSyncVideo::UpkeepAvSync);
+#if DCHECK_IS_ON()
+  // TODO(almasrymina): if this logic turns out to be useful for metrics
+  // recording, keep it and remove this TODO. Otherwise remove it.
+  playback_statistics_timer_.Start(FROM_HERE, kPlaybackStatisticsCheckInterval,
+                                   this,
+                                   &AvSyncVideo::GatherPlaybackStatistics);
+#endif
 }
 
 AvSyncVideo::~AvSyncVideo() = default;
diff --git a/chromecast/media/cma/backend/video/av_sync_video.h b/chromecast/media/cma/backend/video/av_sync_video.h
index 17b9630..8fcc105 100644
--- a/chromecast/media/cma/backend/video/av_sync_video.h
+++ b/chromecast/media/cma/backend/video/av_sync_video.h
@@ -39,9 +39,12 @@
 
  private:
   void UpkeepAvSync();
+  void StartAvSync();
   void StopAvSync();
+  void GatherPlaybackStatistics();
 
-  base::RepeatingTimer timer_;
+  base::RepeatingTimer upkeep_av_sync_timer_;
+  base::RepeatingTimer playback_statistics_timer_;
   bool setup_video_clock_ = false;
 
   // TODO(almasrymina): having a linear regression for the audio pts is
@@ -54,6 +57,10 @@
   std::unique_ptr<WeightedMovingLinearRegression> error_;
   double current_video_playback_rate_ = 1.0;
 
+  int64_t last_gather_timestamp_us_ = 0;
+  int64_t last_repeated_frames_ = 0;
+  int64_t last_dropped_frames_ = 0;
+
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   MediaPipelineBackendForMixer* const backend_;
diff --git a/chromecast/media/cma/backend/video_decoder_for_mixer.h b/chromecast/media/cma/backend/video_decoder_for_mixer.h
index d600245..53c966b 100644
--- a/chromecast/media/cma/backend/video_decoder_for_mixer.h
+++ b/chromecast/media/cma/backend/video_decoder_for_mixer.h
@@ -52,7 +52,7 @@
   // playback rate prior to pausing.
   virtual bool Resume() = 0;
 
-  // Get the current video PTS. This will typically be the pts of the last
+  // Returns the current video PTS. This will typically be the pts of the last
   // video frame displayed.
   virtual int64_t GetCurrentPts() const = 0;
 
@@ -72,6 +72,32 @@
   // Implementation is encouraged to smooth out this transition, such that
   // minimal jitter in the video is shown, but that is not necessary.
   virtual bool SetCurrentPts(int64_t pts) = 0;
+
+  // Returns number of frames dropped since the last call to Start(). This is
+  // used to estimate video playback smoothness.
+  // This is different from VideoDecoder::Statistics::dropped_frames. That
+  // value is the number of *decoded* dropped frames. The value returned here
+  // must be the total number of dropped frames, whether the frames have been
+  // decoded or not.
+  virtual int64_t GetDroppedFrames() = 0;
+
+  // Returns number of frames repeated since the last call to Start(). This is
+  // used to estimate video playback smoothness. Note that repeated frames could
+  // be due to changes in the rate of playback, setting the PTS, or simply due
+  // to frame rate conversion. This number should be the sum of all of these
+  // factors.
+  //
+  // For example, if the current OutputRefreshRate is 60hz, and the current
+  // content frame rate is 24fps, it is expected to repeat 36fps.
+  virtual int64_t GetRepeatedFrames() = 0;
+
+  // Returns the output refresh rate on this platform, in mHz (millihertz). On
+  // display devices, this will be the display refresh rate. On HDMI devices,
+  // this will be the refresh rate of the HDMI connection.
+  virtual int64_t GetOutputRefreshRate() = 0;
+
+  // Returns the current content refresh rate in mHz (millihertz).
+  virtual int64_t GetCurrentContentRefreshRate() = 0;
 };
 
 }  // namespace media
diff --git a/chromecast/media/cma/backend/video_decoder_null.cc b/chromecast/media/cma/backend/video_decoder_null.cc
index ac154c9f..0d83c82 100644
--- a/chromecast/media/cma/backend/video_decoder_null.cc
+++ b/chromecast/media/cma/backend/video_decoder_null.cc
@@ -80,5 +80,21 @@
   return true;
 }
 
+int64_t VideoDecoderNull::GetDroppedFrames() {
+  return 0;
+}
+
+int64_t VideoDecoderNull::GetRepeatedFrames() {
+  return 0;
+}
+
+int64_t VideoDecoderNull::GetOutputRefreshRate() {
+  return 0;
+}
+
+int64_t VideoDecoderNull::GetCurrentContentRefreshRate() {
+  return 0;
+}
+
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/media/cma/backend/video_decoder_null.h b/chromecast/media/cma/backend/video_decoder_null.h
index dda9382..2f339a2 100644
--- a/chromecast/media/cma/backend/video_decoder_null.h
+++ b/chromecast/media/cma/backend/video_decoder_null.h
@@ -34,6 +34,10 @@
   int64_t GetCurrentPts() const override;
   bool SetPlaybackRate(float rate) override;
   bool SetCurrentPts(int64_t pts) override;
+  int64_t GetDroppedFrames() override;
+  int64_t GetRepeatedFrames() override;
+  int64_t GetOutputRefreshRate() override;
+  int64_t GetCurrentContentRefreshRate() override;
 
  private:
   void OnEndOfStream();
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index 91c0ba8..59b0825 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -54,7 +54,7 @@
       "//libassistant/contrib/platform/net",
       "//libassistant/contrib/platform/resources",
       "//libassistant/contrib/platform/system",
-      "//libassistant/internal/assistant/controller:libassistant_static",
+      "//libassistant/internal/assistant/controller:libassistant",
       "//libassistant/shared/internal_api/c:api_wrappers_for_caller_no_chromium",
       "//libassistant/shared/proto:assistant_proto",
       "//libassistant/shared/public",
diff --git a/chromeos/services/assistant/platform/audio_input_provider_impl.cc b/chromeos/services/assistant/platform/audio_input_provider_impl.cc
index ad72192a6..af40c9e 100644
--- a/chromeos/services/assistant/platform/audio_input_provider_impl.cc
+++ b/chromeos/services/assistant/platform/audio_input_provider_impl.cc
@@ -133,5 +133,10 @@
   return audio_input_;
 }
 
+int64_t AudioInputProviderImpl::GetCurrentAudioTime() {
+  // TODO(xiaohuic): see if we can support real timestamp.
+  return 0;
+}
+
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_input_provider_impl.h b/chromeos/services/assistant/platform/audio_input_provider_impl.h
index b9453e3c..86598d3 100644
--- a/chromeos/services/assistant/platform/audio_input_provider_impl.h
+++ b/chromeos/services/assistant/platform/audio_input_provider_impl.h
@@ -73,8 +73,9 @@
   // assistant_client::AudioInputProvider overrides:
   assistant_client::AudioInputConfig& GetAudioInputConfig() override;
   assistant_client::AudioInput& GetAudioInput() override;
-
+  // Assumes no config would change.
   void RegisterConfigChangeCallback(ConfigChangeCallback callback) override {}
+  int64_t GetCurrentAudioTime() override;
 
  private:
   AudioInputConfigImpl audio_input_config_;
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerImpl.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerImpl.java
index 082a047..0928f3ca 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerImpl.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerImpl.java
@@ -31,76 +31,78 @@
 
     @Override
     public boolean schedule(Context context, TaskInfo taskInfo) {
-        TraceEvent.begin(
-                "BackgroundTaskScheduler.schedule", Integer.toString(taskInfo.getTaskId()));
-        ThreadUtils.assertOnUiThread();
-        boolean success = mSchedulerDelegate.schedule(context, taskInfo);
-        BackgroundTaskSchedulerUma.getInstance().reportTaskScheduled(taskInfo.getTaskId(), success);
-        if (success) {
-            BackgroundTaskSchedulerPrefs.addScheduledTask(taskInfo);
+        try (TraceEvent te = TraceEvent.scoped(
+                     "BackgroundTaskScheduler.schedule", Integer.toString(taskInfo.getTaskId()))) {
+            ThreadUtils.assertOnUiThread();
+            boolean success = mSchedulerDelegate.schedule(context, taskInfo);
+            BackgroundTaskSchedulerUma.getInstance().reportTaskScheduled(
+                    taskInfo.getTaskId(), success);
+            if (success) {
+                BackgroundTaskSchedulerPrefs.addScheduledTask(taskInfo);
+            }
+            return success;
         }
-        TraceEvent.end("BackgroundTaskScheduler.schedule");
-        return success;
     }
 
     @Override
     public void cancel(Context context, int taskId) {
-        TraceEvent.begin("BackgroundTaskScheduler.cancel", Integer.toString(taskId));
-        ThreadUtils.assertOnUiThread();
-        BackgroundTaskSchedulerUma.getInstance().reportTaskCanceled(taskId);
-        BackgroundTaskSchedulerPrefs.removeScheduledTask(taskId);
-        mSchedulerDelegate.cancel(context, taskId);
-        TraceEvent.end("BackgroundTaskScheduler.cancel");
+        try (TraceEvent te = TraceEvent.scoped(
+                     "BackgroundTaskScheduler.cancel", Integer.toString(taskId))) {
+            ThreadUtils.assertOnUiThread();
+            BackgroundTaskSchedulerUma.getInstance().reportTaskCanceled(taskId);
+            BackgroundTaskSchedulerPrefs.removeScheduledTask(taskId);
+            mSchedulerDelegate.cancel(context, taskId);
+        }
     }
 
     @Override
     public void checkForOSUpgrade(Context context) {
-        TraceEvent.begin("BackgroundTaskScheduler.checkForOSUpgrade");
-        int oldSdkInt = BackgroundTaskSchedulerPrefs.getLastSdkVersion();
-        int newSdkInt = Build.VERSION.SDK_INT;
+        try (TraceEvent te = TraceEvent.scoped("BackgroundTaskScheduler.checkForOSUpgrade")) {
+            int oldSdkInt = BackgroundTaskSchedulerPrefs.getLastSdkVersion();
+            int newSdkInt = Build.VERSION.SDK_INT;
 
-        if (oldSdkInt != newSdkInt) {
-            // Save the current SDK version to preferences.
-            BackgroundTaskSchedulerPrefs.setLastSdkVersion(newSdkInt);
+            if (oldSdkInt != newSdkInt) {
+                // Save the current SDK version to preferences.
+                BackgroundTaskSchedulerPrefs.setLastSdkVersion(newSdkInt);
+            }
+
+            // No OS upgrade detected or OS upgrade does not change delegate.
+            if (oldSdkInt == newSdkInt || !osUpgradeChangesDelegateType(oldSdkInt, newSdkInt)) {
+                BackgroundTaskSchedulerUma.getInstance().flushStats();
+                return;
+            }
+
+            BackgroundTaskSchedulerUma.getInstance().removeCachedStats();
+
+            // Explicitly create and invoke old delegate type to cancel all scheduled tasks.
+            // All preference entries are kept until reschedule call, which removes then then.
+            BackgroundTaskSchedulerDelegate oldDelegate =
+                    BackgroundTaskSchedulerFactory.getSchedulerDelegateForSdk(oldSdkInt);
+            Set<Integer> scheduledTaskIds = BackgroundTaskSchedulerPrefs.getScheduledTaskIds();
+            for (int taskId : scheduledTaskIds) {
+                oldDelegate.cancel(context, taskId);
+            }
+
+            reschedule(context);
         }
-
-        // No OS upgrade detected or OS upgrade does not change delegate.
-        if (oldSdkInt == newSdkInt || !osUpgradeChangesDelegateType(oldSdkInt, newSdkInt)) {
-            BackgroundTaskSchedulerUma.getInstance().flushStats();
-            return;
-        }
-
-        BackgroundTaskSchedulerUma.getInstance().removeCachedStats();
-
-        // Explicitly create and invoke old delegate type to cancel all scheduled tasks.
-        // All preference entries are kept until reschedule call, which removes then then.
-        BackgroundTaskSchedulerDelegate oldDelegate =
-                BackgroundTaskSchedulerFactory.getSchedulerDelegateForSdk(oldSdkInt);
-        Set<Integer> scheduledTaskIds = BackgroundTaskSchedulerPrefs.getScheduledTaskIds();
-        for (int taskId : scheduledTaskIds) {
-            oldDelegate.cancel(context, taskId);
-        }
-
-        reschedule(context);
-        TraceEvent.end("BackgroundTaskScheduler.checkForOSUpgrade");
     }
 
     @Override
     public void reschedule(Context context) {
-        TraceEvent.begin("BackgroundTaskScheduler.reschedule");
-        Set<String> scheduledTasksClassNames = BackgroundTaskSchedulerPrefs.getScheduledTasks();
-        BackgroundTaskSchedulerPrefs.removeAllTasks();
-        for (String className : scheduledTasksClassNames) {
-            BackgroundTask task =
-                    BackgroundTaskReflection.getBackgroundTaskFromClassName(className);
-            if (task == null) {
-                Log.w(TAG, "Cannot reschedule task for: " + className);
-                continue;
-            }
+        try (TraceEvent te = TraceEvent.scoped("BackgroundTaskScheduler.reschedule")) {
+            Set<String> scheduledTasksClassNames = BackgroundTaskSchedulerPrefs.getScheduledTasks();
+            BackgroundTaskSchedulerPrefs.removeAllTasks();
+            for (String className : scheduledTasksClassNames) {
+                BackgroundTask task =
+                        BackgroundTaskReflection.getBackgroundTaskFromClassName(className);
+                if (task == null) {
+                    Log.w(TAG, "Cannot reschedule task for: " + className);
+                    continue;
+                }
 
-            task.reschedule(context);
+                task.reschedule(context);
+            }
         }
-        TraceEvent.end("BackgroundTaskScheduler.reschedule");
     }
 
     private boolean osUpgradeChangesDelegateType(int oldSdkInt, int newSdkInt) {
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefs.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefs.java
index adae668..607d5b3c 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefs.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefs.java
@@ -85,106 +85,108 @@
 
     /** Adds a task to scheduler's preferences, so that it can be rescheduled with OS upgrade. */
     public static void addScheduledTask(TaskInfo taskInfo) {
-        TraceEvent.begin("BackgroundTaskSchedulerPrefs.addScheduledTask",
-                Integer.toString(taskInfo.getTaskId()));
-        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
-        Set<String> scheduledTasks =
-                prefs.getStringSet(KEY_SCHEDULED_TASKS, new HashSet<String>(1));
-        String prefsEntry = ScheduledTaskPreferenceEntry.createForTaskInfo(taskInfo).toString();
-        if (scheduledTasks.contains(prefsEntry)) return;
+        try (TraceEvent te = TraceEvent.scoped("BackgroundTaskSchedulerPrefs.addScheduledTask",
+                     Integer.toString(taskInfo.getTaskId()))) {
+            SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+            Set<String> scheduledTasks =
+                    prefs.getStringSet(KEY_SCHEDULED_TASKS, new HashSet<String>(1));
+            String prefsEntry = ScheduledTaskPreferenceEntry.createForTaskInfo(taskInfo).toString();
+            if (scheduledTasks.contains(prefsEntry)) return;
 
-        // Set returned from getStringSet() should not be modified.
-        scheduledTasks = new HashSet<>(scheduledTasks);
-        scheduledTasks.add(prefsEntry);
-        updateScheduledTasks(prefs, scheduledTasks);
-        TraceEvent.end("BackgroundTaskSchedulerPrefs.addScheduledTask");
+            // Set returned from getStringSet() should not be modified.
+            scheduledTasks = new HashSet<>(scheduledTasks);
+            scheduledTasks.add(prefsEntry);
+            updateScheduledTasks(prefs, scheduledTasks);
+        }
     }
 
     /** Removes a task from scheduler's preferences. */
     public static void removeScheduledTask(int taskId) {
-        TraceEvent.begin(
-                "BackgroundTaskSchedulerPrefs.removeScheduledTask", Integer.toString(taskId));
-        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
-        Set<String> scheduledTasks = getScheduledTaskEntries(prefs);
+        try (TraceEvent te = TraceEvent.scoped("BackgroundTaskSchedulerPrefs.removeScheduledTask",
+                     Integer.toString(taskId))) {
+            SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+            Set<String> scheduledTasks = getScheduledTaskEntries(prefs);
 
-        String entryToRemove = null;
-        for (String entry : scheduledTasks) {
-            ScheduledTaskPreferenceEntry parsed = ScheduledTaskPreferenceEntry.parseEntry(entry);
-            if (parsed != null && parsed.getTaskId() == taskId) {
-                entryToRemove = entry;
-                break;
+            String entryToRemove = null;
+            for (String entry : scheduledTasks) {
+                ScheduledTaskPreferenceEntry parsed =
+                        ScheduledTaskPreferenceEntry.parseEntry(entry);
+                if (parsed != null && parsed.getTaskId() == taskId) {
+                    entryToRemove = entry;
+                    break;
+                }
             }
+
+            // Entry matching taskId was not found.
+            if (entryToRemove == null) return;
+
+            // Set returned from getStringSet() should not be modified.
+            scheduledTasks = new HashSet<>(scheduledTasks);
+            scheduledTasks.remove(entryToRemove);
+            updateScheduledTasks(prefs, scheduledTasks);
         }
-
-        // Entry matching taskId was not found.
-        if (entryToRemove == null) return;
-
-        // Set returned from getStringSet() should not be modified.
-        scheduledTasks = new HashSet<>(scheduledTasks);
-        scheduledTasks.remove(entryToRemove);
-        updateScheduledTasks(prefs, scheduledTasks);
-        TraceEvent.end("BackgroundTaskSchedulerPrefs.removeScheduledTask");
     }
 
     /** Gets a set of scheduled task class names. */
     public static Set<String> getScheduledTasks() {
-        TraceEvent.begin("BackgroundTaskSchedulerPrefs.getScheduledTasks");
-        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
-        Set<String> scheduledTask = getScheduledTaskEntries(prefs);
-        Set<String> scheduledTasksClassNames = new HashSet<>(scheduledTask.size());
-        for (String entry : scheduledTask) {
-            ScheduledTaskPreferenceEntry parsed = ScheduledTaskPreferenceEntry.parseEntry(entry);
-            if (parsed != null) {
-                scheduledTasksClassNames.add(parsed.getBackgroundTaskClass());
+        try (TraceEvent te = TraceEvent.scoped("BackgroundTaskSchedulerPrefs.getScheduledTasks")) {
+            SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+            Set<String> scheduledTask = getScheduledTaskEntries(prefs);
+            Set<String> scheduledTasksClassNames = new HashSet<>(scheduledTask.size());
+            for (String entry : scheduledTask) {
+                ScheduledTaskPreferenceEntry parsed =
+                        ScheduledTaskPreferenceEntry.parseEntry(entry);
+                if (parsed != null) {
+                    scheduledTasksClassNames.add(parsed.getBackgroundTaskClass());
+                }
             }
+            return scheduledTasksClassNames;
         }
-
-        TraceEvent.end("BackgroundTaskSchedulerPrefs.getScheduledTasks");
-        return scheduledTasksClassNames;
     }
 
     /** Gets a set of scheduled task IDs. */
     public static Set<Integer> getScheduledTaskIds() {
-        TraceEvent.begin("BackgroundTaskSchedulerPrefs.getScheduledTaskIds");
-        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
-        Set<String> scheduledTasks = getScheduledTaskEntries(prefs);
-        Set<Integer> scheduledTaskIds = new HashSet<>(scheduledTasks.size());
-        for (String entry : scheduledTasks) {
-            ScheduledTaskPreferenceEntry parsed = ScheduledTaskPreferenceEntry.parseEntry(entry);
-            if (parsed != null) {
-                scheduledTaskIds.add(parsed.getTaskId());
+        try (TraceEvent te =
+                        TraceEvent.scoped("BackgroundTaskSchedulerPrefs.getScheduledTaskIds")) {
+            SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+            Set<String> scheduledTasks = getScheduledTaskEntries(prefs);
+            Set<Integer> scheduledTaskIds = new HashSet<>(scheduledTasks.size());
+            for (String entry : scheduledTasks) {
+                ScheduledTaskPreferenceEntry parsed =
+                        ScheduledTaskPreferenceEntry.parseEntry(entry);
+                if (parsed != null) {
+                    scheduledTaskIds.add(parsed.getTaskId());
+                }
             }
+            return scheduledTaskIds;
         }
-
-        TraceEvent.end("BackgroundTaskSchedulerPrefs.getScheduledTaskIds");
-        return scheduledTaskIds;
     }
 
     /** Removes all scheduled tasks from shared preferences store. */
     public static void removeAllTasks() {
-        TraceEvent.begin("BackgroundTaskSchedulerPrefs.removeAllTasks");
-        ContextUtils.getAppSharedPreferences().edit().remove(KEY_SCHEDULED_TASKS).apply();
-        TraceEvent.end("BackgroundTaskSchedulerPrefs.removeAllTasks");
+        try (TraceEvent te = TraceEvent.scoped("BackgroundTaskSchedulerPrefs.removeAllTasks")) {
+            ContextUtils.getAppSharedPreferences().edit().remove(KEY_SCHEDULED_TASKS).apply();
+        }
     }
 
     /** Gets the last SDK version on which this instance ran. Defaults to current SDK version. */
     public static int getLastSdkVersion() {
-        TraceEvent.begin("BackgroundTaskSchedulerPrefs.getLastSdkVersion");
-        int sdkInt = ContextUtils.getAppSharedPreferences().getInt(
-                KEY_LAST_SDK_VERSION, Build.VERSION.SDK_INT);
-        TraceEvent.end("BackgroundTaskSchedulerPrefs.getLastSdkVersion");
-        return sdkInt;
+        try (TraceEvent te = TraceEvent.scoped("BackgroundTaskSchedulerPrefs.getLastSdkVersion")) {
+            int sdkInt = ContextUtils.getAppSharedPreferences().getInt(
+                    KEY_LAST_SDK_VERSION, Build.VERSION.SDK_INT);
+            return sdkInt;
+        }
     }
 
     /** Gets the last SDK version on which this instance ran. */
     public static void setLastSdkVersion(int sdkVersion) {
-        TraceEvent.begin(
-                "BackgroundTaskSchedulerPrefs.setLastSdkVersion", Integer.toString(sdkVersion));
-        ContextUtils.getAppSharedPreferences()
-                .edit()
-                .putInt(KEY_LAST_SDK_VERSION, sdkVersion)
-                .apply();
-        TraceEvent.end("BackgroundTaskSchedulerPrefs.setLastSdkVersion");
+        try (TraceEvent te = TraceEvent.scoped("BackgroundTaskSchedulerPrefs.setLastSdkVersion",
+                     Integer.toString(sdkVersion))) {
+            ContextUtils.getAppSharedPreferences()
+                    .edit()
+                    .putInt(KEY_LAST_SDK_VERSION, sdkVersion)
+                    .apply();
+        }
     }
 
     private static void updateScheduledTasks(SharedPreferences prefs, Set<String> tasks) {
diff --git a/components/download/internal/common/download_task_runner.cc b/components/download/internal/common/download_task_runner.cc
index cc24d67d..297e041 100644
--- a/components/download/internal/common/download_task_runner.cc
+++ b/components/download/internal/common/download_task_runner.cc
@@ -4,6 +4,7 @@
 
 #include "components/download/public/common/download_task_runner.h"
 
+#include "base/lazy_instance.h"
 #include "base/task_scheduler/lazy_task_runner.h"
 #include "build/build_config.h"
 
@@ -24,10 +25,22 @@
         base::TaskTraits(base::MayBlock(), base::TaskPriority::USER_VISIBLE));
 #endif
 
+base::LazyInstance<scoped_refptr<base::SingleThreadTaskRunner>>::Leaky
+    g_io_task_runner = LAZY_INSTANCE_INITIALIZER;
+
 }  // namespace
 
 scoped_refptr<base::SequencedTaskRunner> GetDownloadTaskRunner() {
   return g_download_task_runner.Get();
 }
 
+void SetIOTaskRunner(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+  g_io_task_runner.Get() = task_runner;
+}
+
+scoped_refptr<base::SequencedTaskRunner> GetIOTaskRunner() {
+  return g_io_task_runner.Get();
+}
+
 }  // namespace download
diff --git a/components/download/public/common/download_task_runner.h b/components/download/public/common/download_task_runner.h
index b7d5bd3..b5f75e1 100644
--- a/components/download/public/common/download_task_runner.h
+++ b/components/download/public/common/download_task_runner.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_DOWNLOAD_PUBLIC_COMMON_DOWNLOAD_TASK_RUNNER_H_
 
 #include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
 #include "components/download/public/common/download_export.h"
 
 namespace download {
@@ -14,6 +15,14 @@
 COMPONENTS_DOWNLOAD_EXPORT scoped_refptr<base::SequencedTaskRunner>
 GetDownloadTaskRunner();
 
+// Sets the task runner used to perform network IO.
+COMPONENTS_DOWNLOAD_EXPORT void SetIOTaskRunner(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+
+// Returns the task runner used to save files and do other blocking operations.
+COMPONENTS_DOWNLOAD_EXPORT scoped_refptr<base::SequencedTaskRunner>
+GetIOTaskRunner();
+
 }  // namespace download
 
 #endif  // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_DOWNLOAD_TASK_RUNNER_H_
diff --git a/components/download/public/common/url_download_handler.h b/components/download/public/common/url_download_handler.h
index 01102b5b..7ab5198 100644
--- a/components/download/public/common/url_download_handler.h
+++ b/components/download/public/common/url_download_handler.h
@@ -15,6 +15,8 @@
 // Class for handling the download of a url. Implemented by child classes.
 class COMPONENTS_DOWNLOAD_EXPORT UrlDownloadHandler {
  public:
+  using UniqueUrlDownloadHandlerPtr =
+      std::unique_ptr<UrlDownloadHandler, base::OnTaskRunnerDeleter>;
   // Class to be notified when download starts/stops.
   class COMPONENTS_DOWNLOAD_EXPORT Delegate {
    public:
@@ -22,8 +24,13 @@
         std::unique_ptr<DownloadCreateInfo> download_create_info,
         std::unique_ptr<InputStream> input_stream,
         const DownloadUrlParameters::OnStartedCallback& callback) = 0;
+
     // Called after the connection is cancelled or finished.
     virtual void OnUrlDownloadStopped(UrlDownloadHandler* downloader) = 0;
+
+    // Called when a UrlDownloadHandler is created.
+    virtual void OnUrlDownloadHandlerCreated(
+        UniqueUrlDownloadHandlerPtr downloader) {}
   };
 
   UrlDownloadHandler() = default;
diff --git a/components/metrics/generate_expired_histograms_array.gni b/components/metrics/generate_expired_histograms_array.gni
index e44dee0..b211c66 100644
--- a/components/metrics/generate_expired_histograms_array.gni
+++ b/components/metrics/generate_expired_histograms_array.gni
@@ -20,6 +20,9 @@
 #   major_branch_date_filepath:
 #     A path to the file with the base date.
 #
+#   milestone_filepath:
+#     A path to the file with the milestone information.
+#
 template("generate_expired_histograms_array") {
   action(target_name) {
     header_filename = "$target_gen_dir/" + invoker.header_filename
@@ -31,6 +34,7 @@
 
     inputs = invoker.inputs
     major_branch_date_filepath = invoker.major_branch_date_filepath
+    milestone_filepath = invoker.milestone_filepath
 
     args = []
 
@@ -42,6 +46,7 @@
               "-o" + rebase_path(root_gen_dir, root_build_dir),
               "-H" + rebase_path(header_filename, root_gen_dir),
               "-d" + rebase_path(major_branch_date_filepath, root_build_dir),
+              "-m" + rebase_path(milestone_filepath),
             ] + rebase_path(inputs, root_build_dir)
   }
 }
diff --git a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc
index 2f5a7b4a..a31197e 100644
--- a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc
+++ b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.cc
@@ -64,24 +64,59 @@
   DCHECK(navigation_handle()->IsInMainFrame());
   DCHECK(!parent_activation_state_);
   DCHECK(!ruleset_handle_);
-  // DISABLED implies null ruleset.
-  DCHECK(page_activation_state.activation_level != ActivationLevel::DISABLED ||
-         !ruleset_handle);
+  DCHECK_NE(ActivationLevel::DISABLED, page_activation_state.activation_level);
   parent_activation_state_ = page_activation_state;
   ruleset_handle_ = ruleset_handle;
 }
 
 content::NavigationThrottle::ThrottleCheckResult
+ActivationStateComputingNavigationThrottle::WillStartRequest() {
+  if (!navigation_handle()->IsInMainFrame())
+    CheckActivationState();
+  return content::NavigationThrottle::PROCEED;
+}
+
+content::NavigationThrottle::ThrottleCheckResult
+ActivationStateComputingNavigationThrottle::WillRedirectRequest() {
+  if (!navigation_handle()->IsInMainFrame())
+    CheckActivationState();
+  return content::NavigationThrottle::PROCEED;
+}
+
+content::NavigationThrottle::ThrottleCheckResult
 ActivationStateComputingNavigationThrottle::WillProcessResponse() {
-  // Main frame navigations with disabled page-level activation become
-  // pass-through throttles.
-  if (!parent_activation_state_ ||
-      parent_activation_state_->activation_level == ActivationLevel::DISABLED) {
+  // If no parent activation, this is main frame that was never notified of
+  // activation.
+  if (!parent_activation_state_) {
     DCHECK(navigation_handle()->IsInMainFrame());
+    DCHECK(!async_filter_);
     DCHECK(!ruleset_handle_);
     return content::NavigationThrottle::PROCEED;
   }
 
+  // Throttles which have finished their last check should just proceed here.
+  // All others need to defer and either wait for their existing check to
+  // finish, or start a new check now if there was no previous speculative
+  // check.
+  if (async_filter_ && async_filter_->has_activation_state()) {
+    LogDelayMetrics(base::TimeDelta::FromMilliseconds(0));
+    return content::NavigationThrottle::PROCEED;
+  }
+  DCHECK(!defer_timer_);
+  defer_timer_ = std::make_unique<base::ElapsedTimer>();
+  if (!async_filter_) {
+    DCHECK(navigation_handle()->IsInMainFrame());
+    CheckActivationState();
+  }
+  return content::NavigationThrottle::DEFER;
+}
+
+const char* ActivationStateComputingNavigationThrottle::GetNameForLogging() {
+  return "ActivationStateComputingNavigationThrottle";
+}
+
+void ActivationStateComputingNavigationThrottle::CheckActivationState() {
+  DCHECK(parent_activation_state_);
   DCHECK(ruleset_handle_);
   AsyncDocumentSubresourceFilter::InitializationParams params;
   params.document_url = navigation_handle()->GetURL();
@@ -92,24 +127,27 @@
     params.parent_document_origin = parent->GetLastCommittedOrigin();
   }
 
+  // If there is an existing |async_filter_| that hasn't called
+  // OnActivationStateComputed, it will be deleted here and never call that
+  // method. This is by design of the AsyncDocumentSubresourceFilter, which
+  // will drop the message via weak pointer semantics.
   async_filter_ = std::make_unique<AsyncDocumentSubresourceFilter>(
       ruleset_handle_, std::move(params),
       base::Bind(&ActivationStateComputingNavigationThrottle::
                      OnActivationStateComputed,
                  weak_ptr_factory_.GetWeakPtr()));
-
-  defer_timestamp_ = base::TimeTicks::Now();
-  return content::NavigationThrottle::DEFER;
-}
-
-const char* ActivationStateComputingNavigationThrottle::GetNameForLogging() {
-  return "ActivationStateComputingNavigationThrottle";
 }
 
 void ActivationStateComputingNavigationThrottle::OnActivationStateComputed(
     ActivationState state) {
-  DCHECK(!defer_timestamp_.is_null());
-  base::TimeDelta delay = base::TimeTicks::Now() - defer_timestamp_;
+  if (defer_timer_) {
+    LogDelayMetrics(defer_timer_->Elapsed());
+    Resume();
+  }
+}
+
+void ActivationStateComputingNavigationThrottle::LogDelayMetrics(
+    base::TimeDelta delay) const {
   UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
       "SubresourceFilter.DocumentLoad.ActivationComputingDelay", delay,
       base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
@@ -120,7 +158,6 @@
         delay, base::TimeDelta::FromMicroseconds(1),
         base::TimeDelta::FromSeconds(10), 50);
   }
-  Resume();
 }
 
 AsyncDocumentSubresourceFilter*
diff --git a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h
index fc67179..20e01345 100644
--- a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h
+++ b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle.h
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/time/time.h"
+#include "base/timer/elapsed_timer.h"
 #include "components/subresource_filter/content/browser/verified_ruleset_dealer.h"
 #include "components/subresource_filter/core/common/activation_state.h"
 #include "content/public/browser/navigation_throttle.h"
@@ -22,9 +23,15 @@
 // NavigationThrottle responsible for determining the activation state of
 // subresource filtering for a given navigation (either in the main frame or in
 // a subframe); and for deferring that navigation at WillProcessResponse until
-// the activation state computation on the blocking pool thread is complete.
+// the activation state computation on the ruleset's task runner is complete.
+//
 // Interested parties can retrieve the activation state after this point (most
 // likely in ReadyToCommitNavigation).
+//
+// Note: for performance, activation computation for subframes is done
+// speculatively at navigation start and at every redirect. This is to reduce
+// the wait time (most likely to 0) by WillProcessResponse time.
+// TODO(crbug.com/809504): Implement speculation for main frames as well.
 class ActivationStateComputingNavigationThrottle
     : public content::NavigationThrottle {
  public:
@@ -47,8 +54,10 @@
 
   // Notification for main frames when the page level activation is computed.
   // Must be called at most once before WillProcessResponse is called on this
-  // throttle. If it is never called, or it is called with a DISABLED state,
-  // this object will never delay the navigation.
+  // throttle. If it is never called, this object will never delay the
+  // navigation for main frames.
+  //
+  // Should never be called with DISABLED activation.
   void NotifyPageActivationWithRuleset(
       VerifiedRuleset::Handle* ruleset_handle,
       const ActivationState& page_activation_state);
@@ -58,6 +67,9 @@
   }
 
   // content::NavigationThrottle:
+  content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
+  content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest()
+      override;
   content::NavigationThrottle::ThrottleCheckResult WillProcessResponse()
       override;
   const char* GetNameForLogging() override;
@@ -74,8 +86,11 @@
   void WillSendActivationToRenderer();
 
  private:
+  void CheckActivationState();
   void OnActivationStateComputed(ActivationState state);
 
+  void LogDelayMetrics(base::TimeDelta delay) const;
+
   ActivationStateComputingNavigationThrottle(
       content::NavigationHandle* navigation_handle,
       const base::Optional<ActivationState> parent_activation_state,
@@ -90,7 +105,9 @@
   // nullptr until NotifyPageActivationWithRuleset is called.
   VerifiedRuleset::Handle* ruleset_handle_;
 
-  base::TimeTicks defer_timestamp_;
+  // Will be set when DEFER is called in WillProcessResponse. If nullptr, not
+  // deferred.
+  std::unique_ptr<base::ElapsedTimer> defer_timer_;
 
   // Callback to be run in the destructor.
   base::OnceClosure destruction_closure_;
diff --git a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
index 6742720..36ced49 100644
--- a/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/activation_state_computing_navigation_throttle_unittest.cc
@@ -10,11 +10,13 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
 #include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
 #include "base/test/histogram_tester.h"
 #include "base/test/test_simple_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h"
 #include "components/subresource_filter/core/common/activation_level.h"
@@ -36,7 +38,9 @@
     : public content::RenderViewHostTestHarness,
       public content::WebContentsObserver {
  public:
-  ActivationStateComputingNavigationThrottleTest() {}
+  ActivationStateComputingNavigationThrottleTest()
+      : simple_task_runner_(
+            base::MakeRefCounted<base::TestSimpleTaskRunner>()) {}
   ~ActivationStateComputingNavigationThrottleTest() override {}
 
   void SetUp() override {
@@ -49,7 +53,11 @@
   void TearDown() override {
     ruleset_handle_.reset();
     dealer_handle_.reset();
-    RunUntilIdle();
+
+    // The various ruleset classes post tasks to delete their blocking task
+    // runners. Make sure that happens now to avoid test-only leaks.
+    base::RunLoop().RunUntilIdle();
+    simple_task_runner()->RunUntilIdle();
     content::RenderViewHostTestHarness::TearDown();
   }
 
@@ -78,12 +86,7 @@
     // Make the blocking task runner run on the current task runner for the
     // tests, to ensure that the NavigationSimulator properly runs all necessary
     // tasks while waiting for throttle checks to finish.
-    dealer_handle_ = std::make_unique<VerifiedRulesetDealer::Handle>(
-        base::MessageLoop::current()->task_runner());
-    dealer_handle_->TryOpenAndSetRulesetFile(test_ruleset_pair_.indexed.path,
-                                             base::DoNothing());
-    ruleset_handle_ =
-        std::make_unique<VerifiedRuleset::Handle>(dealer_handle_.get());
+    InitializeRulesetHandles(base::SequencedTaskRunnerHandle::Get());
   }
 
   void NavigateAndCommitMainFrameWithPageActivationState(
@@ -96,8 +99,6 @@
     SimulateCommitAndExpectToProceed();
   }
 
-  void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
-
   void CreateTestNavigationForMainFrame(const GURL& first_url) {
     navigation_simulator_ =
         content::NavigationSimulator::CreateRendererInitiated(first_url,
@@ -136,12 +137,19 @@
               navigation_simulator_->GetLastThrottleCheckResult());
   }
 
+  void InitializeRulesetHandles(
+      scoped_refptr<base::SequencedTaskRunner> ruleset_task_runner) {
+    dealer_handle_ = std::make_unique<VerifiedRulesetDealer::Handle>(
+        std::move(ruleset_task_runner));
+    dealer_handle_->TryOpenAndSetRulesetFile(test_ruleset_pair_.indexed.path,
+                                             base::DoNothing());
+    ruleset_handle_ =
+        std::make_unique<VerifiedRuleset::Handle>(dealer_handle_.get());
+  }
+
   void NotifyPageActivation(ActivationState state) {
-    test_throttle_->NotifyPageActivationWithRuleset(
-        state.activation_level == ActivationLevel::DISABLED
-            ? nullptr
-            : ruleset_handle_.get(),
-        state);
+    test_throttle_->NotifyPageActivationWithRuleset(ruleset_handle_.get(),
+                                                    state);
   }
 
   ActivationState last_activation_state() {
@@ -154,6 +162,10 @@
     return last_committed_frame_host_;
   }
 
+  scoped_refptr<base::TestSimpleTaskRunner> simple_task_runner() {
+    return simple_task_runner_;
+  }
+
  protected:
   // content::WebContentsObserver:
   void DidStartNavigation(
@@ -203,6 +215,8 @@
 
   std::unique_ptr<content::NavigationSimulator> navigation_simulator_;
 
+  scoped_refptr<base::TestSimpleTaskRunner> simple_task_runner_;
+
   // Owned by the current navigation.
   ActivationStateComputingNavigationThrottle* test_throttle_;
   base::Optional<ActivationState> last_activation_state_;
@@ -241,16 +255,6 @@
 }
 
 TEST_F(ActivationStateComputingThrottleMainFrameTest,
-       DisabledPageActivation_NoActivation) {
-  // Notify that the page level activation is explicitly disabled. Should be
-  // equivalent to not sending the message at all to the main frame throttle.
-  NavigateAndCommitMainFrameWithPageActivationState(
-      GURL("http://example.test/"), ActivationState(ActivationLevel::DISABLED));
-  ActivationState state = last_activation_state();
-  EXPECT_EQ(ActivationLevel::DISABLED, state.activation_level);
-}
-
-TEST_F(ActivationStateComputingThrottleMainFrameTest,
        WhitelistDoesNotApply_CausesActivation) {
   NavigateAndCommitMainFrameWithPageActivationState(
       GURL("http://allow-child-to-be-whitelisted.com/"),
@@ -474,4 +478,98 @@
   histogram_tester.ExpectTotalCount(kActivationDelayMainFrame, 1);
 }
 
+TEST_F(ActivationStateComputingThrottleSubFrameTest, SpeculativeSubframes) {
+  // Use the activation performance metric as a proxy for how many times
+  // activation computation occurred.
+  base::HistogramTester histogram_tester;
+  const char kActivationCPU[] =
+      "SubresourceFilter.DocumentLoad.Activation.CPUDuration";
+
+  // Main frames don't do speculative lookups, a navigation commit should only
+  // trigger a single ruleset lookup.
+  CreateTestNavigationForMainFrame(GURL("http://example.test/"));
+  SimulateStartAndExpectToProceed();
+  histogram_tester.ExpectTotalCount(kActivationCPU, 0);
+
+  SimulateRedirectAndExpectToProceed(GURL("http://example.test2/"));
+  histogram_tester.ExpectTotalCount(kActivationCPU, 0);
+
+  NotifyPageActivation(ActivationState(ActivationLevel::ENABLED));
+  SimulateCommitAndExpectToProceed();
+  histogram_tester.ExpectTotalCount(kActivationCPU, 1);
+
+  CreateSubframeAndInitTestNavigation(GURL("http://example.test/"),
+                                      last_committed_frame_host(),
+                                      last_activation_state());
+  // For subframes, do a ruleset lookup at the start and every redirect.
+  SimulateStartAndExpectToProceed();
+  histogram_tester.ExpectTotalCount(kActivationCPU, 2);
+
+  SimulateRedirectAndExpectToProceed(GURL("http://example.test2/"));
+  histogram_tester.ExpectTotalCount(kActivationCPU, 3);
+
+  // No ruleset lookup required at commit because we've already checked the
+  // latest URL.
+  SimulateCommitAndExpectToProceed();
+  histogram_tester.ExpectTotalCount(kActivationCPU, 3);
+}
+
+TEST_F(ActivationStateComputingThrottleSubFrameTest,
+       SpeculativeSubframesWithDelay) {
+  InitializeRulesetHandles(simple_task_runner());
+
+  // TODO(csharrison): Once crbug.com/822275 is resolved, implement this via a
+  // Wait() style interface on NavigationSimulator. Right now it is very hacky:
+  // essentially we post a task to finish tasks on |simple_task_runner_| before
+  // a navigation stage (when necessary). NavigationSimulator internals pump the
+  // run loop which runs this task after throttles run.
+  auto task_runner = simple_task_runner();
+  auto post_advance = [task_runner]() {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(&base::TestSimpleTaskRunner::RunPendingTasks,
+                                  base::RetainedRef(task_runner)));
+  };
+
+  // Use the activation performance metric as a proxy for how many times
+  // activation computation occurred.
+  base::HistogramTester histogram_tester;
+  const char kActivationCPU[] =
+      "SubresourceFilter.DocumentLoad.Activation.CPUDuration";
+
+  // Main frames don't do speculative lookups, a navigation commit should only
+  // trigger a single ruleset lookup.
+  CreateTestNavigationForMainFrame(GURL("http://example.test/"));
+  SimulateStartAndExpectToProceed();
+  histogram_tester.ExpectTotalCount(kActivationCPU, 0);
+
+  SimulateRedirectAndExpectToProceed(GURL("http://example.test2/"));
+  histogram_tester.ExpectTotalCount(kActivationCPU, 0);
+
+  NotifyPageActivation(ActivationState(ActivationLevel::ENABLED));
+  post_advance();
+  SimulateCommitAndExpectToProceed();
+  histogram_tester.ExpectTotalCount(kActivationCPU, 1);
+
+  CreateSubframeAndInitTestNavigation(GURL("http://example.test/"),
+                                      last_committed_frame_host(),
+                                      last_activation_state());
+
+  // Simulate slow ruleset checks for the subframe, these should not delay the
+  // navigation until commit time.
+  SimulateStartAndExpectToProceed();
+  histogram_tester.ExpectTotalCount(kActivationCPU, 1);
+
+  // Calling redirect should ensure that the throttle does not receive the
+  // results of the check, but the task to actually perform the check will still
+  // happen.
+  SimulateRedirectAndExpectToProceed(GURL("http://example.test2/"));
+  histogram_tester.ExpectTotalCount(kActivationCPU, 1);
+
+  // Finish the checks dispatched in the start and redirect phase when the
+  // navigation is ready to commit.
+  post_advance();
+  SimulateCommitAndExpectToProceed();
+  histogram_tester.ExpectTotalCount(kActivationCPU, 3);
+}
+
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/async_document_subresource_filter.h b/components/subresource_filter/content/browser/async_document_subresource_filter.h
index 1c3b511..749415f 100644
--- a/components/subresource_filter/content/browser/async_document_subresource_filter.h
+++ b/components/subresource_filter/content/browser/async_document_subresource_filter.h
@@ -95,6 +95,9 @@
   // with the current thread. If MemoryMappedRuleset is not present or
   // malformed, then a default ActivationState is reported (with ActivationLevel
   // equal to DISABLED).
+  //
+  // If deleted before |activation_state_callback| is called, the callback will
+  // never be called.
   AsyncDocumentSubresourceFilter(
       VerifiedRuleset::Handle* ruleset_handle,
       InitializationParams params,
diff --git a/components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h b/components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h
index 4858add..60f9739 100644
--- a/components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h
+++ b/components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h
@@ -25,6 +25,8 @@
   void WaitForActivationDecision();
   void ExpectReceivedOnce(const ActivationState& expected_state) const;
 
+  int callback_count() const { return callback_count_; }
+
  private:
   void Callback(ActivationState activation_state);
 
diff --git a/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc b/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
index d3e3779e..7d4cb88 100644
--- a/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
+++ b/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
@@ -151,6 +151,23 @@
       ActivationState(ActivationLevel::ENABLED));
 }
 
+TEST_F(AsyncDocumentSubresourceFilterTest, DeleteFilter_NoActivationCallback) {
+  dealer_handle()->TryOpenAndSetRulesetFile(ruleset().path, base::DoNothing());
+  auto ruleset_handle = CreateRulesetHandle();
+
+  AsyncDocumentSubresourceFilter::InitializationParams params(
+      GURL("http://example.com"), ActivationLevel::ENABLED, false);
+
+  testing::TestActivationStateCallbackReceiver activation_state;
+  auto filter = std::make_unique<AsyncDocumentSubresourceFilter>(
+      ruleset_handle.get(), std::move(params), activation_state.GetCallback());
+
+  EXPECT_FALSE(filter->has_activation_state());
+  filter.reset();
+  RunUntilIdle();
+  EXPECT_EQ(0, activation_state.callback_count());
+}
+
 TEST_F(AsyncDocumentSubresourceFilterTest, ActivationStateIsComputedCorrectly) {
   dealer_handle()->TryOpenAndSetRulesetFile(ruleset().path, base::DoNothing());
   auto ruleset_handle = CreateRulesetHandle();
diff --git a/components/subresource_filter/content/renderer/subresource_filter_agent.cc b/components/subresource_filter/content/renderer/subresource_filter_agent.cc
index 7e5964c..3fbfe87 100644
--- a/components/subresource_filter/content/renderer/subresource_filter_agent.cc
+++ b/components/subresource_filter/content/renderer/subresource_filter_agent.cc
@@ -58,11 +58,6 @@
   web_frame->GetDocumentLoader()->SetSubresourceFilter(filter.release());
 }
 
-void SubresourceFilterAgent::SetIsAdSubframeForDocument(bool is_ad_subframe) {
-  blink::WebLocalFrame* web_frame = render_frame()->GetWebFrame();
-  web_frame->GetDocumentLoader()->SetIsAdSubframe(is_ad_subframe);
-}
-
 void SubresourceFilterAgent::
     SignalFirstSubresourceDisallowedForCommittedLoad() {
   render_frame()->Send(new SubresourceFilterHostMsg_DidDisallowFirstSubresource(
@@ -207,12 +202,10 @@
       AsWeakPtr()));
   auto filter = std::make_unique<WebDocumentSubresourceFilterImpl>(
       url::Origin::Create(url), activation_state, std::move(ruleset),
-      std::move(first_disallowed_load_callback));
+      std::move(first_disallowed_load_callback), is_ad_subframe);
 
   filter_for_last_committed_load_ = filter->AsWeakPtr();
   SetSubresourceFilterForCommittedLoad(std::move(filter));
-  if (is_ad_subframe)
-    SetIsAdSubframeForDocument(true);
 }
 
 void SubresourceFilterAgent::DidFailProvisionalLoad(
@@ -246,6 +239,12 @@
   base::File ruleset_file = ruleset_dealer_->DuplicateRulesetFile();
   if (!ruleset_file.IsValid())
     return;
+
+  // Propagate the information to the worker whether this is associated with an
+  // ad subframe.
+  bool is_ad_subframe =
+      render_frame()->GetWebFrame()->GetDocumentLoader()->GetIsAdSubframe();
+
   worker_fetch_context->SetSubresourceFilterBuilder(
       std::make_unique<WebDocumentSubresourceFilterImpl::BuilderImpl>(
           url::Origin::Create(GetDocumentURL()),
@@ -253,7 +252,8 @@
           std::move(ruleset_file),
           base::BindOnce(&SubresourceFilterAgent::
                              SignalFirstSubresourceDisallowedForCommittedLoad,
-                         AsWeakPtr())));
+                         AsWeakPtr()),
+          is_ad_subframe));
 }
 
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/renderer/subresource_filter_agent.h b/components/subresource_filter/content/renderer/subresource_filter_agent.h
index def12ac..5860bf6a 100644
--- a/components/subresource_filter/content/renderer/subresource_filter_agent.h
+++ b/components/subresource_filter/content/renderer/subresource_filter_agent.h
@@ -52,9 +52,6 @@
   virtual void SetSubresourceFilterForCommittedLoad(
       std::unique_ptr<blink::WebDocumentSubresourceFilter> filter);
 
-  // Sets in the underlying document, whether this is identified as an ad.
-  virtual void SetIsAdSubframeForDocument(bool is_ad_subframe);
-
   // Informs the browser that the first subresource load has been disallowed for
   // the most recently committed load. Not called if all resources are allowed.
   virtual void SignalFirstSubresourceDisallowedForCommittedLoad();
diff --git a/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc b/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc
index 84270e6..f3b7b74 100644
--- a/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc
+++ b/components/subresource_filter/content/renderer/subresource_filter_agent_unittest.cc
@@ -59,12 +59,10 @@
     OnSetSubresourceFilterForCommittedLoadCalled();
   }
 
-  void SetIsAdSubframeForDocument(bool is_ad_subframe) override {
-    is_ad_subframe_ = is_ad_subframe;
+  bool GetIsAssociatedWithAdSubframe() {
+    return last_injected_filter_->GetIsAssociatedWithAdSubframe();
   }
 
-  bool GetIsAdSubframe() { return is_ad_subframe_; }
-
   blink::WebDocumentSubresourceFilter* filter() {
     return last_injected_filter_.get();
   }
@@ -74,7 +72,6 @@
   }
 
  private:
-  bool is_ad_subframe_ = false;
   std::unique_ptr<blink::WebDocumentSubresourceFilter> last_injected_filter_;
 
   DISALLOW_COPY_AND_ASSIGN(SubresourceFilterAgentUnderTest);
@@ -521,16 +518,21 @@
   filter->ReportDisallowedLoad();
 }
 
-TEST_F(SubresourceFilterAgentTest, DryRun_DocumentIsAdSubframeTagging) {
-  base::HistogramTester histogram_tester;
+TEST_F(SubresourceFilterAgentTest,
+       DryRun_IsAssociatedWithAdSubframeforDocumentOrDedicatedWorker) {
   ASSERT_NO_FATAL_FAILURE(
       SetTestRulesetToDisallowURLsWithPathSuffix(kTestFirstURLPathSuffix));
   ExpectSubresourceFilterGetsInjected();
   StartLoadAndSetActivationState(ActivationState(ActivationLevel::DRYRUN),
-                                 true /* is_ad_subframe */);
+                                 true /* is_associated_with_ad_subframe */);
   ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(agent()));
 
-  EXPECT_TRUE(agent()->GetIsAdSubframe());
+  // Test the ad subframe value that is set at the filter.
+  // This also represents the flag passed to a dedicated worker filter created.
+  // For testing the flag passed to the dedicated worker filter, the unit test
+  // is not able to test the implementation of WillCreateWorkerFetchContext as
+  // that will require setup of a WebWorkerFetchContextImpl.
+  EXPECT_TRUE(agent()->GetIsAssociatedWithAdSubframe());
 }
 
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc b/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
index 9fc90c56..9b0f151 100644
--- a/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
+++ b/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
@@ -106,11 +106,13 @@
     url::Origin document_origin,
     ActivationState activation_state,
     scoped_refptr<const MemoryMappedRuleset> ruleset,
-    base::OnceClosure first_disallowed_load_callback)
+    base::OnceClosure first_disallowed_load_callback,
+    bool is_associated_with_ad_subframe)
     : activation_state_(activation_state),
       filter_(std::move(document_origin), activation_state, std::move(ruleset)),
       first_disallowed_load_callback_(
-          std::move(first_disallowed_load_callback)) {}
+          std::move(first_disallowed_load_callback)),
+      is_associated_with_ad_subframe_(is_associated_with_ad_subframe) {}
 
 WebLoadPolicy WebDocumentSubresourceFilterImpl::GetLoadPolicy(
     const blink::WebURL& resourceUrl,
@@ -134,6 +136,10 @@
   return activation_state().enable_logging;
 }
 
+bool WebDocumentSubresourceFilterImpl::GetIsAssociatedWithAdSubframe() const {
+  return is_associated_with_ad_subframe_;
+}
+
 WebLoadPolicy WebDocumentSubresourceFilterImpl::getLoadPolicyImpl(
     const blink::WebURL& url,
     proto::ElementType element_type) {
@@ -151,13 +157,15 @@
     url::Origin document_origin,
     ActivationState activation_state,
     base::File ruleset_file,
-    base::OnceClosure first_disallowed_load_callback)
+    base::OnceClosure first_disallowed_load_callback,
+    bool is_associated_with_ad_subframe)
     : document_origin_(std::move(document_origin)),
       activation_state_(std::move(activation_state)),
       ruleset_file_(std::move(ruleset_file)),
       first_disallowed_load_callback_(
           std::move(first_disallowed_load_callback)),
-      main_task_runner_(base::MessageLoop::current()->task_runner()) {}
+      main_task_runner_(base::MessageLoop::current()->task_runner()),
+      is_associated_with_ad_subframe_(is_associated_with_ad_subframe) {}
 
 WebDocumentSubresourceFilterImpl::BuilderImpl::~BuilderImpl() {}
 
@@ -169,7 +177,8 @@
       document_origin_, activation_state_,
       base::MakeRefCounted<MemoryMappedRuleset>(std::move(ruleset_file_)),
       base::BindOnce(&ProxyToTaskRunner, main_task_runner_,
-                     std::move(first_disallowed_load_callback_)));
+                     std::move(first_disallowed_load_callback_)),
+      is_associated_with_ad_subframe_);
 }
 
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h b/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h
index 826a07e..3b441713 100644
--- a/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h
+++ b/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h
@@ -31,7 +31,8 @@
     BuilderImpl(url::Origin document_origin,
                 ActivationState activation_state,
                 base::File ruleset_file,
-                base::OnceClosure first_disallowed_load_callback);
+                base::OnceClosure first_disallowed_load_callback,
+                bool is_associated_with_ad_subframe);
     ~BuilderImpl() override;
 
     std::unique_ptr<blink::WebDocumentSubresourceFilter> Build() override;
@@ -42,6 +43,7 @@
     base::File ruleset_file_;
     base::OnceClosure first_disallowed_load_callback_;
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+    bool is_associated_with_ad_subframe_;
 
     DISALLOW_COPY_AND_ASSIGN(BuilderImpl);
   };
@@ -54,7 +56,8 @@
       url::Origin document_origin,
       ActivationState activation_state,
       scoped_refptr<const MemoryMappedRuleset> ruleset,
-      base::OnceClosure first_disallowed_load_callback);
+      base::OnceClosure first_disallowed_load_callback,
+      bool is_associated_with_ad_subframe);
 
   ~WebDocumentSubresourceFilterImpl() override;
 
@@ -67,6 +70,7 @@
       const blink::WebURL& url) override;
   void ReportDisallowedLoad() override;
   bool ShouldLogToConsole() override;
+  bool GetIsAssociatedWithAdSubframe() const override;
 
   const ActivationState& activation_state() const {
     return filter_.activation_state();
@@ -80,6 +84,7 @@
   ActivationState activation_state_;
   DocumentSubresourceFilter filter_;
   base::OnceClosure first_disallowed_load_callback_;
+  bool is_associated_with_ad_subframe_;
 
   DISALLOW_COPY_AND_ASSIGN(WebDocumentSubresourceFilterImpl);
 };
diff --git a/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc b/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
index ac9bcdfe..598225d 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
@@ -106,7 +106,22 @@
 
 }  // namespace
 
-TEST(SubresourceFilterFeaturesTest, ActivationLevel) {
+class SubresourceFilterFeaturesTest : public ::testing::Test {
+ public:
+  SubresourceFilterFeaturesTest() {}
+  ~SubresourceFilterFeaturesTest() override {}
+
+  void SetUp() override {
+    // Reset the global configuration at the start so tests start without a
+    // cached value from a previous in-process test run.
+    testing::GetAndSetActivateConfigurations(nullptr);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SubresourceFilterFeaturesTest);
+};
+
+TEST_F(SubresourceFilterFeaturesTest, ActivationLevel) {
   const struct {
     bool feature_enabled;
     const char* activation_level_param;
@@ -149,7 +164,7 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest, ActivationScope) {
+TEST_F(SubresourceFilterFeaturesTest, ActivationScope) {
   const struct {
     bool feature_enabled;
     const char* activation_scope_param;
@@ -192,7 +207,7 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest, ActivationLevelAndScope) {
+TEST_F(SubresourceFilterFeaturesTest, ActivationLevelAndScope) {
   const struct {
     bool feature_enabled;
     const char* activation_level_param;
@@ -255,7 +270,7 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest, ActivationList) {
+TEST_F(SubresourceFilterFeaturesTest, ActivationList) {
   const std::string activation_soc_eng(
       kActivationListSocialEngineeringAdsInterstitial);
   const std::string activation_phishing(kActivationListPhishingInterstitial);
@@ -315,7 +330,7 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest, ActivationPriority) {
+TEST_F(SubresourceFilterFeaturesTest, ActivationPriority) {
   const struct {
     bool feature_enabled;
     const char* activation_priority_param;
@@ -358,7 +373,7 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest, PerfMeasurementRate) {
+TEST_F(SubresourceFilterFeaturesTest, PerfMeasurementRate) {
   const struct {
     bool feature_enabled;
     const char* perf_measurement_param;
@@ -400,7 +415,7 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest, SuppressNotifications) {
+TEST_F(SubresourceFilterFeaturesTest, SuppressNotifications) {
   const struct {
     bool feature_enabled;
     const char* suppress_notifications_param;
@@ -439,7 +454,7 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest, WhitelistSiteOnReload) {
+TEST_F(SubresourceFilterFeaturesTest, WhitelistSiteOnReload) {
   const struct {
     bool feature_enabled;
     const char* whitelist_site_on_reload_param;
@@ -478,7 +493,7 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest, RulesetFlavor) {
+TEST_F(SubresourceFilterFeaturesTest, RulesetFlavor) {
   const struct {
     bool feature_enabled;
     const char* ruleset_flavor_param;
@@ -508,7 +523,7 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest, LexicographicallyGreatestRulesetFlavor) {
+TEST_F(SubresourceFilterFeaturesTest, LexicographicallyGreatestRulesetFlavor) {
   const struct {
     const char* expected_ruleset_flavor_selected;
     std::vector<std::string> ruleset_flavors;
@@ -549,7 +564,7 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest, EnabledConfigurations_FeatureDisabled) {
+TEST_F(SubresourceFilterFeaturesTest, EnabledConfigurations_FeatureDisabled) {
   ScopedExperimentalStateToggle scoped_experimental_state(
       base::FeatureList::OVERRIDE_DISABLE_FEATURE,
       std::map<std::string, std::string>());
@@ -561,8 +576,8 @@
             config_list->lexicographically_greatest_ruleset_flavor());
 }
 
-TEST(SubresourceFilterFeaturesTest,
-     EnabledConfigurations_FeatureEnabledWithNoParameters) {
+TEST_F(SubresourceFilterFeaturesTest,
+       EnabledConfigurations_FeatureEnabledWithNoParameters) {
   ScopedExperimentalStateToggle scoped_experimental_state(
       base::FeatureList::OVERRIDE_ENABLE_FEATURE,
       std::map<std::string, std::string>());
@@ -577,8 +592,8 @@
             config_list->lexicographically_greatest_ruleset_flavor());
 }
 
-TEST(SubresourceFilterFeaturesTest,
-     PresetForPerformanceTestingDryRunOnAllSites) {
+TEST_F(SubresourceFilterFeaturesTest,
+       PresetForPerformanceTestingDryRunOnAllSites) {
   ExpectPresetCanBeEnabledByName(
       Configuration::MakePresetForPerformanceTestingDryRunOnAllSites(),
       kPresetPerformanceTestingDryRunOnAllSites);
@@ -590,7 +605,7 @@
        {kPerformanceMeasurementRateParameterName, "1.0"}});
 }
 
-TEST(SubresourceFilterFeaturesTest, PresetForLiveRunOnBetterAdsSites) {
+TEST_F(SubresourceFilterFeaturesTest, PresetForLiveRunOnBetterAdsSites) {
   const Configuration config =
       Configuration::MakePresetForLiveRunForBetterAds();
   EXPECT_EQ(ActivationList::BETTER_ADS,
@@ -606,7 +621,7 @@
   EXPECT_FALSE(config.activation_options.should_whitelist_site_on_reload);
 }
 
-TEST(SubresourceFilterFeaturesTest, ConfigurationPriorities) {
+TEST_F(SubresourceFilterFeaturesTest, ConfigurationPriorities) {
   const std::vector<Configuration> expected_order_by_decreasing_priority = {
       Configuration::MakePresetForLiveRunOnPhishingSites(),
       Configuration::MakePresetForLiveRunForBetterAds(),
@@ -626,7 +641,7 @@
       ::testing::ElementsAreArray(expected_order_by_decreasing_priority));
 }
 
-TEST(SubresourceFilterFeaturesTest, EnableDisableMultiplePresets) {
+TEST_F(SubresourceFilterFeaturesTest, EnableDisableMultiplePresets) {
   const std::string kPhishing(kPresetLiveRunOnPhishingSites);
   const std::string kPerfTest(kPresetPerformanceTestingDryRunOnAllSites);
   const std::string kBAS(kPresetLiveRunForBetterAds);
@@ -679,8 +694,8 @@
   }
 }
 
-TEST(SubresourceFilterFeaturesTest,
-     EnableMultiplePresetsAndExperimentalConfig) {
+TEST_F(SubresourceFilterFeaturesTest,
+       EnableMultiplePresetsAndExperimentalConfig) {
   const std::string kPhishing(kPresetLiveRunOnPhishingSites);
   const std::string kPerfTest(kPresetPerformanceTestingDryRunOnAllSites);
   const std::string kBAS(kPresetLiveRunForBetterAds);
@@ -713,7 +728,7 @@
             config_list->lexicographically_greatest_ruleset_flavor());
 }
 
-TEST(SubresourceFilterFeaturesTest, ForcedActivation_NotConfigurable) {
+TEST_F(SubresourceFilterFeaturesTest, ForcedActivation_NotConfigurable) {
   ScopedExperimentalStateToggle scoped_experimental_state(
       base::FeatureList::OVERRIDE_ENABLE_FEATURE,
       {{kActivationLevelParameterName, kActivationLevelEnabled},
@@ -730,7 +745,7 @@
   EXPECT_FALSE(actual_configuration.activation_conditions.forced_activation);
 }
 
-TEST(SubresourceFilterFeaturesTest, AdTagging_EnablesDryRun) {
+TEST_F(SubresourceFilterFeaturesTest, AdTagging_EnablesDryRun) {
   const Configuration dryrun =
       Configuration::MakePresetForPerformanceTestingDryRunOnAllSites();
   base::test::ScopedFeatureList scoped_feature;
@@ -739,14 +754,7 @@
       GetEnabledConfigurations()->configs_by_decreasing_priority(), dryrun));
 }
 
-// TODO(crbug.com/823449): Test fails on iOS. Re-enable after fixing.
-#if defined(OS_IOS)
-#define MAYBE_AdTaggingDisabled_DisablesDryRun \
-  DISABLED_AdTaggingDisabled_DisablesDryRun
-#else
-#define MAYBE_AdTaggingDisabled_DisablesDryRun AdTaggingDisabled_DisablesDryRun
-#endif
-TEST(SubresourceFilterFeaturesTest, MAYBE_AdTaggingDisabled_DisablesDryRun) {
+TEST_F(SubresourceFilterFeaturesTest, AdTaggingDisabled_DisablesDryRun) {
   const Configuration dryrun =
       Configuration::MakePresetForPerformanceTestingDryRunOnAllSites();
   base::test::ScopedFeatureList scoped_feature;
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index b2364574..8f02ddf 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -13,7 +13,7 @@
 // Enables running draw occlusion algorithm to remove Draw Quads that are not
 // shown on screen from CompositorFrame.
 const base::Feature kEnableDrawOcclusion{"DrawOcclusion",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
+                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
 #if defined(USE_AURA) || defined(OS_MACOSX)
 const base::Feature kEnableSurfaceSynchronization{
diff --git a/components/viz/common/gpu/texture_allocation.cc b/components/viz/common/gpu/texture_allocation.cc
index ef19a49..90b51e28 100644
--- a/components/viz/common/gpu/texture_allocation.cc
+++ b/components/viz/common/gpu/texture_allocation.cc
@@ -5,6 +5,7 @@
 #include "components/viz/common/gpu/texture_allocation.h"
 
 #include "components/viz/common/resources/resource_format_utils.h"
+#include "components/viz/common/resources/resource_sizes.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/client/raster_interface.h"
@@ -22,16 +23,14 @@
     ResourceFormat format,
     bool use_gpu_memory_buffer_resources,
     bool for_framebuffer_attachment) {
-  bool overlay_textures = use_gpu_memory_buffer_resources &&
-                          caps.texture_storage_image &&
-                          IsGpuMemoryBufferFormatSupported(format);
-
   uint32_t texture_target = GL_TEXTURE_2D;
-  bool overlay_candidate = false;
-  if (overlay_textures) {
+
+  bool overlay_candidate = use_gpu_memory_buffer_resources &&
+                           caps.texture_storage_image &&
+                           IsGpuMemoryBufferFormatSupported(format);
+  if (overlay_candidate) {
     texture_target = gpu::GetBufferTextureTarget(gfx::BufferUsage::SCANOUT,
                                                  BufferFormat(format), caps);
-    overlay_candidate = true;
   }
 
   uint32_t texture_id;
@@ -106,4 +105,27 @@
   }
 }
 
+void TextureAllocation::UploadStorage(gpu::gles2::GLES2Interface* gl,
+                                      const gpu::Capabilities& caps,
+                                      ResourceFormat format,
+                                      const gfx::Size& size,
+                                      const TextureAllocation& alloc,
+                                      const gfx::ColorSpace& color_space,
+                                      const void* pixels) {
+  if (format == ETC1) {
+    DCHECK_EQ(alloc.texture_target, static_cast<GLenum>(GL_TEXTURE_2D));
+    int num_bytes = ResourceSizes::CheckedSizeInBytes<int>(size, ETC1);
+
+    gl->BindTexture(alloc.texture_target, alloc.texture_id);
+    gl->CompressedTexImage2D(alloc.texture_target, 0, GLInternalFormat(ETC1),
+                             size.width(), size.height(), 0, num_bytes,
+                             const_cast<void*>(pixels));
+  } else {
+    AllocateStorage(gl, caps, format, size, alloc, color_space);
+    gl->TexSubImage2D(alloc.texture_target, 0, 0, 0, size.width(),
+                      size.height(), GLDataFormat(format), GLDataType(format),
+                      const_cast<void*>(pixels));
+  }
+}
+
 }  // namespace viz
diff --git a/components/viz/common/gpu/texture_allocation.h b/components/viz/common/gpu/texture_allocation.h
index 61b40e4f..6524f61 100644
--- a/components/viz/common/gpu/texture_allocation.h
+++ b/components/viz/common/gpu/texture_allocation.h
@@ -58,6 +58,18 @@
                               const gfx::Size& size,
                               const TextureAllocation& alloc,
                               const gfx::ColorSpace& color_space);
+
+  // Allocates storage for a texture id previously generated by MakeTextureId(),
+  // and uploads the contents of |pixels| to it. |pixels| should point to a
+  // bitmap with a width and height of |size|, and no additional row stride
+  // padding.
+  static void UploadStorage(gpu::gles2::GLES2Interface* gl,
+                            const gpu::Capabilities& caps,
+                            ResourceFormat format,
+                            const gfx::Size& size,
+                            const TextureAllocation& alloc,
+                            const gfx::ColorSpace& color_space,
+                            const void* pixels);
 };
 
 }  // namespace viz
diff --git a/components/viz/common/resources/bitmap_allocation.cc b/components/viz/common/resources/bitmap_allocation.cc
index 3924c58b..1e23ed5c61 100644
--- a/components/viz/common/resources/bitmap_allocation.cc
+++ b/components/viz/common/resources/bitmap_allocation.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include "base/debug/alias.h"
 #include "base/memory/shared_memory.h"
 #include "base/process/memory.h"
 #include "build/build_config.h"
@@ -14,11 +15,6 @@
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "ui/gfx/geometry/size.h"
 
-#if defined(OS_WIN)
-#include "base/debug/alias.h"
-#include "base/process/process_metrics.h"
-#endif
-
 namespace viz {
 
 namespace {
@@ -27,25 +23,17 @@
                               ResourceFormat format,
                               size_t alloc_size) {
 #if defined(OS_WIN)
+  DWORD last_error = GetLastError();
+  base::debug::Alias(&last_error);
+#endif
+
   int width = size.width();
   int height = size.height();
-  DWORD last_error = GetLastError();
-
-  std::unique_ptr<base::ProcessMetrics> metrics(
-      base::ProcessMetrics::CreateProcessMetrics(
-          base::GetCurrentProcessHandle()));
-
-  size_t private_bytes = 0;
-  size_t shared_bytes = 0;
-  metrics->GetMemoryBytes(&private_bytes, &shared_bytes);
 
   base::debug::Alias(&width);
   base::debug::Alias(&height);
   base::debug::Alias(&format);
-  base::debug::Alias(&last_error);
-  base::debug::Alias(&private_bytes);
-  base::debug::Alias(&shared_bytes);
-#endif
+
   base::TerminateBecauseOutOfMemory(alloc_size);
 }
 }  // namespace
diff --git a/components/viz/common/resources/resource_format_utils.cc b/components/viz/common/resources/resource_format_utils.cc
index 5433ef0..a7c072f52 100644
--- a/components/viz/common/resources/resource_format_utils.cc
+++ b/components/viz/common/resources/resource_format_utils.cc
@@ -160,9 +160,10 @@
     case LUMINANCE_8:
     case RGB_565:
     case LUMINANCE_F16:
+      // These types not allowed by IsGpuMemoryBufferFormatSupported(), so
+      // give a default value that will not be used.
       break;
   }
-  NOTREACHED();
   return gfx::BufferFormat::RGBA_8888;
 }
 
diff --git a/components/viz/common/resources/resource_format_utils.h b/components/viz/common/resources/resource_format_utils.h
index fb05834..5d6b4101 100644
--- a/components/viz/common/resources/resource_format_utils.h
+++ b/components/viz/common/resources/resource_format_utils.h
@@ -27,6 +27,10 @@
 VIZ_RESOURCE_FORMAT_EXPORT unsigned int GLCopyTextureInternalFormat(
     ResourceFormat format);
 
+// Returns the pixel format of the resource when mapped into client-side memory.
+// Returns a default value when IsGpuMemoryBufferFormatSupported() returns false
+// for a given format, as in this case the resource will not be mapped into
+// client-side memory, and the returned value is not used.
 VIZ_RESOURCE_FORMAT_EXPORT gfx::BufferFormat BufferFormat(
     ResourceFormat format);
 VIZ_RESOURCE_FORMAT_EXPORT bool IsResourceFormatCompressed(
diff --git a/components/viz/common/yuv_readback_unittest.cc b/components/viz/common/yuv_readback_unittest.cc
index acdb53c..85dd3499 100644
--- a/components/viz/common/yuv_readback_unittest.cc
+++ b/components/viz/common/yuv_readback_unittest.cc
@@ -404,7 +404,7 @@
 
     const gfx::Rect paste_rect(gfx::Point(xmargin, ymargin),
                                gfx::Size(xsize, ysize));
-    media::LetterboxYUV(output_frame.get(), paste_rect);
+    media::LetterboxVideoFrame(output_frame.get(), paste_rect);
     run_loop.Run();
 
     if (flip) {
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 70e0f88..1b7f689a 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -276,6 +276,7 @@
     cc::OverlayCandidate output_surface_plane;
     output_surface_plane.display_rect =
         gfx::RectF(device_viewport_size.width(), device_viewport_size.height());
+    output_surface_plane.resource_size_in_pixels = device_viewport_size;
     output_surface_plane.format = output_surface_->GetOverlayBufferFormat();
     output_surface_plane.use_output_surface_for_resource = true;
     output_surface_plane.overlay_handled = true;
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 41bdac1c..c343c05 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -271,7 +271,9 @@
   }
 
   base::ElapsedTimer aggregate_timer;
-  CompositorFrame frame = aggregator_->Aggregate(current_surface_id_);
+  CompositorFrame frame = aggregator_->Aggregate(
+      current_surface_id_, scheduler_ ? scheduler_->current_frame_display_time()
+                                      : base::TimeTicks::Now());
   UMA_HISTOGRAM_COUNTS_1M("Compositing.SurfaceAggregator.AggregateUs",
                           aggregate_timer.Elapsed().InMicroseconds());
 
@@ -372,7 +374,7 @@
       frame.metadata.latency_info.emplace_back(ui::SourceEventType::FRAME);
       frame.metadata.latency_info.back().AddLatencyNumberWithTimestamp(
           ui::LATENCY_BEGIN_FRAME_DISPLAY_COMPOSITOR_COMPONENT, 0, 0,
-          scheduler_->CurrentFrameTime(), 1);
+          scheduler_->current_frame_time(), 1);
     }
 
     DLOG_IF(WARNING, !presented_callbacks_.empty())
diff --git a/components/viz/service/display/display_scheduler.h b/components/viz/service/display/display_scheduler.h
index d8af7a42..d3cfbb04 100644
--- a/components/viz/service/display/display_scheduler.h
+++ b/components/viz/service/display/display_scheduler.h
@@ -50,9 +50,13 @@
   void SetRootSurfaceResourcesLocked(bool locked);
   void ForceImmediateSwapIfPossible();
   void SetNeedsOneBeginFrame();
-  base::TimeTicks CurrentFrameTime() {
+  base::TimeTicks current_frame_time() const {
     return current_begin_frame_args_.frame_time;
   }
+  base::TimeTicks current_frame_display_time() const {
+    return current_begin_frame_args_.frame_time +
+           current_begin_frame_args_.interval;
+  }
   virtual void DisplayResized();
   virtual void SetNewRootSurface(const SurfaceId& root_surface_id);
   virtual void ProcessSurfaceDamage(const SurfaceId& surface_id,
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index f63b2f10..a262fad 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -1050,7 +1050,7 @@
     // The following call can cause one or more copy requests to be added to the
     // Surface. Therefore, no code before this point should have assumed
     // anything about the presence or absence of copy requests after this point.
-    surface->NotifyAggregatedDamage(damage_rect);
+    surface->NotifyAggregatedDamage(damage_rect, expected_display_time_);
   }
 
   // If any CopyOutputRequests were made at FrameSink level, make sure we grab
@@ -1150,7 +1150,11 @@
   }
 }
 
-CompositorFrame SurfaceAggregator::Aggregate(const SurfaceId& surface_id) {
+CompositorFrame SurfaceAggregator::Aggregate(
+    const SurfaceId& surface_id,
+    base::TimeTicks expected_display_time) {
+  DCHECK(!expected_display_time.is_null());
+
   uma_stats_.Reset();
 
   Surface* surface = manager_->GetSurfaceForId(surface_id);
@@ -1169,6 +1173,7 @@
   CompositorFrame frame;
 
   dest_pass_list_ = &frame.render_pass_list;
+  expected_display_time_ = expected_display_time;
 
   valid_surfaces_.clear();
   has_cached_render_passes_ = false;
@@ -1208,6 +1213,7 @@
     return {};
 
   dest_pass_list_ = nullptr;
+  expected_display_time_ = base::TimeTicks();
   ProcessAddedAndRemovedSurfaces();
   contained_surfaces_.swap(previous_contained_surfaces_);
   contained_surfaces_.clear();
diff --git a/components/viz/service/display/surface_aggregator.h b/components/viz/service/display/surface_aggregator.h
index 17beb2c..9ff6edbe 100644
--- a/components/viz/service/display/surface_aggregator.h
+++ b/components/viz/service/display/surface_aggregator.h
@@ -40,7 +40,8 @@
                     bool aggregate_only_damaged);
   ~SurfaceAggregator();
 
-  CompositorFrame Aggregate(const SurfaceId& surface_id);
+  CompositorFrame Aggregate(const SurfaceId& surface_id,
+                            base::TimeTicks expected_display_time);
   void ReleaseResources(const SurfaceId& surface_id);
   const SurfaceIndexMap& previous_contained_surfaces() const {
     return previous_contained_surfaces_;
@@ -244,6 +245,9 @@
   // This is the pass list for the aggregated frame.
   RenderPassList* dest_pass_list_;
 
+  // The target display time for the aggregated frame.
+  base::TimeTicks expected_display_time_;
+
   // This is the set of aggregated pass ids that are affected by filters that
   // move pixels.
   base::flat_set<RenderPassId> moved_pixel_passes_;
diff --git a/components/viz/service/display/surface_aggregator_perftest.cc b/components/viz/service/display/surface_aggregator_perftest.cc
index 2cb18bf1..9ae9e88 100644
--- a/components/viz/service/display/surface_aggregator_perftest.cc
+++ b/components/viz/service/display/surface_aggregator_perftest.cc
@@ -6,6 +6,7 @@
 #include "cc/resources/display_resource_provider.h"
 #include "cc/test/fake_output_surface_client.h"
 #include "cc/test/fake_resource_provider.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/surface_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
@@ -106,6 +107,8 @@
     auto root_support = std::make_unique<CompositorFrameSinkSupport>(
         nullptr, &manager_, FrameSinkId(1, num_surfaces + 1), kIsRoot,
         kNeedsSyncPoints);
+    base::TimeTicks next_fake_display_time =
+        base::TimeTicks() + base::TimeDelta::FromSeconds(1);
     timer_.Reset();
     do {
       auto pass = RenderPass::Create();
@@ -133,7 +136,9 @@
 
       CompositorFrame aggregated = aggregator_->Aggregate(
           SurfaceId(FrameSinkId(1, num_surfaces + 1),
-                    LocalSurfaceId(num_surfaces + 1, kArbitraryToken)));
+                    LocalSurfaceId(num_surfaces + 1, kArbitraryToken)),
+          next_fake_display_time);
+      next_fake_display_time += BeginFrameArgs::DefaultInterval();
       timer_.NextLap();
     } while (!timer_.HasTimeLimitExpired());
 
diff --git a/components/viz/service/display/surface_aggregator_pixeltest.cc b/components/viz/service/display/surface_aggregator_pixeltest.cc
index 8b3bbbd..7b90734 100644
--- a/components/viz/service/display/surface_aggregator_pixeltest.cc
+++ b/components/viz/service/display/surface_aggregator_pixeltest.cc
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "cc/test/pixel_comparator.h"
 #include "cc/test/pixel_test.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/render_pass.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
@@ -42,10 +44,18 @@
             kNeedsSyncPoints)) {}
   ~SurfaceAggregatorPixelTest() override {}
 
+  base::TimeTicks GetNextDisplayTime() {
+    base::TimeTicks display_time = next_display_time_;
+    next_display_time_ += BeginFrameArgs::DefaultInterval();
+    return display_time;
+  }
+
  protected:
   FrameSinkManagerImpl manager_;
   ParentLocalSurfaceIdAllocator allocator_;
   std::unique_ptr<CompositorFrameSinkSupport> support_;
+  base::TimeTicks next_display_time_ =
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1);
 };
 
 SharedQuadState* CreateAndAppendTestSharedQuadState(
@@ -89,7 +99,8 @@
 
   SurfaceAggregator aggregator(manager_.surface_manager(),
                                resource_provider_.get(), true);
-  CompositorFrame aggregated_frame = aggregator.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator.Aggregate(root_surface_id, GetNextDisplayTime());
 
   bool discard_alpha = false;
   cc::ExactPixelComparator pixel_comparator(discard_alpha);
@@ -161,7 +172,8 @@
 
   SurfaceAggregator aggregator(manager_.surface_manager(),
                                resource_provider_.get(), true);
-  CompositorFrame aggregated_frame = aggregator.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator.Aggregate(root_surface_id, GetNextDisplayTime());
 
   bool discard_alpha = false;
   cc::ExactPixelComparator pixel_comparator(discard_alpha);
@@ -287,7 +299,8 @@
 
   SurfaceAggregator aggregator(manager_.surface_manager(),
                                resource_provider_.get(), true);
-  CompositorFrame aggregated_frame = aggregator.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator.Aggregate(root_surface_id, GetNextDisplayTime());
 
   bool discard_alpha = false;
   cc::ExactPixelComparator pixel_comparator(discard_alpha);
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index 0ad33ed..768aa885 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -13,9 +13,11 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/stringprintf.h"
+#include "base/time/time.h"
 #include "cc/resources/display_resource_provider.h"
 #include "cc/test/fake_resource_provider.h"
 #include "cc/test/render_pass_test_utils.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/render_pass.h"
 #include "components/viz/common/quads/render_pass_draw_quad.h"
@@ -77,9 +79,11 @@
         weak_ptr_factory_.GetWeakPtr());
   }
 
-  MOCK_METHOD2(OnAggregatedDamage,
+  MOCK_METHOD4(OnAggregatedDamage,
                void(const LocalSurfaceId& local_surface_id,
-                    const gfx::Rect& damage_rect));
+                    const gfx::Size& frame_size_in_pixels,
+                    const gfx::Rect& damage_rect,
+                    base::TimeTicks expected_display_time));
 
  private:
   base::WeakPtrFactory<MockAggregatedDamageCallback> weak_ptr_factory_;
@@ -87,7 +91,22 @@
   DISALLOW_COPY_AND_ASSIGN(MockAggregatedDamageCallback);
 };
 
-class SurfaceAggregatorTest : public testing::Test {
+class DisplayTimeSource {
+ public:
+  base::TimeTicks next_display_time() const { return next_display_time_; }
+
+  base::TimeTicks GetNextDisplayTimeAndIncrement() {
+    const base::TimeTicks display_time = next_display_time_;
+    next_display_time_ += BeginFrameArgs::DefaultInterval();
+    return display_time;
+  }
+
+ private:
+  base::TimeTicks next_display_time_ =
+      base::TimeTicks() + base::TimeDelta::FromSeconds(1);
+};
+
+class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource {
  public:
   explicit SurfaceAggregatorTest(bool use_damage_rect)
       : observer_(false),
@@ -372,7 +391,8 @@
                           SurfaceId* surface_ids,
                           size_t expected_surface_count) {
     CompositorFrame aggregated_frame = aggregator_.Aggregate(
-        SurfaceId(support_->frame_sink_id(), root_local_surface_id_));
+        SurfaceId(support_->frame_sink_id(), root_local_surface_id_),
+        GetNextDisplayTimeAndIncrement());
 
     TestPassesMatchExpectations(expected_passes, expected_pass_count,
                                 &aggregated_frame.render_pass_list);
@@ -462,7 +482,8 @@
   // Check that the AggregatedDamageCallback is called with the right arguments.
   EXPECT_CALL(
       aggregated_damage_callback,
-      OnAggregatedDamage(root_local_surface_id_, gfx::Rect(SurfaceSize())));
+      OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                         gfx::Rect(SurfaceSize()), next_display_time()));
 
   AggregateAndVerify(passes, arraysize(passes), ids, arraysize(ids));
 
@@ -498,7 +519,8 @@
                         root_local_surface_id_, device_scale_factor);
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   auto& render_pass_list = aggregated_frame.render_pass_list;
   ASSERT_EQ(2u, render_pass_list.size());
@@ -542,7 +564,8 @@
                         root_local_surface_id_, device_scale_factor);
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   auto& render_pass_list = aggregated_frame.render_pass_list;
   EXPECT_EQ(2u, render_pass_list.size());
@@ -587,13 +610,15 @@
   SurfaceId surface_id(support_->frame_sink_id(), root_local_surface_id_);
 
   CompositorFrame aggregated_frame;
-  aggregated_frame = aggregator_.Aggregate(surface_id);
+  aggregated_frame =
+      aggregator_.Aggregate(surface_id, GetNextDisplayTimeAndIncrement());
   auto id0 = aggregated_frame.render_pass_list[0]->id;
   auto id1 = aggregated_frame.render_pass_list[1]->id;
   EXPECT_NE(id1, id0);
 
   // Aggregated RenderPass ids should remain the same between frames.
-  aggregated_frame = aggregator_.Aggregate(surface_id);
+  aggregated_frame =
+      aggregator_.Aggregate(surface_id, GetNextDisplayTimeAndIncrement());
   EXPECT_EQ(id0, aggregated_frame.render_pass_list[0]->id);
   EXPECT_EQ(id1, aggregated_frame.render_pass_list[1]->id);
 
@@ -604,7 +629,8 @@
                         root_local_surface_id_, device_scale_factor);
 
   // The RenderPass that still exists should keep the same ID.
-  aggregated_frame = aggregator_.Aggregate(surface_id);
+  aggregated_frame =
+      aggregator_.Aggregate(surface_id, GetNextDisplayTimeAndIncrement());
   auto id2 = aggregated_frame.render_pass_list[0]->id;
   EXPECT_NE(id2, id1);
   EXPECT_NE(id2, id0);
@@ -615,7 +641,8 @@
 
   // |id1| didn't exist in the previous frame, so it should be
   // mapped to a new ID.
-  aggregated_frame = aggregator_.Aggregate(surface_id);
+  aggregated_frame =
+      aggregator_.Aggregate(surface_id, GetNextDisplayTimeAndIncrement());
   auto id3 = aggregated_frame.render_pass_list[0]->id;
   EXPECT_NE(id3, id2);
   EXPECT_NE(id3, id1);
@@ -736,16 +763,18 @@
   SurfaceId ids[] = {root_surface_id, fallback_child_surface_id};
 
   EXPECT_CALL(aggregated_damage_callback,
-              OnAggregatedDamage(fallback_child_local_surface_id, _))
+              OnAggregatedDamage(fallback_child_local_surface_id, fallback_size,
+                                 gfx::Rect(fallback_size), next_display_time()))
       .Times(1);
   EXPECT_CALL(aggregated_damage_callback,
-              OnAggregatedDamage(primary_child_local_surface_id, _))
+              OnAggregatedDamage(primary_child_local_surface_id, SurfaceSize(),
+                                 gfx::Rect(SurfaceSize()), next_display_time()))
       .Times(0);
   // The whole root surface should be damaged because this is the first
   // aggregation.
-  EXPECT_CALL(
-      aggregated_damage_callback,
-      OnAggregatedDamage(root_local_surface_id_, gfx::Rect(SurfaceSize())))
+  EXPECT_CALL(aggregated_damage_callback,
+              OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                                 gfx::Rect(SurfaceSize()), next_display_time()))
       .Times(1);
 
   // The primary_surface will not be listed in previously contained surfaces.
@@ -762,7 +791,8 @@
 
   // The damage should be equal to whole size of the primary SurfaceDrawQuad.
   EXPECT_CALL(aggregated_damage_callback,
-              OnAggregatedDamage(root_local_surface_id_, surface_quad_rect))
+              OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                                 surface_quad_rect, next_display_time()))
       .Times(1);
 
   AggregateAndVerify(expected_passes1, arraysize(expected_passes1), ids,
@@ -793,17 +823,21 @@
 
   SurfaceId ids2[] = {root_surface_id, primary_child_surface_id};
 
-  EXPECT_CALL(aggregated_damage_callback,
-              OnAggregatedDamage(primary_child_local_surface_id, _))
+  EXPECT_CALL(
+      aggregated_damage_callback,
+      OnAggregatedDamage(primary_child_local_surface_id, primary_surface_size,
+                         gfx::Rect(primary_surface_size), next_display_time()))
       .Times(1);
   EXPECT_CALL(aggregated_damage_callback,
-              OnAggregatedDamage(fallback_child_local_surface_id, _))
+              OnAggregatedDamage(fallback_child_local_surface_id, fallback_size,
+                                 gfx::Rect(fallback_size), next_display_time()))
       .Times(0);
   // The damage of the root should be equal to the damage of the primary
   // surface.
-  EXPECT_CALL(aggregated_damage_callback,
-              OnAggregatedDamage(root_local_surface_id_,
-                                 gfx::Rect(primary_surface_size)))
+  EXPECT_CALL(
+      aggregated_damage_callback,
+      OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                         gfx::Rect(primary_surface_size), next_display_time()))
       .Times(1);
 
   AggregateAndVerify(expected_passes2, arraysize(expected_passes2), ids2,
@@ -854,9 +888,12 @@
   SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
                         root_local_surface_id_, 1.0f);
 
-  EXPECT_CALL(aggregated_damage_callback,
-              OnAggregatedDamage(root_local_surface_id_, _));
-  CompositorFrame frame = aggregator_.Aggregate(root_surface_id);
+  EXPECT_CALL(
+      aggregated_damage_callback,
+      OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                         gfx::Rect(SurfaceSize()), next_display_time()));
+  CompositorFrame frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
   testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
 
   EXPECT_EQ(1u, frame.render_pass_list.size());
@@ -919,9 +956,12 @@
   SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
                         root_local_surface_id_, 2.0f);
 
-  EXPECT_CALL(aggregated_damage_callback,
-              OnAggregatedDamage(root_local_surface_id_, _));
-  CompositorFrame frame = aggregator_.Aggregate(root_surface_id);
+  EXPECT_CALL(
+      aggregated_damage_callback,
+      OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                         gfx::Rect(SurfaceSize()), next_display_time()));
+  CompositorFrame frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
 
@@ -985,9 +1025,12 @@
   SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
                         root_local_surface_id_, 0.5f);
 
-  EXPECT_CALL(aggregated_damage_callback,
-              OnAggregatedDamage(root_local_surface_id_, _));
-  CompositorFrame frame = aggregator_.Aggregate(root_surface_id);
+  EXPECT_CALL(
+      aggregated_damage_callback,
+      OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                         gfx::Rect(SurfaceSize()), next_display_time()));
+  CompositorFrame frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
   testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
 
   EXPECT_EQ(1u, frame.render_pass_list.size());
@@ -1075,7 +1118,8 @@
   SurfaceId ids[] = {root_surface_id, primary_child_surface_id};
 
   EXPECT_CALL(aggregated_damage_callback,
-              OnAggregatedDamage(root_local_surface_id_, gfx::Rect(root_size)));
+              OnAggregatedDamage(root_local_surface_id_, root_size,
+                                 gfx::Rect(root_size), next_display_time()));
 
   // The fallback will not be contained within the aggregated frame.
   AggregateAndVerify(expected_passes1, arraysize(expected_passes1), ids,
@@ -1089,9 +1133,9 @@
                         primary_child_local_surface_id, device_scale_factor);
 
   // The size of the damage should be equal to the size of the primary surface.
-  EXPECT_CALL(
-      aggregated_damage_callback,
-      OnAggregatedDamage(root_local_surface_id_, gfx::Rect(primary_size)));
+  EXPECT_CALL(aggregated_damage_callback,
+              OnAggregatedDamage(root_local_surface_id_, root_size,
+                                 gfx::Rect(primary_size), next_display_time()));
 
   // Generate a new aggregated frame.
   AggregateAndVerify(expected_passes1, arraysize(expected_passes1), ids,
@@ -1132,7 +1176,8 @@
                         root_local_surface_id_, device_scale_factor);
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   Quad expected_quads[] = {
       Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 5)),
@@ -1201,7 +1246,8 @@
   }
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   Quad expected_quads[] = {
       Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 5)),
@@ -1305,7 +1351,8 @@
   }
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   // First pass should come from surface that had a copy request but was not
   // referenced directly. The second pass comes from the root surface.
@@ -1378,7 +1425,8 @@
                         root_local_surface_id_, device_scale_factor);
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -1671,7 +1719,8 @@
                         root_local_surface_id_, device_scale_factor);
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -1847,7 +1896,8 @@
                    device_scale_factor, support_.get());
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -1978,7 +2028,8 @@
                                   std::move(root_frame));
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -2051,6 +2102,11 @@
 
 // Tests that damage rects are aggregated correctly when surfaces change.
 TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRect) {
+  // Add a callback for when the surface is damaged.
+  MockAggregatedDamageCallback aggregated_damage_callback;
+  support_->SetAggregatedDamageCallbackForTesting(
+      aggregated_damage_callback.GetCallback());
+
   auto parent_support = std::make_unique<CompositorFrameSinkSupport>(
       nullptr, &manager_, kArbitraryMiddleFrameSinkId, kChildIsRoot,
       kNeedsSyncPoints);
@@ -2112,16 +2168,20 @@
   support_->SubmitCompositorFrame(root_local_surface_id_,
                                   std::move(root_frame));
 
+  // Damage rect for first aggregation should contain entire root surface. The
+  // damage rect reported to the callback is actually 10 pixels taller because
+  // of the 10-pixel vertical translation of the first RenderPass.
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
-
+  EXPECT_CALL(
+      aggregated_damage_callback,
+      OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                         gfx::Rect(0, 0, 100, 110), next_display_time()));
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
+  testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
-
   ASSERT_EQ(2u, aggregated_pass_list.size());
-
-  // Damage rect for first aggregation should contain entire root surface.
-  EXPECT_TRUE(
-      aggregated_pass_list[1]->damage_rect.Contains(gfx::Rect(SurfaceSize())));
+  EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[1]->damage_rect);
 
   {
     CompositorFrame child_frame = MakeEmptyCompositorFrame();
@@ -2136,17 +2196,19 @@
     child_support_->SubmitCompositorFrame(child_local_surface_id,
                                           std::move(child_frame));
 
+    // Outer surface didn't change, so a transformed inner damage rect is
+    // expected.
     SurfaceId root_surface_id(support_->frame_sink_id(),
                               root_local_surface_id_);
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
-
+    const gfx::Rect expected_damage_rect(10, 20, 10, 10);
+    EXPECT_CALL(aggregated_damage_callback,
+                OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                                   expected_damage_rect, next_display_time()));
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
-
     ASSERT_EQ(2u, aggregated_pass_list.size());
-
-    // Outer surface didn't change, so transformed inner damage rect should be
-    // used.
-    EXPECT_EQ(gfx::Rect(10, 20, 10, 10).ToString(),
+    EXPECT_EQ(expected_damage_rect.ToString(),
               aggregated_pass_list[1]->damage_rect.ToString());
   }
 
@@ -2177,30 +2239,31 @@
     support_->SubmitCompositorFrame(root_local_surface_id_,
                                     std::move(root_frame));
 
-    SurfaceId root_surface_id(support_->frame_sink_id(),
-                              root_local_surface_id_);
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
-
-    const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
-
-    ASSERT_EQ(2u, aggregated_pass_list.size());
-
     // The root surface was enqueued without being aggregated once, so it should
     // be treated as completely damaged.
-    EXPECT_TRUE(aggregated_pass_list[1]->damage_rect.Contains(
-        gfx::Rect(SurfaceSize())));
+    SurfaceId root_surface_id(support_->frame_sink_id(),
+                              root_local_surface_id_);
+    EXPECT_CALL(
+        aggregated_damage_callback,
+        OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                           gfx::Rect(SurfaceSize()), next_display_time()));
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
+    const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+    ASSERT_EQ(2u, aggregated_pass_list.size());
+    EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[1]->damage_rect);
   }
 
   // No Surface changed, so no damage should be given.
   {
     SurfaceId root_surface_id(support_->frame_sink_id(),
                               root_local_surface_id_);
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
-
+    EXPECT_CALL(aggregated_damage_callback, OnAggregatedDamage(_, _, _, _))
+        .Times(0);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
-
     ASSERT_EQ(2u, aggregated_pass_list.size());
-
     EXPECT_TRUE(aggregated_pass_list[1]->damage_rect.IsEmpty());
   }
 
@@ -2208,12 +2271,14 @@
   // marked as damaged.
   {
     aggregator_.SetFullDamageForSurface(root_surface_id);
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
-
+    EXPECT_CALL(
+        aggregated_damage_callback,
+        OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                           gfx::Rect(SurfaceSize()), next_display_time()));
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
-
     ASSERT_EQ(2u, aggregated_pass_list.size());
-
     EXPECT_TRUE(aggregated_pass_list[1]->damage_rect.Contains(
         gfx::Rect(SurfaceSize())));
   }
@@ -2222,6 +2287,11 @@
 // Tests that damage rects are aggregated correctly when surfaces stretch to
 // fit and device size is less than 1.
 TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithSquashToFit) {
+  // Add a callback for when the surface is damaged.
+  MockAggregatedDamageCallback aggregated_damage_callback;
+  support_->SetAggregatedDamageCallbackForTesting(
+      aggregated_damage_callback.GetCallback());
+
   auto parent_support = std::make_unique<CompositorFrameSinkSupport>(
       nullptr, &manager_, kArbitraryMiddleFrameSinkId, kChildIsRoot,
       kNeedsSyncPoints);
@@ -2271,28 +2341,22 @@
       Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2,
            SurfaceSize())};
 
-  CompositorFrame root_frame = MakeEmptyCompositorFrame();
-  AddPasses(&root_frame.render_pass_list, root_passes, arraysize(root_passes));
-
-  root_frame.render_pass_list[0]
-      ->shared_quad_state_list.front()
-      ->quad_to_target_transform.Translate(0, 10);
-  root_frame.render_pass_list[0]->damage_rect = gfx::Rect(5, 5, 10, 10);
-  root_frame.render_pass_list[1]->damage_rect = gfx::Rect(5, 5, 100, 100);
-
   SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
                         root_local_surface_id_, 1.0f);
 
+  // Damage rect for first aggregation should be exactly the entire root
+  // surface.
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
-
+  EXPECT_CALL(
+      aggregated_damage_callback,
+      OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                         gfx::Rect(SurfaceSize()), next_display_time()));
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
+  testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
-
   ASSERT_EQ(2u, aggregated_pass_list.size());
-
-  // Damage rect for first aggregation should contain entire root surface.
-  EXPECT_TRUE(
-      aggregated_pass_list[1]->damage_rect.Contains(gfx::Rect(SurfaceSize())));
+  EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[1]->damage_rect);
 
   {
     CompositorFrame child_frame = MakeEmptyCompositorFrame();
@@ -2305,19 +2369,21 @@
     child_support_->SubmitCompositorFrame(child_local_surface_id,
                                           std::move(child_frame));
 
-    SurfaceId root_surface_id(support_->frame_sink_id(),
-                              root_local_surface_id_);
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
-
-    const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
-
-    ASSERT_EQ(2u, aggregated_pass_list.size());
-
     // Outer surface didn't change, so transformed inner damage rect should be
     // used. Since the child surface is stretching to fit the outer surface
     // which is half the size, we end up with a damage rect that is half the
     // size of the child surface.
-    EXPECT_EQ(gfx::Rect(5, 10, 10, 15).ToString(),
+    SurfaceId root_surface_id(support_->frame_sink_id(),
+                              root_local_surface_id_);
+    const gfx::Rect expected_damage_rect(5, 10, 10, 15);
+    EXPECT_CALL(aggregated_damage_callback,
+                OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                                   expected_damage_rect, next_display_time()));
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
+    const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+    ASSERT_EQ(2u, aggregated_pass_list.size());
+    EXPECT_EQ(expected_damage_rect.ToString(),
               aggregated_pass_list[1]->damage_rect.ToString());
   }
 }
@@ -2325,6 +2391,11 @@
 // Tests that damage rects are aggregated correctly when surfaces stretch to
 // fit and device size is greater than 1.
 TEST_F(SurfaceAggregatorValidSurfaceTest, AggregateDamageRectWithStretchToFit) {
+  // Add a callback for when the surface is damaged.
+  MockAggregatedDamageCallback aggregated_damage_callback;
+  support_->SetAggregatedDamageCallbackForTesting(
+      aggregated_damage_callback.GetCallback());
+
   auto parent_support = std::make_unique<CompositorFrameSinkSupport>(
       nullptr, &manager_, kArbitraryMiddleFrameSinkId, kChildIsRoot,
       kNeedsSyncPoints);
@@ -2374,28 +2445,23 @@
       Pass(root_render_pass_quads, arraysize(root_render_pass_quads), 2,
            SurfaceSize())};
 
-  CompositorFrame root_frame = MakeEmptyCompositorFrame();
-  AddPasses(&root_frame.render_pass_list, root_passes, arraysize(root_passes));
-
-  root_frame.render_pass_list[0]
-      ->shared_quad_state_list.front()
-      ->quad_to_target_transform.Translate(0, 10);
-  root_frame.render_pass_list[0]->damage_rect = gfx::Rect(5, 5, 10, 10);
-  root_frame.render_pass_list[1]->damage_rect = gfx::Rect(5, 5, 100, 100);
-
   SubmitCompositorFrame(support_.get(), root_passes, arraysize(root_passes),
                         root_local_surface_id_, 1.0f);
 
+  // Damage rect for first aggregation should contain entire root surface. The
+  // damage rect reported to the callback is actually 200x200, larger than the
+  // root surface size, because the root's Quad is 200x200.
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
-
+  EXPECT_CALL(
+      aggregated_damage_callback,
+      OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                         gfx::Rect(0, 0, 200, 200), next_display_time()));
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
+  testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
-
   ASSERT_EQ(2u, aggregated_pass_list.size());
-
-  // Damage rect for first aggregation should contain entire root surface.
-  EXPECT_TRUE(
-      aggregated_pass_list[1]->damage_rect.Contains(gfx::Rect(SurfaceSize())));
+  EXPECT_EQ(gfx::Rect(SurfaceSize()), aggregated_pass_list[1]->damage_rect);
 
   {
     CompositorFrame child_frame = MakeEmptyCompositorFrame();
@@ -2408,19 +2474,21 @@
     child_support_->SubmitCompositorFrame(child_local_surface_id,
                                           std::move(child_frame));
 
-    SurfaceId root_surface_id(support_->frame_sink_id(),
-                              root_local_surface_id_);
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
-
-    const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
-
-    ASSERT_EQ(2u, aggregated_pass_list.size());
-
     // Outer surface didn't change, so transformed inner damage rect should be
     // used. Since the child surface is stretching to fit the outer surface
     // which is twice the size, we end up with a damage rect that is double the
     // size of the child surface.
-    EXPECT_EQ(gfx::Rect(20, 30, 40, 60).ToString(),
+    SurfaceId root_surface_id(support_->frame_sink_id(),
+                              root_local_surface_id_);
+    const gfx::Rect expected_damage_rect(20, 30, 40, 60);
+    EXPECT_CALL(aggregated_damage_callback,
+                OnAggregatedDamage(root_local_surface_id_, SurfaceSize(),
+                                   expected_damage_rect, next_display_time()));
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
+    const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
+    ASSERT_EQ(2u, aggregated_pass_list.size());
+    EXPECT_EQ(expected_damage_rect.ToString(),
               aggregated_pass_list[1]->damage_rect.ToString());
   }
 }
@@ -2444,7 +2512,8 @@
   {
     SurfaceId root_surface_id(support_->frame_sink_id(),
                               root_local_surface_id_);
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
 
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -2475,8 +2544,8 @@
                                     std::move(root_frame));
   }
   {
-    CompositorFrame aggregated_frame =
-        aggregator_.Aggregate(second_root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        second_root_surface_id, GetNextDisplayTimeAndIncrement());
 
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -2485,8 +2554,8 @@
     EXPECT_EQ(gfx::Rect(1, 2, 3, 4), aggregated_pass_list[0]->damage_rect);
   }
   {
-    CompositorFrame aggregated_frame =
-        aggregator_.Aggregate(second_root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        second_root_surface_id, GetNextDisplayTimeAndIncrement());
 
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -2572,7 +2641,8 @@
   }
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -2605,7 +2675,8 @@
   }
 
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
 
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -2654,7 +2725,8 @@
   }
 
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
 
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -2674,7 +2746,8 @@
   }
 
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
 
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
     // There were no changes since last aggregation, so output should be empty
@@ -2714,7 +2787,8 @@
   }
 
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
 
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -2763,7 +2837,8 @@
   }
 
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
     ASSERT_EQ(3u, aggregated_pass_list.size());
@@ -2813,7 +2888,8 @@
   }
 
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
 
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -2833,7 +2909,8 @@
   }
 }
 
-class SurfaceAggregatorWithResourcesTest : public testing::Test {
+class SurfaceAggregatorWithResourcesTest : public testing::Test,
+                                           public DisplayTimeSource {
  public:
   void SetUp() override {
     shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
@@ -2910,7 +2987,8 @@
   SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
                                      support.get(), surface_id);
 
-  CompositorFrame frame = aggregator_->Aggregate(surface_id);
+  CompositorFrame frame =
+      aggregator_->Aggregate(surface_id, GetNextDisplayTimeAndIncrement());
 
   // Nothing should be available to be returned yet.
   EXPECT_TRUE(client.returned_resources().empty());
@@ -2918,7 +2996,7 @@
   SubmitCompositorFrameWithResources(nullptr, 0u, true, SurfaceId(),
                                      support.get(), surface_id);
 
-  frame = aggregator_->Aggregate(surface_id);
+  frame = aggregator_->Aggregate(surface_id, GetNextDisplayTimeAndIncrement());
 
   ASSERT_EQ(3u, client.returned_resources().size());
   ResourceId returned_ids[3];
@@ -2946,7 +3024,8 @@
   SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
                                      support.get(), surface_id1);
 
-  CompositorFrame frame = aggregator_->Aggregate(surface_id1);
+  CompositorFrame frame =
+      aggregator_->Aggregate(surface_id1, GetNextDisplayTimeAndIncrement());
 
   // Nothing should be available to be returned yet.
   EXPECT_TRUE(client.returned_resources().empty());
@@ -2957,7 +3036,7 @@
                                      support.get(), surface_id2);
   manager_.surface_manager()->GarbageCollectSurfaces();
 
-  frame = aggregator_->Aggregate(surface_id2);
+  frame = aggregator_->Aggregate(surface_id2, GetNextDisplayTimeAndIncrement());
 
   ASSERT_EQ(3u, client.returned_resources().size());
   ResourceId returned_ids[3];
@@ -2988,7 +3067,8 @@
                               .Build();
   support->SubmitCompositorFrame(local_surface_id, std::move(frame));
 
-  CompositorFrame returned_frame = aggregator_->Aggregate(surface_id);
+  CompositorFrame returned_frame =
+      aggregator_->Aggregate(surface_id, GetNextDisplayTimeAndIncrement());
 
   // Nothing should be available to be returned yet.
   EXPECT_TRUE(client.returned_resources().empty());
@@ -3018,7 +3098,8 @@
   SubmitCompositorFrameWithResources(ids2, arraysize(ids2), true, SurfaceId(),
                                      support2.get(), surface2_id);
 
-  CompositorFrame frame = aggregator_->Aggregate(surface1_id);
+  CompositorFrame frame =
+      aggregator_->Aggregate(surface1_id, GetNextDisplayTimeAndIncrement());
 
   SubmitCompositorFrameWithResources(nullptr, 0, true, SurfaceId(),
                                      support1.get(), surface1_id);
@@ -3026,7 +3107,7 @@
   // Nothing should be available to be returned yet.
   EXPECT_TRUE(client.returned_resources().empty());
 
-  frame = aggregator_->Aggregate(surface2_id);
+  frame = aggregator_->Aggregate(surface2_id, GetNextDisplayTimeAndIncrement());
 
   // surface1_id wasn't referenced, so its resources should be returned.
   ASSERT_EQ(3u, client.returned_resources().size());
@@ -3076,7 +3157,8 @@
                                      root_surface_id);
 
   CompositorFrame frame;
-  frame = aggregator_->Aggregate(root_surface_id);
+  frame =
+      aggregator_->Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   auto* pass_list = &frame.render_pass_list;
   ASSERT_EQ(1u, pass_list->size());
@@ -3086,7 +3168,8 @@
                                      child_surface_id, middle_support.get(),
                                      middle_surface_id);
 
-  frame = aggregator_->Aggregate(root_surface_id);
+  frame =
+      aggregator_->Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   pass_list = &frame.render_pass_list;
   ASSERT_EQ(1u, pass_list->size());
@@ -3110,7 +3193,8 @@
   SubmitCompositorFrameWithResources(ids, arraysize(ids), true, SurfaceId(),
                                      support1.get(), surface1_id);
 
-  CompositorFrame frame = aggregator_->Aggregate(surface1_id);
+  CompositorFrame frame =
+      aggregator_->Aggregate(surface1_id, GetNextDisplayTimeAndIncrement());
 
   auto* render_pass = frame.render_pass_list.back().get();
 
@@ -3133,14 +3217,14 @@
     support2->SubmitCompositorFrame(local_frame2_id, std::move(frame));
   }
 
-  frame = aggregator_->Aggregate(surface2_id);
+  frame = aggregator_->Aggregate(surface2_id, GetNextDisplayTimeAndIncrement());
   EXPECT_EQ(1u, frame.render_pass_list.size());
   render_pass = frame.render_pass_list.front().get();
 
   // Parent has copy request, so texture should not be drawn.
   EXPECT_EQ(DrawQuad::SOLID_COLOR, render_pass->quad_list.back()->material);
 
-  frame = aggregator_->Aggregate(surface2_id);
+  frame = aggregator_->Aggregate(surface2_id, GetNextDisplayTimeAndIncrement());
   EXPECT_EQ(1u, frame.render_pass_list.size());
   render_pass = frame.render_pass_list.front().get();
 
@@ -3150,7 +3234,7 @@
 
   aggregator_->set_output_is_secure(false);
 
-  frame = aggregator_->Aggregate(surface2_id);
+  frame = aggregator_->Aggregate(surface2_id, GetNextDisplayTimeAndIncrement());
   render_pass = frame.render_pass_list.back().get();
 
   // Output is insecure, so texture should be drawn.
@@ -3177,19 +3261,22 @@
 
   CompositorFrame aggregated_frame;
   aggregator_.SetOutputColorSpace(color_space1, color_space1);
-  aggregated_frame = aggregator_.Aggregate(surface_id);
+  aggregated_frame =
+      aggregator_.Aggregate(surface_id, GetNextDisplayTimeAndIncrement());
   EXPECT_EQ(2u, aggregated_frame.render_pass_list.size());
   EXPECT_EQ(color_space1, aggregated_frame.render_pass_list[0]->color_space);
   EXPECT_EQ(color_space1, aggregated_frame.render_pass_list[1]->color_space);
 
   aggregator_.SetOutputColorSpace(color_space2, color_space2);
-  aggregated_frame = aggregator_.Aggregate(surface_id);
+  aggregated_frame =
+      aggregator_.Aggregate(surface_id, GetNextDisplayTimeAndIncrement());
   EXPECT_EQ(2u, aggregated_frame.render_pass_list.size());
   EXPECT_EQ(color_space2, aggregated_frame.render_pass_list[0]->color_space);
   EXPECT_EQ(color_space2, aggregated_frame.render_pass_list[1]->color_space);
 
   aggregator_.SetOutputColorSpace(color_space1, color_space3);
-  aggregated_frame = aggregator_.Aggregate(surface_id);
+  aggregated_frame =
+      aggregator_.Aggregate(surface_id, GetNextDisplayTimeAndIncrement());
   EXPECT_EQ(3u, aggregated_frame.render_pass_list.size());
   EXPECT_EQ(color_space1, aggregated_frame.render_pass_list[0]->color_space);
   EXPECT_EQ(color_space1, aggregated_frame.render_pass_list[1]->color_space);
@@ -3228,11 +3315,13 @@
 
   // On first frame there is no existing cache texture to worry about re-using,
   // so we don't worry what this bool is set to.
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   // No Surface changed, so no damage should be given.
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     EXPECT_FALSE(aggregated_frame.render_pass_list[0]
                      ->has_damage_from_contributing_content);
   }
@@ -3245,7 +3334,8 @@
     child_support_->SubmitCompositorFrame(child_local_surface_id,
                                           std::move(child_surface_frame));
 
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     // True for new child_frame with damage.
     EXPECT_TRUE(aggregated_frame.render_pass_list[0]
                     ->has_damage_from_contributing_content);
@@ -3260,7 +3350,8 @@
     child_support_->SubmitCompositorFrame(child_local_surface_id,
                                           std::move(child_surface_frame));
 
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     // False for new child_frame without damage.
     EXPECT_FALSE(aggregated_frame.render_pass_list[0]
                      ->has_damage_from_contributing_content);
@@ -3304,11 +3395,13 @@
 
   // On first frame there is no existing cache texture to worry about re-using,
   // so we don't worry what this bool is set to.
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   // No Surface changed, so no damage should be given.
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     EXPECT_FALSE(aggregated_frame.render_pass_list[0]
                      ->has_damage_from_contributing_content);
   }
@@ -3341,7 +3434,8 @@
     child_support_->SubmitCompositorFrame(child_local_surface_id,
                                           std::move(child_surface_frame));
 
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     // True for new grand_child_frame.
     EXPECT_TRUE(aggregated_frame.render_pass_list[0]
                     ->has_damage_from_contributing_content);
@@ -3349,7 +3443,8 @@
 
   // No Surface changed, so no damage should be given.
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     EXPECT_FALSE(aggregated_frame.render_pass_list[0]
                      ->has_damage_from_contributing_content);
   }
@@ -3362,7 +3457,8 @@
     grand_child_support->SubmitCompositorFrame(grand_child_local_surface_id,
                                                std::move(grand_child_frame));
 
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     // True for new grand_child_frame with damage.
     EXPECT_TRUE(aggregated_frame.render_pass_list[0]
                     ->has_damage_from_contributing_content);
@@ -3377,7 +3473,8 @@
     grand_child_support->SubmitCompositorFrame(grand_child_local_surface_id,
                                                std::move(grand_child_frame));
 
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     // False for new grand_child_frame without damage.
     EXPECT_FALSE(aggregated_frame.render_pass_list[0]
                      ->has_damage_from_contributing_content);
@@ -3418,7 +3515,8 @@
                                   std::move(root_frame));
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   // On first frame there is no existing cache texture to worry about re-using,
   // so we don't worry what this bool is set to.
@@ -3428,7 +3526,8 @@
 
   // No Surface changed, so no damage should be given.
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     EXPECT_FALSE(aggregated_frame.render_pass_list[0]
                      ->has_damage_from_contributing_content);
     EXPECT_FALSE(aggregated_frame.render_pass_list[1]
@@ -3443,7 +3542,8 @@
     child_support_->SubmitCompositorFrame(child_local_surface_id,
                                           std::move(child_frame));
 
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     // True for new child_frame.
     EXPECT_TRUE(aggregated_frame.render_pass_list[0]
                     ->has_damage_from_contributing_content);
@@ -3471,7 +3571,8 @@
                                   std::move(root_frame));
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -3502,7 +3603,8 @@
     support_->SubmitCompositorFrame(root_local_surface_id_,
                                     std::move(root_frame));
 
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
     // Only the visible area is damaged.
@@ -3529,7 +3631,8 @@
     support_->SubmitCompositorFrame(root_local_surface_id_,
                                     std::move(root_frame));
 
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
     // Should have full damage.
@@ -3576,7 +3679,8 @@
                                   std::move(root_frame));
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -3607,7 +3711,8 @@
     child_support_->SubmitCompositorFrame(child_local_surface_id,
                                           std::move(child_frame));
 
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
     // Only the visible area is damaged.
@@ -3634,7 +3739,8 @@
     child_support_->SubmitCompositorFrame(child_local_surface_id,
                                           std::move(child_frame));
 
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
     // Should have full damage.
@@ -3707,7 +3813,8 @@
   }
 
   SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
-  CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
 
   const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
@@ -3745,7 +3852,8 @@
   }
 
   {
-    CompositorFrame aggregated_frame = aggregator_.Aggregate(root_surface_id);
+    CompositorFrame aggregated_frame = aggregator_.Aggregate(
+        root_surface_id, GetNextDisplayTimeAndIncrement());
     const auto& aggregated_pass_list = aggregated_frame.render_pass_list;
 
     ASSERT_EQ(3u, aggregated_pass_list.size());
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index d96429b6..d3c3f5a 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -426,8 +426,6 @@
   last_begin_frame_args_ = args;
   if (client_)
     client_->OnBeginFrame(args);
-  for (CapturableFrameSink::Client* capture_client : capture_clients_)
-    capture_client->OnBeginFrame(args);
 }
 
 const BeginFrameArgs& CompositorFrameSinkSupport::LastUsedBeginFrameArgs()
@@ -525,17 +523,20 @@
 
 void CompositorFrameSinkSupport::OnAggregatedDamage(
     const LocalSurfaceId& local_surface_id,
+    const gfx::Size& frame_size_in_pixels,
     const gfx::Rect& damage_rect,
-    const CompositorFrame& frame) const {
+    base::TimeTicks expected_display_time) const {
   DCHECK(!damage_rect.IsEmpty());
 
-  if (aggregated_damage_callback_)
-    aggregated_damage_callback_.Run(local_surface_id, damage_rect);
+  if (aggregated_damage_callback_) {
+    aggregated_damage_callback_.Run(local_surface_id, frame_size_in_pixels,
+                                    damage_rect, expected_display_time);
+  }
 
-  const BeginFrameAck& ack = frame.metadata.begin_frame_ack;
-  const gfx::Size& frame_size = frame.size_in_pixels();
-  for (CapturableFrameSink::Client* client : capture_clients_)
-    client->OnFrameDamaged(ack, frame_size, damage_rect);
+  for (CapturableFrameSink::Client* client : capture_clients_) {
+    client->OnFrameDamaged(frame_size_in_pixels, damage_rect,
+                           expected_display_time);
+  }
 }
 
 }  // namespace viz
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
index 6e179c3..d1612f6 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/surfaces/surface_info.h"
@@ -46,7 +47,9 @@
 
   using AggregatedDamageCallback =
       base::RepeatingCallback<void(const LocalSurfaceId& local_surface_id,
-                                   const gfx::Rect& damage_rect)>;
+                                   const gfx::Size& frame_size_in_pixels,
+                                   const gfx::Rect& damage_rect,
+                                   base::TimeTicks expected_display_time)>;
 
   static const uint64_t kFrameIndexStart = 2;
 
@@ -175,8 +178,9 @@
   Surface* CreateSurface(const SurfaceInfo& surface_info);
 
   void OnAggregatedDamage(const LocalSurfaceId& local_surface_id,
+                          const gfx::Size& frame_size_in_pixels,
                           const gfx::Rect& damage_rect,
-                          const CompositorFrame& frame) const;
+                          base::TimeTicks expected_display_time) const;
 
   mojom::CompositorFrameSinkClient* const client_;
 
diff --git a/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h b/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h
index 986efed..806133f8 100644
--- a/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h
+++ b/components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/time/time.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace gfx {
@@ -15,8 +16,6 @@
 
 namespace viz {
 
-struct BeginFrameAck;
-struct BeginFrameArgs;
 class CopyOutputRequest;
 
 // Interface for CompositorFrameSink implementations that support frame sink
@@ -29,16 +28,15 @@
    public:
     virtual ~Client() = default;
 
-    // Called to indicate compositing has started for a new frame.
-    virtual void OnBeginFrame(const BeginFrameArgs& args) = 0;
-
-    // Called to indicate a frame's content has changed since the last
-    // frame. |ack| identifies the frame. |frame_size| is the output size of the
-    // frame, with |damage_rect| being the region within the frame that has
-    // changed.
-    virtual void OnFrameDamaged(const BeginFrameAck& ack,
-                                const gfx::Size& frame_size,
-                                const gfx::Rect& damage_rect) = 0;
+    // Called when a frame's content, or that of one or more of its child
+    // frames, has changed. |frame_size| is the output size of the currently-
+    // active compositor frame for the frame sink being monitored, with
+    // |damage_rect| being the region within that has changed (never empty).
+    // |expected_display_time| indicates when the content change was expected to
+    // appear on the Display.
+    virtual void OnFrameDamaged(const gfx::Size& frame_size,
+                                const gfx::Rect& damage_rect,
+                                base::TimeTicks expected_display_time) = 0;
   };
 
   virtual ~CapturableFrameSink() = default;
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
index 0ee7870..1aaac05b 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -36,18 +36,6 @@
                                          std::numeric_limits<int>::max(),
                                          std::numeric_limits<int>::max());
 
-// Returns |raw_size| truncated to positive even-numbered values.
-gfx::Size AdjustSizeForI420(const gfx::Size& raw_size) {
-  gfx::Size result(raw_size.width() & ~1, raw_size.height() & ~1);
-  if (result.width() <= 0) {
-    result.set_width(2);
-  }
-  if (result.height() <= 0) {
-    result.set_height(2);
-  }
-  return result;
-}
-
 }  // namespace
 
 // static
@@ -57,10 +45,6 @@
 // static
 constexpr media::ColorSpace FrameSinkVideoCapturerImpl::kDefaultColorSpace;
 
-// static
-constexpr base::TimeDelta
-    FrameSinkVideoCapturerImpl::kDisplayTimeCacheKeepAliveInterval;
-
 FrameSinkVideoCapturerImpl::FrameSinkVideoCapturerImpl(
     FrameSinkVideoCapturerManager* frame_sink_manager,
     mojom::FrameSinkVideoCapturerRequest request)
@@ -132,8 +116,9 @@
 
   bool format_changed = false;
 
-  if (format != media::PIXEL_FORMAT_I420) {
-    LOG(DFATAL) << "Invalid pixel format: Only I420 supported.";
+  if (format != media::PIXEL_FORMAT_I420 &&
+      format != media::PIXEL_FORMAT_ARGB) {
+    LOG(DFATAL) << "Invalid pixel format: Only I420 and ARGB are supported.";
   } else {
     format_changed |= (pixel_format_ != format);
     pixel_format_ = format;
@@ -328,64 +313,16 @@
                     gfx::Rect(oracle_.source_size()), clock_->NowTicks());
 }
 
-void FrameSinkVideoCapturerImpl::OnBeginFrame(const BeginFrameArgs& args) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(args.IsValid());
-  DCHECK(resolved_target_);
-
-  // Note: It's possible that there are multiple BeginFrameSources that may call
-  // this method. It's not possible to know which one will be associated with a
-  // later OnFrameDamaged() call, so all recent timestamps must be cached.
-
-  const size_t prior_source_count = frame_display_times_.size();
-  TimeRingBuffer& ring_buffer = frame_display_times_[args.source_id];
-  const base::TimeTicks display_time = args.frame_time + args.interval;
-  DCHECK(!display_time.is_null());
-  ring_buffer[args.sequence_number % ring_buffer.size()] = display_time;
-
-  // Garbage-collect |frame_display_times_| entries that are no longer being
-  // actively updated. This only runs when this method is being called with an
-  // as-yet-unseen |args.source_id|. An entry is pruned only if all of its
-  // timestamps are older than a reasonable threshold.
-  if (frame_display_times_.size() != prior_source_count) {
-    const base::TimeTicks threshold =
-        display_time - kDisplayTimeCacheKeepAliveInterval;
-    using KeyValuePair = decltype(frame_display_times_)::value_type;
-    base::EraseIf(frame_display_times_, [&threshold](const KeyValuePair& p) {
-      const TimeRingBuffer& ring_buffer = p.second;
-      return std::all_of(ring_buffer.begin(), ring_buffer.end(),
-                         [&threshold](base::TimeTicks t) {
-                           return t.is_null() || t < threshold;
-                         });
-    });
-  }
-}
-
-void FrameSinkVideoCapturerImpl::OnFrameDamaged(const BeginFrameAck& ack,
-                                                const gfx::Size& frame_size,
-                                                const gfx::Rect& damage_rect) {
+void FrameSinkVideoCapturerImpl::OnFrameDamaged(
+    const gfx::Size& frame_size,
+    const gfx::Rect& damage_rect,
+    base::TimeTicks expected_display_time) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!frame_size.IsEmpty());
   DCHECK(!damage_rect.IsEmpty());
+  DCHECK(!expected_display_time.is_null());
   DCHECK(resolved_target_);
 
-  base::TimeTicks display_time;
-  const auto it = frame_display_times_.find(ack.source_id);
-  if (it != frame_display_times_.end()) {
-    const TimeRingBuffer& ring_buffer = it->second;
-    display_time = ring_buffer[ack.sequence_number % ring_buffer.size()];
-  }
-  if (display_time.is_null()) {
-    // This can sometimes occur for the first few frames when capture starts,
-    // or whenever Surfaces are changed; but should not otherwise happen. If
-    // this is too frequent, the oracle will be making suboptimal decisions.
-    VLOG(1)
-        << "OnFrameDamaged() called without prior OnBeginFrame() for source_id="
-        << ack.source_id << " and sequence_number=" << ack.sequence_number
-        << ". Using NOW as a substitute display time.";
-    display_time = clock_->NowTicks();
-  }
-
   if (frame_size == oracle_.source_size()) {
     dirty_rect_.Union(damage_rect);
   } else {
@@ -394,7 +331,7 @@
   }
 
   MaybeCaptureFrame(VideoCaptureOracle::kCompositorUpdate, damage_rect,
-                    display_time);
+                    expected_display_time);
 }
 
 void FrameSinkVideoCapturerImpl::MaybeCaptureFrame(
@@ -433,20 +370,20 @@
 
   // Reserve a buffer from the pool for the next frame.
   const OracleFrameNumber oracle_frame_number = oracle_.next_frame_number();
-  const gfx::Size i420_capture_size = AdjustSizeForI420(oracle_.capture_size());
+  const gfx::Size capture_size =
+      AdjustSizeForPixelFormat(oracle_.capture_size());
   scoped_refptr<VideoFrame> frame;
   if (dirty_rect_.IsEmpty()) {
-    frame =
-        frame_pool_.ResurrectLastVideoFrame(pixel_format_, i420_capture_size);
+    frame = frame_pool_.ResurrectLastVideoFrame(pixel_format_, capture_size);
     // If the resurrection failed, promote to a full frame capture.
     if (!frame) {
       TRACE_EVENT_INSTANT0("gpu.capture", "ResurrectionFailed",
                            TRACE_EVENT_SCOPE_THREAD);
       dirty_rect_ = kMaxRect;
-      frame = frame_pool_.ReserveVideoFrame(pixel_format_, i420_capture_size);
+      frame = frame_pool_.ReserveVideoFrame(pixel_format_, capture_size);
     }
   } else {
-    frame = frame_pool_.ReserveVideoFrame(pixel_format_, i420_capture_size);
+    frame = frame_pool_.ReserveVideoFrame(pixel_format_, capture_size);
   }
 
   // Compute the current in-flight utilization and attenuate it: The utilization
@@ -507,12 +444,23 @@
 
   const gfx::Size& source_size = oracle_.source_size();
   DCHECK(!source_size.IsEmpty());
-  const gfx::Rect content_rect =
-      media::ComputeLetterboxRegionForI420(frame->visible_rect(), source_size);
+  gfx::Rect content_rect;
+  if (pixel_format_ == media::PIXEL_FORMAT_I420) {
+    content_rect = media::ComputeLetterboxRegionForI420(frame->visible_rect(),
+                                                        source_size);
+  } else {
+    DCHECK_EQ(media::PIXEL_FORMAT_ARGB, pixel_format_);
+    content_rect =
+        media::ComputeLetterboxRegion(frame->visible_rect(), source_size);
+  }
   // Extreme edge-case: If somehow the source size is so tiny that the content
   // region becomes empty, just deliver a frame filled with black.
   if (content_rect.IsEmpty()) {
-    media::FillYUV(frame.get(), 0x00, 0x80, 0x80);
+    if (pixel_format_ == media::PIXEL_FORMAT_I420) {
+      media::FillYUV(frame.get(), 0x00, 0x80, 0x80);
+    } else {
+      media::LetterboxVideoFrame(frame.get(), gfx::Rect());
+    }
     dirty_rect_ = gfx::Rect();
     DidCaptureFrame(frame_number, oracle_frame_number, std::move(frame),
                     gfx::Rect());
@@ -528,7 +476,9 @@
 
   // Request a copy of the next frame from the frame sink.
   std::unique_ptr<CopyOutputRequest> request(new CopyOutputRequest(
-      CopyOutputRequest::ResultFormat::I420_PLANES,
+      pixel_format_ == media::PIXEL_FORMAT_I420
+          ? CopyOutputRequest::ResultFormat::I420_PLANES
+          : CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(&FrameSinkVideoCapturerImpl::DidCopyFrame,
                      capture_weak_factory_.GetWeakPtr(), frame_number,
                      oracle_frame_number, std::move(frame), content_rect)));
@@ -554,38 +504,61 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_GE(frame_number, next_delivery_frame_number_);
   DCHECK(frame);
-  DCHECK_EQ(content_rect.x() % 2, 0);
-  DCHECK_EQ(content_rect.y() % 2, 0);
-  DCHECK_EQ(content_rect.width() % 2, 0);
-  DCHECK_EQ(content_rect.height() % 2, 0);
   DCHECK(result);
 
   // Stop() should have canceled any outstanding copy requests. So, by reaching
   // this point, |consumer_| should be bound.
   DCHECK(consumer_);
 
-  // Populate the VideoFrame from the CopyOutputResult.
-  const int y_stride = frame->stride(VideoFrame::kYPlane);
-  uint8_t* const y = frame->visible_data(VideoFrame::kYPlane) +
-                     content_rect.y() * y_stride + content_rect.x();
-  const int u_stride = frame->stride(VideoFrame::kUPlane);
-  uint8_t* const u = frame->visible_data(VideoFrame::kUPlane) +
-                     (content_rect.y() / 2) * u_stride + (content_rect.x() / 2);
-  const int v_stride = frame->stride(VideoFrame::kVPlane);
-  uint8_t* const v = frame->visible_data(VideoFrame::kVPlane) +
-                     (content_rect.y() / 2) * v_stride + (content_rect.x() / 2);
-  if (result->ReadI420Planes(y, y_stride, u, u_stride, v, v_stride)) {
-    // The result may be smaller than what was requested, if unforeseen clamping
-    // to the source boundaries occurred by the executor of the
-    // CopyOutputRequest. However, the result should never contain more than
-    // what was requested.
-    DCHECK_LE(result->size().width(), content_rect.width());
-    DCHECK_LE(result->size().height(), content_rect.height());
-    media::LetterboxYUV(
-        frame.get(),
-        gfx::Rect(content_rect.origin(), AdjustSizeForI420(result->size())));
+  if (pixel_format_ == media::PIXEL_FORMAT_I420) {
+    DCHECK_EQ(content_rect.x() % 2, 0);
+    DCHECK_EQ(content_rect.y() % 2, 0);
+    DCHECK_EQ(content_rect.width() % 2, 0);
+    DCHECK_EQ(content_rect.height() % 2, 0);
+    // Populate the VideoFrame from the CopyOutputResult.
+    const int y_stride = frame->stride(VideoFrame::kYPlane);
+    uint8_t* const y = frame->visible_data(VideoFrame::kYPlane) +
+                       content_rect.y() * y_stride + content_rect.x();
+    const int u_stride = frame->stride(VideoFrame::kUPlane);
+    uint8_t* const u = frame->visible_data(VideoFrame::kUPlane) +
+                       (content_rect.y() / 2) * u_stride +
+                       (content_rect.x() / 2);
+    const int v_stride = frame->stride(VideoFrame::kVPlane);
+    uint8_t* const v = frame->visible_data(VideoFrame::kVPlane) +
+                       (content_rect.y() / 2) * v_stride +
+                       (content_rect.x() / 2);
+    if (result->ReadI420Planes(y, y_stride, u, u_stride, v, v_stride)) {
+      // The result may be smaller than what was requested, if unforeseen
+      // clamping to the source boundaries occurred by the executor of the
+      // CopyOutputRequest. However, the result should never contain more than
+      // what was requested.
+      DCHECK_LE(result->size().width(), content_rect.width());
+      DCHECK_LE(result->size().height(), content_rect.height());
+      media::LetterboxVideoFrame(
+          frame.get(), gfx::Rect(content_rect.origin(),
+                                 AdjustSizeForPixelFormat(result->size())));
+    } else {
+      frame = nullptr;
+    }
   } else {
-    frame = nullptr;
+    // TODO(samans): Avoid doing an extra copy by implementing a method similar
+    // to ReadI420Planes() that copies directly from GPU memory into shared
+    // memory. https://crbug.com/822264
+    DCHECK_EQ(media::PIXEL_FORMAT_ARGB, pixel_format_);
+    const SkBitmap& bitmap = result->AsSkBitmap();
+    if (bitmap.readyToDraw()) {
+      SkImageInfo image_info = SkImageInfo::MakeN32(
+          bitmap.width(), bitmap.height(), kPremul_SkAlphaType);
+      const int stride = frame->stride(VideoFrame::kARGBPlane);
+      uint8_t* const pixels = frame->visible_data(VideoFrame::kARGBPlane) +
+                              content_rect.y() * stride + content_rect.x() * 4;
+      bitmap.readPixels(image_info, pixels, stride, 0, 0);
+      media::LetterboxVideoFrame(
+          frame.get(), gfx::Rect(content_rect.origin(),
+                                 AdjustSizeForPixelFormat(result->size())));
+    } else {
+      frame = nullptr;
+    }
   }
 
   DidCaptureFrame(frame_number, oracle_frame_number, std::move(frame),
@@ -697,6 +670,25 @@
                              update_rect, content_rect, std::move(callbacks));
 }
 
+gfx::Size FrameSinkVideoCapturerImpl::AdjustSizeForPixelFormat(
+    const gfx::Size& raw_size) {
+  if (pixel_format_ == media::PIXEL_FORMAT_ARGB) {
+    gfx::Size result(raw_size);
+    if (result.width() <= 0)
+      result.set_width(1);
+    if (result.height() <= 0)
+      result.set_height(1);
+    return result;
+  }
+  DCHECK_EQ(media::PIXEL_FORMAT_I420, pixel_format_);
+  gfx::Size result(raw_size.width() & ~1, raw_size.height() & ~1);
+  if (result.width() <= 0)
+    result.set_width(2);
+  if (result.height() <= 0)
+    result.set_height(2);
+  return result;
+}
+
 FrameSinkVideoCapturerImpl::CapturedFrame::CapturedFrame(
     int64_t fn,
     OracleFrameNumber ofn,
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
index 7c6b42c..cc1e20d 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
@@ -7,10 +7,8 @@
 
 #include <stdint.h>
 
-#include <array>
 #include <queue>
 
-#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
@@ -19,7 +17,6 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "base/unguessable_token.h"
-#include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h"
 #include "components/viz/service/frame_sinks/video_capture/in_flight_frame_delivery.h"
@@ -130,7 +127,6 @@
  private:
   friend class FrameSinkVideoCapturerTest;
 
-  using BeginFrameSourceId = decltype(BeginFrameArgs::source_id);
   using OracleFrameNumber =
       decltype(std::declval<media::VideoCaptureOracle>().next_frame_number());
 
@@ -158,10 +154,9 @@
   void RefreshSoon();
 
   // CapturableFrameSink::Client implementation:
-  void OnBeginFrame(const BeginFrameArgs& args) final;
-  void OnFrameDamaged(const BeginFrameAck& ack,
-                      const gfx::Size& frame_size,
-                      const gfx::Rect& damage_rect) final;
+  void OnFrameDamaged(const gfx::Size& frame_size,
+                      const gfx::Rect& damage_rect,
+                      base::TimeTicks target_display_time) final;
 
   // Consults the VideoCaptureOracle to decide whether to capture a frame,
   // then ensures prerequisites are met before initiating the capture: that
@@ -194,6 +189,10 @@
                          scoped_refptr<media::VideoFrame> frame,
                          const gfx::Rect& content_rect);
 
+  // For ARGB format, ensures that every dimension of |size| is positive. For
+  // I420 format, ensures that every dimension is even and at least 2.
+  gfx::Size AdjustSizeForPixelFormat(const gfx::Size& size);
+
   // Owner/Manager of this instance.
   FrameSinkVideoCapturerManager* const frame_sink_manager_;
 
@@ -230,13 +229,6 @@
   // cleared when Stop() is called.
   mojom::FrameSinkVideoConsumerPtr consumer_;
 
-  // A cache of recently-recorded future frame display times, according to the
-  // BeginFrameArgs passed to OnBeginFrame() calls. There is one TimeRingBuffer
-  // per BeginFrameSource. TimeRingBuffer is an array mapping
-  // BeginFrameArgs::sequence_number to the expected display time.
-  using TimeRingBuffer = std::array<base::TimeTicks, kDesignLimitMaxFrames>;
-  base::flat_map<BeginFrameSourceId, TimeRingBuffer> frame_display_times_;
-
   // The portion of the source content that has changed, but has not yet been
   // captured.
   gfx::Rect dirty_rect_;
@@ -293,11 +285,6 @@
   // copy output requests.
   base::WeakPtrFactory<FrameSinkVideoCapturerImpl> capture_weak_factory_;
 
-  // Retain entries in |frame_display_times_| that contain timestamps newer than
-  // this long ago.
-  static constexpr base::TimeDelta kDisplayTimeCacheKeepAliveInterval =
-      base::TimeDelta::FromMilliseconds(500);
-
   DISALLOW_COPY_AND_ASSIGN(FrameSinkVideoCapturerImpl);
 };
 
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
index 8c60845..27bd0aa5 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
@@ -324,28 +324,19 @@
     PropagateMojoTasks();
   }
 
-  void AdvanceClockToNextVsync() {
+  base::TimeTicks GetNextVsync() const {
     const auto now = task_runner_->NowTicks();
     const auto num_vsyncs_elapsed = (now - start_time_) / kVsyncInterval;
-    const auto advance_to_time =
-        start_time_ + (num_vsyncs_elapsed + 1) * kVsyncInterval;
-    task_runner_->FastForwardBy(advance_to_time - now);
+    return start_time_ + (num_vsyncs_elapsed + 1) * kVsyncInterval;
   }
 
-  void NotifyBeginFrame(int source_id, int frame_number) {
-    BeginFrameArgs args;
-    args.interval = kVsyncInterval;
-    args.frame_time = task_runner_->NowTicks();
-    args.sequence_number = BeginFrameArgs::kStartingFrameNumber + frame_number;
-    args.source_id = source_id;
-    capturer_.OnBeginFrame(args);
+  void AdvanceClockToNextVsync() {
+    task_runner_->FastForwardBy(GetNextVsync() - task_runner_->NowTicks());
   }
 
-  void NotifyFrameDamaged(int source_id, int frame_number) {
-    BeginFrameAck ack;
-    ack.sequence_number = BeginFrameArgs::kStartingFrameNumber + frame_number;
-    ack.source_id = source_id;
-    capturer_.OnFrameDamaged(ack, kSourceSize, gfx::Rect(kSourceSize));
+  void NotifyFrameDamaged() {
+    capturer_.OnFrameDamaged(kSourceSize, gfx::Rect(kSourceSize),
+                             GetNextVsync());
   }
 
   void NotifyTargetWentAway() {
@@ -362,15 +353,6 @@
     PropagateMojoTasks();
   }
 
-  bool HasCacheEntryForSource(int source_id) {
-    return capturer_.frame_display_times_.find(source_id) !=
-           capturer_.frame_display_times_.end();
-  }
-
-  static constexpr base::TimeDelta GetDisplayTimeCacheKeepAliveInterval() {
-    return FrameSinkVideoCapturerImpl::kDisplayTimeCacheKeepAliveInterval;
-  }
-
  protected:
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
   base::TimeTicks start_time_;
@@ -513,12 +495,10 @@
        ++i) {
     SCOPED_TRACE(testing::Message() << "frame #" << i);
 
-    // Move time forward to the next display vsync and notify the capturer that
-    // compositing of the frame has begun.
+    // Move time forward to the next display vsync.
     AdvanceClockToNextVsync();
     const base::TimeTicks expected_reference_time =
         task_runner_->NowTicks() + kVsyncInterval;
-    NotifyBeginFrame(1, i);
 
     // Change the content of the frame sink and notify the capturer of the
     // damage.
@@ -527,7 +507,7 @@
     task_runner_->FastForwardBy(kVsyncInterval / 4);
     const base::TimeTicks expected_capture_begin_time =
         task_runner_->NowTicks();
-    NotifyFrameDamaged(1, i);
+    NotifyFrameDamaged();
 
     // The frame sink should have received a CopyOutputRequest. Simulate a short
     // pause before the result is sent back to the capturer, and the capturer
@@ -602,8 +582,7 @@
   int num_frames = FrameSinkVideoCapturerImpl::kDesignLimitMaxFrames;
   for (int i = num_refresh_frames; i < num_frames; ++i) {
     AdvanceClockToNextVsync();
-    NotifyBeginFrame(1, i);
-    NotifyFrameDamaged(1, i);
+    NotifyFrameDamaged();
     // The oracle should not be rejecting captures caused by compositor updates.
     ASSERT_FALSE(IsRefreshRetryTimerRunning());
   }
@@ -613,10 +592,8 @@
   // requests to be issued at this point. However, the refresh timer should be
   // scheduled to account for the capture of changed content that could not take
   // place.
-  const int first_uncaptured_frame = num_frames;
   AdvanceClockToNextVsync();
-  NotifyBeginFrame(1, first_uncaptured_frame);
-  NotifyFrameDamaged(1, first_uncaptured_frame);
+  NotifyFrameDamaged();
   ASSERT_EQ(num_frames, frame_sink_.num_copy_results());
   EXPECT_TRUE(IsRefreshRetryTimerRunning());
 
@@ -625,10 +602,8 @@
   // frame is still in the middle of being delivered/consumed.
   frame_sink_.SendCopyOutputResult(0);
   ASSERT_EQ(1, consumer.num_frames_received());
-  const int second_uncaptured_frame = num_frames;
   AdvanceClockToNextVsync();
-  NotifyBeginFrame(1, second_uncaptured_frame);
-  NotifyFrameDamaged(1, second_uncaptured_frame);
+  NotifyFrameDamaged();
   ASSERT_EQ(num_frames, frame_sink_.num_copy_results());
   EXPECT_TRUE(IsRefreshRetryTimerRunning());
 
@@ -638,10 +613,8 @@
   // capture will satisfy the need to send updated content to the consumer.
   EXPECT_TRUE(consumer.TakeFrame(0));
   consumer.SendDoneNotification(0);
-  const int first_capture_resumed_frame = second_uncaptured_frame + 1;
   AdvanceClockToNextVsync();
-  NotifyBeginFrame(1, first_capture_resumed_frame);
-  NotifyFrameDamaged(1, first_capture_resumed_frame);
+  NotifyFrameDamaged();
   ++num_frames;
   ASSERT_EQ(num_frames, frame_sink_.num_copy_results());
   EXPECT_FALSE(IsRefreshRetryTimerRunning());
@@ -649,10 +622,8 @@
   // With yet another compositor update, no new copy requests should be issued
   // because the pipeline became saturated again. Once again, the refresh timer
   // should be started to account for the need to capture at some future point.
-  const int third_uncaptured_frame = first_capture_resumed_frame + 1;
   AdvanceClockToNextVsync();
-  NotifyBeginFrame(1, third_uncaptured_frame);
-  NotifyFrameDamaged(1, third_uncaptured_frame);
+  NotifyFrameDamaged();
   ASSERT_EQ(num_frames, frame_sink_.num_copy_results());
   EXPECT_TRUE(IsRefreshRetryTimerRunning());
 
@@ -664,10 +635,8 @@
     frame_sink_.SendCopyOutputResult(i);
   }
   ASSERT_EQ(frame_sink_.num_copy_results(), consumer.num_frames_received());
-  const int fourth_uncaptured_frame = third_uncaptured_frame + 1;
   AdvanceClockToNextVsync();
-  NotifyBeginFrame(1, fourth_uncaptured_frame);
-  NotifyFrameDamaged(1, fourth_uncaptured_frame);
+  NotifyFrameDamaged();
   ASSERT_EQ(num_frames, frame_sink_.num_copy_results());
   EXPECT_TRUE(IsRefreshRetryTimerRunning());
 
@@ -678,10 +647,8 @@
     EXPECT_TRUE(consumer.TakeFrame(i));
     consumer.SendDoneNotification(i);
   }
-  const int second_capture_resumed_frame = fourth_uncaptured_frame + 1;
   AdvanceClockToNextVsync();
-  NotifyBeginFrame(1, second_capture_resumed_frame);
-  NotifyFrameDamaged(1, second_capture_resumed_frame);
+  NotifyFrameDamaged();
   ++num_frames;
   ASSERT_EQ(num_frames, frame_sink_.num_copy_results());
   frame_sink_.SendCopyOutputResult(frame_sink_.num_copy_results() - 1);
@@ -717,8 +684,7 @@
                               static_cast<uint8_t>((i << 4) + 0x20)});
     frame_sink_.SetCopyOutputColor(colors.back());
     AdvanceClockToNextVsync();
-    NotifyBeginFrame(1, i);
-    NotifyFrameDamaged(1, i);
+    NotifyFrameDamaged();
   }
   ASSERT_EQ(num_frames, frame_sink_.num_copy_results());
 
@@ -771,8 +737,7 @@
   for (int i = num_refresh_frames; i < num_copy_requests; ++i) {
     SCOPED_TRACE(testing::Message() << "frame #" << i);
     AdvanceClockToNextVsync();
-    NotifyBeginFrame(1, i);
-    NotifyFrameDamaged(1, i);
+    NotifyFrameDamaged();
   }
   ASSERT_EQ(num_copy_requests, frame_sink_.num_copy_results());
 
@@ -820,8 +785,7 @@
     if (i == 0) {
       // Expect that advancing the clock caused the refresh timer to fire.
     } else {
-      NotifyBeginFrame(1, num_copy_requests);
-      NotifyFrameDamaged(1, num_copy_requests);
+      NotifyFrameDamaged();
     }
     ++num_copy_requests;
     ASSERT_EQ(num_copy_requests, frame_sink_.num_copy_results());
@@ -866,8 +830,7 @@
   int num_frames = 1 + num_update_frames;
   for (int i = 1; i < num_frames; ++i) {
     AdvanceClockToNextVsync();
-    NotifyBeginFrame(1, i);
-    NotifyFrameDamaged(1, i);
+    NotifyFrameDamaged();
     ASSERT_EQ(i + 1, frame_sink_.num_copy_results());
     ASSERT_FALSE(IsRefreshRetryTimerRunning());
     frame_sink_.SendCopyOutputResult(i);
@@ -893,41 +856,4 @@
   StopCapture();
 }
 
-// Tests that the capturer caches display times from OnBeginFrame()
-// notifications and throws away old data.
-TEST_F(FrameSinkVideoCapturerTest, CachesAndPrunesDisplayTimes) {
-  EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId))
-      .WillRepeatedly(Return(&frame_sink_));
-  capturer_.ChangeTarget(kFrameSinkId);
-
-  // The first OnBeginFrame() call should create a cache entry for source_id=1.
-  NotifyBeginFrame(1, 1);
-  EXPECT_TRUE(HasCacheEntryForSource(1));
-
-  // The next OnBeginFrame() call should create a second cache entry, for
-  // source_id=2.
-  NotifyBeginFrame(2, 1);
-  EXPECT_TRUE(HasCacheEntryForSource(1));
-  EXPECT_TRUE(HasCacheEntryForSource(2));
-
-  // Make a sequence of OnBeginFrame() calls for source_id=2. The cache entries
-  // for both sources should remain.
-  const base::TimeTicks end_time =
-      task_runner_->NowTicks() + GetDisplayTimeCacheKeepAliveInterval();
-  int seq = 2;
-  while (task_runner_->NowTicks() < end_time) {
-    task_runner_->FastForwardBy(kVsyncInterval);
-    NotifyBeginFrame(2, seq++);
-    ASSERT_TRUE(HasCacheEntryForSource(1));
-    ASSERT_TRUE(HasCacheEntryForSource(2));
-  }
-
-  // Now, if a third source is introduced, the garbage collection will run and
-  // prune out the entry for source_id=1 since it has not been actively updated.
-  NotifyBeginFrame(3, 1);
-  EXPECT_FALSE(HasCacheEntryForSource(1));
-  EXPECT_TRUE(HasCacheEntryForSource(2));
-  EXPECT_TRUE(HasCacheEntryForSource(3));
-}
-
 }  // namespace viz
diff --git a/components/viz/service/frame_sinks/video_detector_unittest.cc b/components/viz/service/frame_sinks/video_detector_unittest.cc
index 52bc5ce..f3cfe50 100644
--- a/components/viz/service/frame_sinks/video_detector_unittest.cc
+++ b/components/viz/service/frame_sinks/video_detector_unittest.cc
@@ -81,7 +81,9 @@
   ~VideoDetectorTest() override {}
 
   void SetUp() override {
-    mock_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+    mock_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
+        base::Time() + base::TimeDelta::FromSeconds(1),
+        base::TimeTicks() + base::TimeDelta::FromSeconds(1));
 
     detector_ = frame_sink_manager_.CreateVideoDetectorForTesting(
         mock_task_runner_->DeprecatedGetMockTickClock(), mock_task_runner_);
@@ -112,8 +114,8 @@
   }
 
   void CreateDisplayFrame() {
-    surface_aggregator_.Aggregate(
-        root_frame_sink_->last_activated_surface_id());
+    surface_aggregator_.Aggregate(root_frame_sink_->last_activated_surface_id(),
+                                  mock_task_runner_->NowTicks());
   }
 
   void EmbedClient(CompositorFrameSinkSupport* frame_sink) {
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index 1cb5b95..d46aa31 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -485,13 +485,16 @@
     std::move(active_frame_data_->draw_callback).Run();
 }
 
-void Surface::NotifyAggregatedDamage(const gfx::Rect& damage_rect) {
+void Surface::NotifyAggregatedDamage(const gfx::Rect& damage_rect,
+                                     base::TimeTicks expected_display_time) {
   if (!active_frame_data_ ||
       active_frame_data_->aggregated_damage_callback.is_null())
     return;
 
   active_frame_data_->aggregated_damage_callback.Run(
-      surface_id().local_surface_id(), damage_rect, active_frame_data_->frame);
+      surface_id().local_surface_id(),
+      active_frame_data_->frame.size_in_pixels(), damage_rect,
+      expected_display_time);
 }
 
 void Surface::OnDeadline(base::TimeDelta duration) {
diff --git a/components/viz/service/surfaces/surface.h b/components/viz/service/surfaces/surface.h
index c742367..4d9a63a 100644
--- a/components/viz/service/surfaces/surface.h
+++ b/components/viz/service/surfaces/surface.h
@@ -18,6 +18,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "base/time/time.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
@@ -71,8 +72,9 @@
  public:
   using AggregatedDamageCallback =
       base::RepeatingCallback<void(const LocalSurfaceId& local_surface_id,
+                                   const gfx::Size& frame_size_in_pixels,
                                    const gfx::Rect& damage_rect,
-                                   const CompositorFrame& frame)>;
+                                   base::TimeTicks expected_display_time)>;
   using PresentedCallback =
       base::OnceCallback<void(base::TimeTicks, base::TimeDelta, uint32_t)>;
 
@@ -175,7 +177,8 @@
   void TakeLatencyInfo(std::vector<ui::LatencyInfo>* latency_info);
   bool TakePresentedCallback(PresentedCallback* callback);
   void RunDrawCallback();
-  void NotifyAggregatedDamage(const gfx::Rect& damage_rect);
+  void NotifyAggregatedDamage(const gfx::Rect& damage_rect,
+                              base::TimeTicks expected_display_time);
 
   const std::vector<SurfaceId>* active_referenced_surfaces() const {
     return active_frame_data_
diff --git a/content/OWNERS b/content/OWNERS
index 0b7cd02..53d2f57 100644
--- a/content/OWNERS
+++ b/content/OWNERS
@@ -17,6 +17,10 @@
 # structural changes, please get a review from a reviewer in this file.
 per-file BUILD.gn=*
 
+# For threading (BrowserThread, BrowserMainLoop, TaskScheduler, etc.)
+per-file *browser_main_loop*=gab@chromium.org
+per-file *browser_thread*=gab@chromium.org
+
 # Mac Sandbox profiles.
 per-file *.sb=set noparent
 per-file *.sb=rsesek@chromium.org
diff --git a/content/browser/background_fetch/OWNERS b/content/browser/background_fetch/OWNERS
index 317a557..a7cd217 100644
--- a/content/browser/background_fetch/OWNERS
+++ b/content/browser/background_fetch/OWNERS
@@ -1,9 +1,10 @@
 # This OWNERS file also covers:
 #
+# //chrome/browser/background_fetch/
 # //content/common/background_fetch/
 
+awdf@chromium.org
 peter@chromium.org
-johnme@chromium.org
 delphick@chromium.org
 
 # TEAM: platform-capabilities@chromium.org
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index a4b7f54..a3f6524 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -2873,6 +2873,33 @@
   ASSERT_TRUE(server.ShutdownAndWaitUntilComplete());
 }
 
+// Test that the target attribute is ignored on anchors with a download
+// attribute.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest,
+                       DownloadAttributeIgnoresTargetAttribute) {
+  net::EmbeddedTestServer server;
+  ASSERT_TRUE(server.InitializeAndListen());
+
+  GURL url = server.GetURL(std::string("/download-attribute-with-target.html"));
+  server.ServeFilesFromDirectory(GetTestFilePath("download", ""));
+  server.StartAcceptingConnections();
+
+  NavigateToURLAndWaitForDownload(shell(), url,
+                                  download::DownloadItem::COMPLETE);
+
+  std::vector<download::DownloadItem*> downloads;
+  DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+  ASSERT_EQ(1u, downloads.size());
+
+  // The target="_blank" should have been ignored, and so no additional window
+  // was created.
+  ASSERT_EQ(1u, Shell::windows().size());
+
+  EXPECT_EQ(FILE_PATH_LITERAL("suggested-filename"),
+            downloads[0]->GetTargetFilePath().BaseName().value());
+  ASSERT_TRUE(server.ShutdownAndWaitUntilComplete());
+}
+
 // A request for a non-existent resource should result in an aborted navigation,
 // and the old site staying current.
 IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeServerError) {
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 9f0687b..1e178877 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -151,14 +151,16 @@
       item.GetGuid(), download::DownloadSource::UNKNOWN, GetUniqueDownloadId());
 }
 
-DownloadManagerImpl::UniqueUrlDownloadHandlerPtr BeginDownload(
-    std::unique_ptr<download::DownloadUrlParameters> params,
-    std::unique_ptr<storage::BlobDataHandle> blob_data_handle,
-    content::ResourceContext* resource_context,
-    uint32_t download_id,
-    base::WeakPtr<DownloadManagerImpl> download_manager) {
+void BeginDownload(std::unique_ptr<download::DownloadUrlParameters> params,
+                   std::unique_ptr<storage::BlobDataHandle> blob_data_handle,
+                   content::ResourceContext* resource_context,
+                   uint32_t download_id,
+                   base::WeakPtr<DownloadManagerImpl> download_manager) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
+  download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader(
+      nullptr, base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get()));
+
   params->set_blob_storage_context_getter(
       base::BindOnce(&BlobStorageContextGetter, resource_context));
   std::unique_ptr<net::URLRequest> url_request =
@@ -179,21 +181,23 @@
 
     // If the download was accepted, the DownloadResourceHandler is now
     // responsible for driving the request to completion.
-    if (reason == download::DOWNLOAD_INTERRUPT_REASON_NONE)
-      return nullptr;
-
     // Otherwise, create an interrupted download.
-    CreateInterruptedDownload(params.get(), reason, download_manager);
-    return nullptr;
+    if (reason != download::DOWNLOAD_INTERRUPT_REASON_NONE)
+      CreateInterruptedDownload(params.get(), reason, download_manager);
+  } else {
+    downloader.reset(UrlDownloader::BeginDownload(download_manager,
+                                                  std::move(url_request),
+                                                  params.get(), false)
+                         .release());
   }
-
-  return DownloadManagerImpl::UniqueUrlDownloadHandlerPtr(
-      UrlDownloader::BeginDownload(download_manager, std::move(url_request),
-                                   params.get(), false)
-          .release());
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(
+          &download::UrlDownloadHandler::Delegate::OnUrlDownloadHandlerCreated,
+          download_manager, std::move(downloader)));
 }
 
-DownloadManagerImpl::UniqueUrlDownloadHandlerPtr BeginResourceDownload(
+void BeginResourceDownload(
     std::unique_ptr<download::DownloadUrlParameters> params,
     std::unique_ptr<network::ResourceRequest> request,
     std::unique_ptr<storage::BlobDataHandle> blob_data_handle,
@@ -206,6 +210,9 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
+  download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader(
+      nullptr, base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get()));
+
   // Check if the renderer is permitted to request the requested URL.
   if (params->render_process_host_id() >= 0 &&
       !CanRequestURLFromRenderer(params->render_process_host_id(),
@@ -214,30 +221,35 @@
         params.get(),
         download::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
         download_manager);
-    return nullptr;
-  }
-
-  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory;
-  if (params->url().SchemeIs(url::kBlobScheme)) {
-    network::mojom::URLLoaderFactoryPtrInfo url_loader_factory_ptr_info;
-    storage::BlobURLLoaderFactory::Create(
-        std::move(blob_data_handle), params->url(),
-        mojo::MakeRequest(&url_loader_factory_ptr_info));
-    shared_url_loader_factory =
-        base::MakeRefCounted<WrapperSharedURLLoaderFactory>(
-            std::move(url_loader_factory_ptr_info));
   } else {
-    shared_url_loader_factory = url_loader_factory_getter->GetNetworkFactory();
+    scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory;
+    if (params->url().SchemeIs(url::kBlobScheme)) {
+      network::mojom::URLLoaderFactoryPtrInfo url_loader_factory_ptr_info;
+      storage::BlobURLLoaderFactory::Create(
+          std::move(blob_data_handle), params->url(),
+          mojo::MakeRequest(&url_loader_factory_ptr_info));
+      shared_url_loader_factory =
+          base::MakeRefCounted<WrapperSharedURLLoaderFactory>(
+              std::move(url_loader_factory_ptr_info));
+    } else {
+      shared_url_loader_factory =
+          url_loader_factory_getter->GetNetworkFactory();
+    }
+    // TODO(qinmin): Check the storage permission before creating the URLLoader.
+    // This is already done for context menu download, but it is missing for
+    // download service and download resumption.
+    downloader.reset(
+        download::ResourceDownloader::BeginDownload(
+            download_manager, std::move(params), std::move(request),
+            std::move(shared_url_loader_factory), site_url, tab_url,
+            tab_referrer_url, download_id, false, task_runner)
+            .release());
   }
-  // TODO(qinmin): Check the storage permission before creating the URLLoader.
-  // This is already done for context menu download, but it is missing for
-  // download service and download resumption.
-  return DownloadManagerImpl::UniqueUrlDownloadHandlerPtr(
-      download::ResourceDownloader::BeginDownload(
-          download_manager, std::move(params), std::move(request),
-          std::move(shared_url_loader_factory), site_url, tab_url,
-          tab_referrer_url, download_id, false, task_runner)
-          .release());
+  task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &download::UrlDownloadHandler::Delegate::OnUrlDownloadHandlerCreated,
+          download_manager, std::move(downloader)));
 }
 
 class DownloadItemFactoryImpl : public DownloadItemFactory {
@@ -390,10 +402,13 @@
       delegate_(nullptr),
       weak_factory_(this) {
   DCHECK(browser_context);
+  download::SetIOTaskRunner(
+      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
 }
 
 DownloadManagerImpl::~DownloadManagerImpl() {
   DCHECK(!shutdown_needed_);
+  download::SetIOTaskRunner(nullptr);
 }
 
 DownloadItemImpl* DownloadManagerImpl::CreateActiveItem(
@@ -816,8 +831,8 @@
   downloads_.erase(download->GetId());
 }
 
-void DownloadManagerImpl::AddUrlDownloadHandler(
-    UniqueUrlDownloadHandlerPtr downloader) {
+void DownloadManagerImpl::OnUrlDownloadHandlerCreated(
+    download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (downloader)
     url_download_handlers_.push_back(std::move(downloader));
@@ -1195,20 +1210,20 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  std::unique_ptr<download::ResourceDownloader> resource_downloader =
+  download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr resource_downloader(
       download::ResourceDownloader::InterceptNavigationResponse(
           download_manager, std::move(resource_request), render_process_id,
           render_frame_id, site_url, tab_url, tab_referrer_url,
           std::move(url_chain), suggested_filename, std::move(response),
           std::move(cert_status), std::move(url_loader_client_endpoints),
-          task_runner);
+          task_runner)
+          .release(),
+      base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get()));
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&DownloadManagerImpl::AddUrlDownloadHandler,
-                     download_manager,
-                     DownloadManagerImpl::UniqueUrlDownloadHandlerPtr(
-                         resource_downloader.release())));
+      base::BindOnce(&DownloadManagerImpl::OnUrlDownloadHandlerCreated,
+                     download_manager, std::move(resource_downloader)));
 }
 
 void DownloadManagerImpl::BeginDownloadInternal(
@@ -1232,24 +1247,20 @@
       }
     }
 
-    BrowserThread::PostTaskAndReplyWithResult(
+    BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::BindOnce(&BeginResourceDownload, std::move(params),
                        std::move(request), std::move(blob_data_handle),
                        storage_partition->url_loader_factory_getter(), id,
                        weak_factory_.GetWeakPtr(), site_url, tab_url,
                        tab_referrer_url,
-                       base::MessageLoop::current()->task_runner()),
-        base::BindOnce(&DownloadManagerImpl::AddUrlDownloadHandler,
-                       weak_factory_.GetWeakPtr()));
+                       base::MessageLoop::current()->task_runner()));
   } else {
-    BrowserThread::PostTaskAndReplyWithResult(
+    BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::BindOnce(&BeginDownload, std::move(params),
                        std::move(blob_data_handle),
                        browser_context_->GetResourceContext(), id,
-                       weak_factory_.GetWeakPtr()),
-        base::BindOnce(&DownloadManagerImpl::AddUrlDownloadHandler,
                        weak_factory_.GetWeakPtr()));
    }
 }
diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h
index 313e9f3..93d915b 100644
--- a/content/browser/download/download_manager_impl.h
+++ b/content/browser/download/download_manager_impl.h
@@ -48,9 +48,6 @@
       private DownloadItemImplDelegate {
  public:
   using DownloadItemImplCreated = base::Callback<void(DownloadItemImpl*)>;
-  using UniqueUrlDownloadHandlerPtr =
-      std::unique_ptr<download::UrlDownloadHandler,
-                      BrowserThread::DeleteOnIOThread>;
 
   // Caller guarantees that |net_log| will remain valid
   // for the lifetime of DownloadManagerImpl (until Shutdown() is called).
@@ -136,6 +133,9 @@
       const download::DownloadUrlParameters::OnStartedCallback& callback)
       override;
   void OnUrlDownloadStopped(download::UrlDownloadHandler* downloader) override;
+  void OnUrlDownloadHandlerCreated(
+      download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader)
+      override;
 
   // For testing; specifically, accessed from TestFileErrorInjector.
   void SetDownloadItemFactoryForTesting(
@@ -230,8 +230,6 @@
   void ShowDownloadInShell(DownloadItemImpl* download) override;
   void DownloadRemoved(DownloadItemImpl* download) override;
 
-  void AddUrlDownloadHandler(UniqueUrlDownloadHandlerPtr downloader);
-
   // Helper method to start or resume a download.
   void BeginDownloadInternal(
       std::unique_ptr<download::DownloadUrlParameters> params,
@@ -310,7 +308,8 @@
   // Allows an embedder to control behavior. Guaranteed to outlive this object.
   DownloadManagerDelegate* delegate_;
 
-  std::vector<UniqueUrlDownloadHandlerPtr> url_download_handlers_;
+  std::vector<download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr>
+      url_download_handlers_;
 
   base::WeakPtrFactory<DownloadManagerImpl> weak_factory_;
 
diff --git a/content/browser/download/download_worker.cc b/content/browser/download/download_worker.cc
index 37981901f..1bde3542 100644
--- a/content/browser/download/download_worker.cc
+++ b/content/browser/download/download_worker.cc
@@ -7,6 +7,7 @@
 #include "base/message_loop/message_loop.h"
 #include "components/download/public/common/download_create_info.h"
 #include "components/download/public/common/download_interrupt_reasons.h"
+#include "components/download/public/common/download_task_runner.h"
 #include "components/download/public/common/input_stream.h"
 #include "components/download/public/common/resource_downloader.h"
 #include "content/browser/download/download_utils.h"
@@ -43,24 +44,23 @@
   DISALLOW_COPY_AND_ASSIGN(CompletedInputStream);
 };
 
-std::unique_ptr<download::UrlDownloadHandler, BrowserThread::DeleteOnIOThread>
-CreateUrlDownloadHandler(
+void CreateUrlDownloadHandler(
     std::unique_ptr<download::DownloadUrlParameters> params,
     base::WeakPtr<download::UrlDownloadHandler::Delegate> delegate,
     scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter,
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader(
+      nullptr, base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get()));
 
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     std::unique_ptr<network::ResourceRequest> request =
         CreateResourceRequest(params.get());
-    return std::unique_ptr<download::ResourceDownloader,
-                           BrowserThread::DeleteOnIOThread>(
-        download::ResourceDownloader::BeginDownload(
-            delegate, std::move(params), std::move(request),
-            url_loader_factory_getter->GetNetworkFactory(), GURL(), GURL(),
-            GURL(), download::DownloadItem::kInvalidId, true, task_runner)
-            .release());
+    downloader.reset(download::ResourceDownloader::BeginDownload(
+                         delegate, std::move(params), std::move(request),
+                         url_loader_factory_getter->GetNetworkFactory(), GURL(),
+                         GURL(), GURL(), download::DownloadItem::kInvalidId,
+                         true, task_runner)
+                         .release());
   } else {
     // Build the URLRequest, BlobDataHandle is hold in original request for
     // image download.
@@ -68,11 +68,15 @@
         DownloadRequestCore::CreateRequestOnIOThread(
             download::DownloadItem::kInvalidId, params.get());
 
-    return std::unique_ptr<UrlDownloader, BrowserThread::DeleteOnIOThread>(
-        UrlDownloader::BeginDownload(delegate, std::move(url_request),
-                                     params.get(), true)
-            .release());
+    downloader.reset(UrlDownloader::BeginDownload(
+                         delegate, std::move(url_request), params.get(), true)
+                         .release());
   }
+  task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &download::UrlDownloadHandler::Delegate::OnUrlDownloadHandlerCreated,
+          delegate, std::move(downloader)));
 }
 
 }  // namespace
@@ -86,6 +90,7 @@
       is_paused_(false),
       is_canceled_(false),
       is_user_cancel_(false),
+      url_download_handler_(nullptr, base::OnTaskRunnerDeleter(nullptr)),
       weak_factory_(this) {
   DCHECK(delegate_);
 }
@@ -95,14 +100,11 @@
 void DownloadWorker::SendRequest(
     std::unique_ptr<download::DownloadUrlParameters> params,
     scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::IO, FROM_HERE,
+  download::GetIOTaskRunner()->PostTask(
+      FROM_HERE,
       base::BindOnce(&CreateUrlDownloadHandler, std::move(params),
                      weak_factory_.GetWeakPtr(), url_loader_factory_getter,
-                     base::MessageLoop::current()->task_runner()),
-      base::BindOnce(&DownloadWorker::AddUrlDownloadHandler,
-                     weak_factory_.GetWeakPtr()));
+                     base::ThreadTaskRunnerHandle::Get()));
 }
 
 void DownloadWorker::Pause() {
@@ -166,9 +168,8 @@
   url_download_handler_.reset();
 }
 
-void DownloadWorker::AddUrlDownloadHandler(
-    std::unique_ptr<download::UrlDownloadHandler,
-                    BrowserThread::DeleteOnIOThread> downloader) {
+void DownloadWorker::OnUrlDownloadHandlerCreated(
+    download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader) {
   url_download_handler_ = std::move(downloader);
 }
 
diff --git a/content/browser/download/download_worker.h b/content/browser/download/download_worker.h
index b36bb211..7ccc36b 100644
--- a/content/browser/download/download_worker.h
+++ b/content/browser/download/download_worker.h
@@ -15,7 +15,6 @@
 #include "content/browser/download/download_request_core.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/common/content_export.h"
-#include "content/public/browser/browser_thread.h"
 
 namespace content {
 
@@ -62,10 +61,9 @@
       const download::DownloadUrlParameters::OnStartedCallback& callback)
       override;
   void OnUrlDownloadStopped(download::UrlDownloadHandler* downloader) override;
-
-  void AddUrlDownloadHandler(
-      std::unique_ptr<download::UrlDownloadHandler,
-                      BrowserThread::DeleteOnIOThread> downloader);
+  void OnUrlDownloadHandlerCreated(
+      download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader)
+      override;
 
   DownloadWorker::Delegate* const delegate_;
 
@@ -84,7 +82,7 @@
   std::unique_ptr<download::DownloadRequestHandleInterface> request_handle_;
 
   // Used to handle the url request. Live and die on IO thread.
-  std::unique_ptr<download::UrlDownloadHandler, BrowserThread::DeleteOnIOThread>
+  download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr
       url_download_handler_;
 
   base::WeakPtrFactory<DownloadWorker> weak_factory_;
diff --git a/content/browser/frame_host/data_url_navigation_throttle.cc b/content/browser/frame_host/data_url_navigation_throttle.cc
index 4e1e7112..4d725ab2 100644
--- a/content/browser/frame_host/data_url_navigation_throttle.cc
+++ b/content/browser/frame_host/data_url_navigation_throttle.cc
@@ -33,18 +33,6 @@
 
 NavigationThrottle::ThrottleCheckResult
 DataUrlNavigationThrottle::WillProcessResponse() {
-#if defined(OS_ANDROID)
-  // This should ideally be done in CreateThrottleForNavigation(), but
-  // NavigationHandleImpl::GetRenderFrameHost() expects to not be run before
-  // WillProcessResponse().
-  // TODO(meacer): Remove this special case when PlzNavigate is enabled.
-  if (!IsBrowserSideNavigationEnabled() &&
-      navigation_handle()
-          ->GetRenderFrameHost()
-          ->IsDataUrlNavigationAllowedForAndroidWebView()) {
-    return PROCEED;
-  }
-#endif
   NavigationHandleImpl* handle =
       static_cast<NavigationHandleImpl*>(navigation_handle());
   if (handle->IsDownload())
diff --git a/content/browser/frame_host/render_frame_host_android.cc b/content/browser/frame_host/render_frame_host_android.cc
index 9f36fd67..8ef7214 100644
--- a/content/browser/frame_host/render_frame_host_android.cc
+++ b/content/browser/frame_host/render_frame_host_android.cc
@@ -93,10 +93,10 @@
       env, render_frame_host_->GetOverlayRoutingToken());
 }
 
-void RenderFrameHostAndroid::SetHasReceivedUserGesture(
+void RenderFrameHostAndroid::NotifyUserActivation(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>&) {
-  render_frame_host_->SetHasReceivedUserGesture();
+  render_frame_host_->NotifyUserActivation();
 }
 
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_android.h b/content/browser/frame_host/render_frame_host_android.h
index 90b9369..423e1a6 100644
--- a/content/browser/frame_host/render_frame_host_android.h
+++ b/content/browser/frame_host/render_frame_host_android.h
@@ -49,8 +49,8 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>&) const;
 
-  void SetHasReceivedUserGesture(JNIEnv* env,
-                                 const base::android::JavaParamRef<jobject>&);
+  void NotifyUserActivation(JNIEnv* env,
+                            const base::android::JavaParamRef<jobject>&);
 
  private:
   RenderFrameHostImpl* const render_frame_host_;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index b5137005..b44fe8c3 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -191,10 +191,6 @@
 // Whether to allow injecting javascript into any kind of frame (for Android
 // WebView).
 bool g_allow_injecting_javascript = false;
-
-// Whether to allow data URL navigations for Android WebView.
-// TODO(meacer): Remove after PlzNavigate ships.
-bool g_allow_data_url_navigation = false;
 #endif
 
 // The (process id, routing id) pair that identifies one RenderFrame.
@@ -389,16 +385,6 @@
 void RenderFrameHost::AllowInjectingJavaScriptForAndroidWebView() {
   g_allow_injecting_javascript = true;
 }
-
-// static
-void RenderFrameHost::AllowDataUrlNavigationForAndroidWebView() {
-  g_allow_data_url_navigation = true;
-}
-
-// static
-bool RenderFrameHost::IsDataUrlNavigationAllowedForAndroidWebView() {
-  return g_allow_data_url_navigation;
-}
 #endif  // defined(OS_ANDROID)
 
 // static
@@ -2744,8 +2730,8 @@
                       bounds_in_frame_widget.size()));
 }
 
-void RenderFrameHostImpl::SetHasReceivedUserGesture() {
-  Send(new FrameMsg_SetHasReceivedUserGesture(routing_id_));
+void RenderFrameHostImpl::NotifyUserActivation() {
+  Send(new FrameMsg_NotifyUserActivation(routing_id_));
 }
 
 void RenderFrameHostImpl::OnSetHasReceivedUserGesture() {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 46e4270..604dd2d 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -698,13 +698,13 @@
     return active_sandbox_flags_;
   }
 
-  // Call |FlushForTesting()| on Network Service and FrameNavigationControl
+  // Calls |FlushForTesting()| on Network Service and FrameNavigationControl
   // related interfaces to make sure all in-flight mojo messages have been
   // received by the other end. For test use only.
   void FlushNetworkAndNavigationInterfacesForTesting();
 
-  // Notifies the render frame that a user gesture was received.
-  void SetHasReceivedUserGesture();
+  // Notifies the render frame about a user activation from the browser side.
+  void NotifyUserActivation();
 
   // Returns the current size for this frame.
   const base::Optional<gfx::Size>& frame_size() const { return frame_size_; }
diff --git a/content/browser/media/capture/aura_window_capture_machine.cc b/content/browser/media/capture/aura_window_capture_machine.cc
index 3797696f..2f6caae 100644
--- a/content/browser/media/capture/aura_window_capture_machine.cc
+++ b/content/browser/media/capture/aura_window_capture_machine.cc
@@ -367,7 +367,7 @@
       base::Bind(&CopyOutputFinishedForVideo, weak_factory_.GetWeakPtr(),
                  event_time, capture_frame_cb, video_frame, region_in_frame,
                  base::Passed(&release_callback)));
-  media::LetterboxYUV(video_frame.get(), region_in_frame);
+  media::LetterboxVideoFrame(video_frame.get(), region_in_frame);
   return true;
 }
 
diff --git a/content/browser/notifications/OWNERS b/content/browser/notifications/OWNERS
index 81314bd..c172d4b 100644
--- a/content/browser/notifications/OWNERS
+++ b/content/browser/notifications/OWNERS
@@ -4,8 +4,8 @@
 # //content/renderer/notifications/
 # //content/test/mock_platform_notification_service.*
 
+awdf@chromium.org
 mkwst@chromium.org
-mvanouwerkerk@chromium.org
 peter@chromium.org
 
 # TEAM: platform-capabilities@chromium.org
diff --git a/content/browser/renderer_host/p2p/socket_host_tcp.cc b/content/browser/renderer_host/p2p/socket_host_tcp.cc
index 83d8168d..12ead42 100644
--- a/content/browser/renderer_host/p2p/socket_host_tcp.cc
+++ b/content/browser/renderer_host/p2p/socket_host_tcp.cc
@@ -126,7 +126,8 @@
   // The default SSLConfig is good enough for us for now.
   const net::SSLConfig ssl_config;
   socket_ = proxy_resolving_socket_factory_->CreateSocket(
-      ssl_config, GURL("https://" + dest_host_port_pair.ToString()));
+      ssl_config, GURL("https://" + dest_host_port_pair.ToString()),
+      false /*use_tls*/);
 
   int status = socket_->Connect(
       base::Bind(&P2PSocketHostTcpBase::OnConnected,
diff --git a/content/browser/renderer_host/render_frame_metadata_provider_impl.cc b/content/browser/renderer_host/render_frame_metadata_provider_impl.cc
index 46aff7e..d16a615 100644
--- a/content/browser/renderer_host/render_frame_metadata_provider_impl.cc
+++ b/content/browser/renderer_host/render_frame_metadata_provider_impl.cc
@@ -58,6 +58,11 @@
     observer.OnRenderFrameSubmission();
 }
 
+void RenderFrameMetadataProviderImpl::SetLastRenderFrameMetadataForTest(
+    cc::RenderFrameMetadata metadata) {
+  last_render_frame_metadata_ = metadata;
+}
+
 void RenderFrameMetadataProviderImpl::OnRenderFrameMetadataChanged(
     uint32_t frame_token,
     const cc::RenderFrameMetadata& metadata) {
diff --git a/content/browser/renderer_host/render_frame_metadata_provider_impl.h b/content/browser/renderer_host/render_frame_metadata_provider_impl.h
index b59187e..48ab56154 100644
--- a/content/browser/renderer_host/render_frame_metadata_provider_impl.h
+++ b/content/browser/renderer_host/render_frame_metadata_provider_impl.h
@@ -23,7 +23,7 @@
 // notified of all frame submissions.
 //
 // All RenderFrameMetadataProvider::Observer will be notified.
-class RenderFrameMetadataProviderImpl
+class CONTENT_EXPORT RenderFrameMetadataProviderImpl
     : public RenderFrameMetadataProvider,
       public mojom::RenderFrameMetadataObserverClient {
  public:
@@ -44,6 +44,8 @@
   const cc::RenderFrameMetadata& LastRenderFrameMetadata() const override;
 
  private:
+  friend class FakeRenderWidgetHostViewAura;
+
   // Paired with the mojom::RenderFrameMetadataObserverClient overrides, these
   // methods are enqueued in |frame_token_message_queue_|. They are invoked when
   // the browser process receives their associated frame tokens. These then
@@ -51,6 +53,10 @@
   void OnFrameTokenRenderFrameMetadataChanged(cc::RenderFrameMetadata metadata);
   void OnFrameTokenFrameSubmissionForTesting();
 
+  // Set |last_render_frame_metadata_| to the given |metadata| for testing
+  // purpose.
+  void SetLastRenderFrameMetadataForTest(cc::RenderFrameMetadata metadata);
+
   // mojom::RenderFrameMetadataObserverClient:
   void OnRenderFrameMetadataChanged(
       uint32_t frame_token,
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 29d8878..42252a6 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -54,6 +54,9 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "cc/base/switches.h"
@@ -342,6 +345,50 @@
 
 #endif
 
+// Globally tracks all existing RenderProcessHostImpl instances.
+//
+// TODO(https://crbug.com/813045): Remove this.
+class RenderProcessMemoryDumpProvider
+    : public base::trace_event::MemoryDumpProvider {
+ public:
+  RenderProcessMemoryDumpProvider() {
+    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+        this, "RenderProcessHost", base::ThreadTaskRunnerHandle::Get());
+  }
+
+  ~RenderProcessMemoryDumpProvider() override {
+    base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+        this);
+  }
+
+  void AddHost(RenderProcessHostImpl* host) { hosts_.insert(host); }
+  void RemoveHost(RenderProcessHostImpl* host) { hosts_.erase(host); }
+
+ private:
+  // base::trace_event::MemoryDumpProvider:
+  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+                    base::trace_event::ProcessMemoryDump* pmd) override {
+    for (auto* host : hosts_) {
+      base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(
+          base::StringPrintf("mojo/render_process_host/0x%" PRIxPTR,
+                             reinterpret_cast<uintptr_t>(host)));
+      dump->AddScalar("is_initialized",
+                      base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                      host->is_initialized() ? 1 : 0);
+    }
+    return true;
+  }
+
+  std::set<RenderProcessHostImpl*> hosts_;
+
+  DISALLOW_COPY_AND_ASSIGN(RenderProcessMemoryDumpProvider);
+};
+
+RenderProcessMemoryDumpProvider& GetMemoryDumpProvider() {
+  static base::NoDestructor<RenderProcessMemoryDumpProvider> tracker;
+  return *tracker;
+}
+
 // the global list of all renderer processes
 base::LazyInstance<base::IDMap<RenderProcessHost*>>::Leaky g_all_hosts =
     LAZY_INSTANCE_INITIALIZER;
@@ -1367,6 +1414,8 @@
 
   if (!base::FeatureList::IsEnabled(features::kMash))
     gpu_client_.reset(new GpuClient(GetID()));
+
+  GetMemoryDumpProvider().AddHost(this);
 }
 
 // static
@@ -1430,6 +1479,8 @@
     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
                             base::BindOnce(&RemoveShaderInfo, GetID()));
   }
+
+  GetMemoryDumpProvider().RemoveHost(this);
 }
 
 bool RenderProcessHostImpl::Init() {
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 89ff6281..dab3d66d 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -391,6 +391,8 @@
     return *permission_service_context_;
   }
 
+  bool is_initialized() const { return is_initialized_; }
+
  protected:
   // A proxy for our IPC::Channel that lives on the IO thread.
   std::unique_ptr<IPC::ChannelProxy> channel_;
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 5cf5706..e5038f8 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -694,11 +694,7 @@
 
   // If we navigated in background, clear the displayed graphics of the
   // previous page before going visible.
-  if (new_content_rendering_timeout_ &&
-      new_content_rendering_timeout_->IsRunning()) {
-    new_content_rendering_timeout_->Stop();
-    ClearDisplayedGraphics();
-  }
+  ForceFirstFrameAfterNavigationTimeout();
 
   SendScreenRects();
   RestartHangMonitorTimeoutIfNecessary();
@@ -3012,6 +3008,14 @@
   new_content_rendering_timeout_->Stop();
 }
 
+void RenderWidgetHostImpl::ForceFirstFrameAfterNavigationTimeout() {
+  if (new_content_rendering_timeout_ &&
+      new_content_rendering_timeout_->IsRunning()) {
+    new_content_rendering_timeout_->Stop();
+    ClearDisplayedGraphics();
+  }
+}
+
 void RenderWidgetHostImpl::StopFling() {
   if (input_router_)
     input_router_->StopFling();
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index a9e5d18..6206a2bb 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -95,7 +95,6 @@
 class BrowserAccessibilityManager;
 class InputRouter;
 class MockRenderWidgetHost;
-class RenderFrameMetadataProvider;
 class RenderWidgetHostOwnerDelegate;
 class SyntheticGestureController;
 class TimeoutMonitor;
@@ -602,7 +601,7 @@
     return last_auto_resize_request_number_;
   }
 
-  RenderFrameMetadataProvider* render_frame_metadata_provider() {
+  RenderFrameMetadataProviderImpl* render_frame_metadata_provider() {
     return &render_frame_metadata_provider_;
   }
 
@@ -658,6 +657,13 @@
 
   void DidReceiveFirstFrameAfterNavigation();
 
+  // The RenderWidgetHostImpl will keep showing the old page (for a while) after
+  // navigation until the first frame of the new page arrives. This reduces
+  // flicker. However, if for some reason it is known that the frames won't be
+  // arriving, this call can be used for force a timeout, to avoid showing the
+  // content of the old page under UI from the new page.
+  void ForceFirstFrameAfterNavigationTimeout();
+
   uint32_t current_content_source_id() { return current_content_source_id_; }
 
   void SetScreenOrientationForTesting(uint16_t angle,
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index a62fdee..b7a19301 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -878,12 +878,6 @@
   DCHECK(delegated_frame_host_);
   TRACE_EVENT0("content", "RenderWidgetHostViewAura::OnSwapCompositorFrame");
 
-  // Override the background color to the current compositor background.
-  // This allows us to, when navigating to a new page, transfer this color to
-  // that page. This allows us to pass this background color to new views on
-  // navigation.
-  UpdateBackgroundColorFromRenderer(frame.metadata.root_background_color);
-
   last_scroll_offset_ = frame.metadata.root_scroll_offset;
   if (IsUseZoomForDSFEnabled()) {
     // With zoom-for-DSF Blink pixel coordinates are used and zoom is used to
@@ -1783,6 +1777,16 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// RenderWidgetHostViewAura, RenderFrameMetadataProvider::Observer
+// implementation:
+void RenderWidgetHostViewAura::OnRenderFrameMetadataChanged() {
+  UpdateBackgroundColorFromRenderer(host()
+                                        ->render_frame_metadata_provider()
+                                        ->LastRenderFrameMetadata()
+                                        .root_background_color);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // RenderWidgetHostViewAura, private:
 
 RenderWidgetHostViewAura::~RenderWidgetHostViewAura() {
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index 3c65a71..f54822b 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -283,6 +283,9 @@
   void OnHostMovedInPixels(aura::WindowTreeHost* host,
                            const gfx::Point& new_origin_in_pixels) override;
 
+  // RenderFrameMetadataProvider::Observer
+  void OnRenderFrameMetadataChanged() override;
+
 #if defined(OS_WIN)
   // Gets the HWND of the host window.
   HWND GetHostWindowHWND() const;
@@ -522,7 +525,9 @@
   void UpdateNeedsBeginFramesInternal();
 
   // Applies background color without notifying the RenderWidget about
-  // opaqueness changes.
+  // opaqueness changes. This allows us to, when navigating to a new page,
+  // transfer this color to that page. This allows us to pass this background
+  // color to new views on navigation.
   void UpdateBackgroundColorFromRenderer(SkColor color);
 
   const bool is_mus_browser_plugin_guest_;
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index eb3ee7b7..a916856 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -25,6 +25,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "cc/trees/render_frame_metadata.h"
 #include "components/viz/common/features.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/quads/compositor_frame.h"
@@ -50,6 +51,7 @@
 #include "content/browser/renderer_host/input/mouse_wheel_event_queue.h"
 #include "content/browser/renderer_host/overscroll_controller.h"
 #include "content/browser/renderer_host/overscroll_controller_delegate.h"
+#include "content/browser/renderer_host/render_frame_metadata_provider_impl.h"
 #include "content/browser/renderer_host/render_view_host_factory.h"
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
@@ -137,8 +139,6 @@
     RenderWidgetHostViewAura* render_widget_host_view,
     std::unique_ptr<DelegatedFrameHostClient> delegated_frame_host_client);
 
-namespace {
-
 constexpr uint64_t kFrameIndexStart =
     viz::CompositorFrameSinkSupport::kFrameIndexStart;
 
@@ -352,6 +352,11 @@
     return event_handler()->pointer_state();
   }
 
+  void SetRenderFrameMetadata(cc::RenderFrameMetadata metadata) {
+    host()->render_frame_metadata_provider()->SetLastRenderFrameMetadataForTest(
+        metadata);
+  }
+
   bool resize_locked() const {
     return delegated_frame_host_client_->resize_locked();
   }
@@ -479,8 +484,6 @@
   kAsyncWheelEvents,
 };
 
-}  // namespace
-
 class RenderWidgetHostViewAuraTest : public testing::Test {
  public:
   RenderWidgetHostViewAuraTest(
@@ -2759,11 +2762,10 @@
       gfx::Rect());
   view_->SetSize(frame_size);
   view_->Show();
-  viz::CompositorFrame frame =
-      MakeDelegatedFrame(1.f, frame_size, gfx::Rect(frame_size));
-  frame.metadata.root_background_color = SK_ColorRED;
-  view_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr);
-
+  cc::RenderFrameMetadata metadata;
+  metadata.root_background_color = SK_ColorRED;
+  view_->SetRenderFrameMetadata(metadata);
+  view_->OnRenderFrameMetadataChanged();
   ui::Layer* parent_layer = view_->GetNativeView()->layer();
 
   EXPECT_EQ(gfx::Rect(0, 0, 100, 100), parent_layer->bounds());
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc
index 37da659..122a22ae 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.cc
+++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -51,7 +51,9 @@
           features::kTouchpadAndWheelScrollLatching)),
       web_contents_accessibility_(nullptr),
       renderer_frame_number_(0),
-      weak_factory_(this) {}
+      weak_factory_(this) {
+  host_->render_frame_metadata_provider()->AddObserver(this);
+}
 
 RenderWidgetHostViewBase::~RenderWidgetHostViewBase() {
   DCHECK(!mouse_locked_);
@@ -67,6 +69,8 @@
   // so that the |text_input_manager_| will free its state.
   if (text_input_manager_)
     text_input_manager_->Unregister(this);
+  if (host_)
+    host_->render_frame_metadata_provider()->RemoveObserver(this);
 }
 
 RenderWidgetHostImpl* RenderWidgetHostViewBase::GetFocusedWidget() const {
@@ -92,6 +96,10 @@
   return false;
 }
 
+void RenderWidgetHostViewBase::OnRenderFrameMetadataChanged() {}
+
+void RenderWidgetHostViewBase::OnRenderFrameSubmission() {}
+
 void RenderWidgetHostViewBase::SetBackgroundColorToDefault() {
   SetBackgroundColor(SK_ColorWHITE);
 }
@@ -468,7 +476,10 @@
 }
 
 void RenderWidgetHostViewBase::Destroy() {
-  host_ = nullptr;
+  if (host_) {
+    host_->render_frame_metadata_provider()->RemoveObserver(this);
+    host_ = nullptr;
+  }
 }
 
 void RenderWidgetHostViewBase::TextInputStateChanged(
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index e44d950..5937ae30 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -23,6 +23,7 @@
 #include "components/viz/common/surfaces/surface_id.h"
 #include "content/browser/renderer_host/event_with_latency_info.h"
 #include "content/common/content_export.h"
+#include "content/public/browser/render_frame_metadata_provider.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/common/input_event_ack_state.h"
 #include "content/public/common/screen_info.h"
@@ -88,8 +89,10 @@
 struct TextInputState;
 
 // Basic implementation shared by concrete RenderWidgetHostView subclasses.
-class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView,
-                                                public IPC::Listener {
+class CONTENT_EXPORT RenderWidgetHostViewBase
+    : public RenderWidgetHostView,
+      public IPC::Listener,
+      public RenderFrameMetadataProvider::Observer {
  public:
   using CreateCompositorFrameSinkCallback =
       base::OnceCallback<void(const viz::FrameSinkId&)>;
@@ -136,6 +139,10 @@
   // IPC::Listener implementation:
   bool OnMessageReceived(const IPC::Message& msg) override;
 
+  // RenderFrameMetadataProvider::Observer
+  void OnRenderFrameMetadataChanged() override;
+  void OnRenderFrameSubmission() override;
+
   void SetPopupType(blink::WebPopupType popup_type);
 
   blink::WebPopupType GetPopupType();
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 6f871af2..8f029c47 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -408,7 +408,6 @@
   // RenderWidgetHostInputEventRouter afterwards.
   NotifyObserversAboutShutdown();
 
-  host()->SetView(nullptr);
   RenderWidgetHostViewBase::Destroy();
 
   delete this;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 992ea36..ce2f473 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3334,12 +3334,14 @@
 
 void WebContentsImpl::SaveFrame(const GURL& url,
                                 const Referrer& referrer) {
-  SaveFrameWithHeaders(url, referrer, std::string());
+  SaveFrameWithHeaders(url, referrer, std::string(), base::string16());
 }
 
-void WebContentsImpl::SaveFrameWithHeaders(const GURL& url,
-                                           const Referrer& referrer,
-                                           const std::string& headers) {
+void WebContentsImpl::SaveFrameWithHeaders(
+    const GURL& url,
+    const Referrer& referrer,
+    const std::string& headers,
+    const base::string16& suggested_filename) {
   if (!GetLastCommittedURL().is_valid())
     return;
   if (delegate_ && delegate_->SaveFrame(url, referrer))
@@ -3401,6 +3403,7 @@
       params->add_request_header(key_value.first, key_value.second);
     }
   }
+  params->set_suggested_name(suggested_filename);
   params->set_download_source(download::DownloadSource::WEB_CONTENTS_API);
   BrowserContext::GetDownloadManager(GetBrowserContext())
       ->DownloadUrl(std::move(params));
@@ -4684,6 +4687,16 @@
                                           const base::string16& default_prompt,
                                           JavaScriptDialogType dialog_type,
                                           IPC::Message* reply_msg) {
+  // Ensure that if showing a dialog is the first thing that a page does, that
+  // the contents of the previous page aren't shown behind it. This is required
+  // because showing a dialog freezes the renderer, so no frames will be coming
+  // from it. https://crbug.com/823353
+  auto* render_widget_host_impl =
+      static_cast<RenderFrameHostImpl*>(render_frame_host)
+          ->GetRenderWidgetHost();
+  if (render_widget_host_impl)
+    render_widget_host_impl->ForceFirstFrameAfterNavigationTimeout();
+
   // Running a dialog causes an exit to webpage-initiated fullscreen.
   // http://crbug.com/728276
   if (IsFullscreenForCurrentTab())
@@ -4743,6 +4756,16 @@
     RenderFrameHost* render_frame_host,
     bool is_reload,
     IPC::Message* reply_msg) {
+  // Ensure that if showing a dialog is the first thing that a page does, that
+  // the contents of the previous page aren't shown behind it. This is required
+  // because showing a dialog freezes the renderer, so no frames will be coming
+  // from it. https://crbug.com/823353
+  auto* render_widget_host_impl =
+      static_cast<RenderFrameHostImpl*>(render_frame_host)
+          ->GetRenderWidgetHost();
+  if (render_widget_host_impl)
+    render_widget_host_impl->ForceFirstFrameAfterNavigationTimeout();
+
   // Running a dialog causes an exit to webpage-initiated fullscreen.
   // http://crbug.com/728276
   if (IsFullscreenForCurrentTab())
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 6d682fb4..97527f1 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -418,7 +418,8 @@
   void SaveFrame(const GURL& url, const Referrer& referrer) override;
   void SaveFrameWithHeaders(const GURL& url,
                             const Referrer& referrer,
-                            const std::string& headers) override;
+                            const std::string& headers,
+                            const base::string16& suggested_filename) override;
   void GenerateMHTML(const MHTMLGenerationParams& params,
                      const base::Callback<void(int64_t)>& callback) override;
   const std::string& GetContentsMimeType() const override;
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 05abf08..9490c80 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -3566,8 +3566,8 @@
       blink::WebSandboxFlags::kPopups | blink::WebSandboxFlags::kModals |
       blink::WebSandboxFlags::kTopNavigation;
   params.starting_sandbox_flags = expected_flags;
-  WebContentsImpl* new_contents =
-      WebContentsImpl::CreateWithOpener(params, nullptr);
+  std::unique_ptr<WebContentsImpl> new_contents(
+      WebContentsImpl::CreateWithOpener(params, nullptr));
   FrameTreeNode* root = new_contents->GetFrameTree()->root();
   blink::WebSandboxFlags pending_flags =
       root->pending_frame_policy().sandbox_flags;
diff --git a/content/common/content_security_policy/csp_source_list_unittest.cc b/content/common/content_security_policy/csp_source_list_unittest.cc
index 575eed4..2e0838d29 100644
--- a/content/common/content_security_policy/csp_source_list_unittest.cc
+++ b/content/common/content_security_policy/csp_source_list_unittest.cc
@@ -94,10 +94,10 @@
                             false,                      // allow_star:
                             std::vector<CSPSource>());  // source_list
 
-  EXPECT_TRUE(Allow(
-      source_list,
-      GURL("chrome://print/pdf_preview.html?chrome://print/1/0/print.pdf"),
-      &context));
+  EXPECT_TRUE(
+      Allow(source_list,
+            GURL("chrome://print/pdf/index.html?chrome://print/1/0/print.pdf"),
+            &context));
 }
 
 TEST(CSPSourceList, AllowNone) {
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index beb92fce..25e820f 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -1117,8 +1117,11 @@
 // ScopedPageLoadDeferrer is on the stack for SwapOut.
 IPC_MESSAGE_ROUTED0(FrameMsg_SuppressFurtherDialogs)
 
+// Notifies the RenderFrame about a user activation from the browser side.
+IPC_MESSAGE_ROUTED0(FrameMsg_NotifyUserActivation)
+
 // Tells the frame to consider itself to have received a user gesture (based
-// on a user gesture processed in a different process).
+// on a user gesture processed in a different renderer process).
 IPC_MESSAGE_ROUTED0(FrameMsg_SetHasReceivedUserGesture)
 
 // Tells the frame to mark that the previous document on that frame had received
diff --git a/content/common/render_frame_metadata.mojom b/content/common/render_frame_metadata.mojom
index ffc97dc..bf659e5 100644
--- a/content/common/render_frame_metadata.mojom
+++ b/content/common/render_frame_metadata.mojom
@@ -8,6 +8,11 @@
 
 // See components/viz/service/quads/render_frame_metadata.h
 struct RenderFrameMetadata {
+  // The background color of a CompositorFrame. It can be used for filling the
+  // content area if the primary surface is unavailable and fallback is not
+  // specified.
+  uint32 root_background_color;
+
   // Scroll offset of the root layer. This optional parameter is only sent
   // during tests.
   gfx.mojom.Vector2dF? root_scroll_offset;
diff --git a/content/common/render_frame_metadata_struct_traits.cc b/content/common/render_frame_metadata_struct_traits.cc
index c8c2719..2897c954 100644
--- a/content/common/render_frame_metadata_struct_traits.cc
+++ b/content/common/render_frame_metadata_struct_traits.cc
@@ -13,6 +13,7 @@
                   cc::RenderFrameMetadata>::
     Read(content::mojom::RenderFrameMetadataDataView data,
          cc::RenderFrameMetadata* out) {
+  out->root_background_color = data.root_background_color();
   return data.ReadRootScrollOffset(&out->root_scroll_offset);
 }
 
diff --git a/content/common/render_frame_metadata_struct_traits.h b/content/common/render_frame_metadata_struct_traits.h
index ff267126..228a6792 100644
--- a/content/common/render_frame_metadata_struct_traits.h
+++ b/content/common/render_frame_metadata_struct_traits.h
@@ -14,6 +14,11 @@
 template <>
 struct StructTraits<content::mojom::RenderFrameMetadataDataView,
                     cc::RenderFrameMetadata> {
+  static SkColor root_background_color(
+      const cc::RenderFrameMetadata& metadata) {
+    return metadata.root_background_color;
+  }
+
   static base::Optional<gfx::Vector2dF> root_scroll_offset(
       const cc::RenderFrameMetadata& metadata) {
     return metadata.root_scroll_offset;
diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java b/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
index b5a59b7..1c2ea6f 100644
--- a/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/framehost/RenderFrameHostImpl.java
@@ -92,8 +92,8 @@
     }
 
     @Override
-    public void setHasReceivedUserGesture() {
-        nativeSetHasReceivedUserGesture(mNativeRenderFrameHostAndroid);
+    public void notifyUserActivation() {
+        nativeNotifyUserActivation(mNativeRenderFrameHostAndroid);
     }
 
     /**
@@ -109,5 +109,5 @@
             long nativeRenderFrameHostAndroid, Callback<String> callback);
     private native UnguessableToken nativeGetAndroidOverlayRoutingToken(
             long nativeRenderFrameHostAndroid);
-    private native void nativeSetHasReceivedUserGesture(long nativeRenderFrameHostAndroid);
+    private native void nativeNotifyUserActivation(long nativeRenderFrameHostAndroid);
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectImpl.java b/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectImpl.java
index af0f1f3..b8cf1c0 100644
--- a/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectImpl.java
@@ -9,6 +9,7 @@
 import org.chromium.blink.mojom.RemoteInvocationArgument;
 import org.chromium.blink.mojom.RemoteInvocationError;
 import org.chromium.blink.mojom.RemoteInvocationResult;
+import org.chromium.blink.mojom.RemoteInvocationResultValue;
 import org.chromium.blink.mojom.RemoteObject;
 import org.chromium.blink.mojom.SingletonJavaScriptValue;
 import org.chromium.mojo.system.MojoException;
@@ -141,6 +142,8 @@
             // return arrays. Spec requires calling the method and converting the
             // result to a JavaScript array.
             RemoteInvocationResult result = new RemoteInvocationResult();
+            result.value = new RemoteInvocationResultValue();
+            result.value.setSingletonValue(SingletonJavaScriptValue.UNDEFINED);
             callback.call(result);
             return;
         }
@@ -356,8 +359,37 @@
     }
 
     private RemoteInvocationResult convertResult(Object result, Class<?> returnType) {
-        // TODO(jbroman): Convert result.
-        return new RemoteInvocationResult();
+        // Methods returning arrays should not be called (for legacy reasons).
+        assert !returnType.isArray();
+
+        // LIVECONNECT_COMPLIANCE: The specification suggests that the conversion should happen
+        // based on the type of the result value. Existing behavior is to rely on the declared
+        // return type of the method. This means, for instance, that a java.lang.String returned
+        // from a method declared as returning java.lang.Object will not be converted to a
+        // JavaScript string.
+        RemoteInvocationResultValue resultValue = new RemoteInvocationResultValue();
+        if (returnType == void.class) {
+            resultValue.setSingletonValue(SingletonJavaScriptValue.UNDEFINED);
+        } else if (returnType == boolean.class) {
+            resultValue.setBooleanValue((Boolean) result);
+        } else if (returnType == char.class) {
+            resultValue.setNumberValue((Character) result);
+        } else if (returnType.isPrimitive()) {
+            resultValue.setNumberValue(((Number) result).doubleValue());
+        } else if (returnType == String.class) {
+            if (result == null) {
+                // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
+                // Spec requires returning a null string.
+                resultValue.setSingletonValue(SingletonJavaScriptValue.UNDEFINED);
+            } else {
+                resultValue.setStringValue(javaStringToMojoString((String) result));
+            }
+        } else {
+            // TODO(jbroman): Implement handling for other objects.
+        }
+        RemoteInvocationResult mojoResult = new RemoteInvocationResult();
+        mojoResult.value = resultValue;
+        return mojoResult;
     }
 
     private static RemoteInvocationResult makeErrorResult(int error) {
@@ -437,4 +469,14 @@
         }
         return String.valueOf(chars);
     }
+
+    private static String16 javaStringToMojoString(String string) {
+        short[] data = new short[string.length()];
+        for (int i = 0; i < data.length; i++) {
+            data[i] = (short) string.charAt(i);
+        }
+        String16 mojoString = new String16();
+        mojoString.data = data;
+        return mojoString;
+    }
 }
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java b/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
index e87bda0..e6a5891 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/RenderFrameHost.java
@@ -35,7 +35,7 @@
     InterfaceProvider getRemoteInterfaces();
 
     /**
-     * Notifies the native RenderFrameHost of a user gesture.
+     * Notifies the native RenderFrameHost about a user activation from the browser side.
      */
-    void setHasReceivedUserGesture();
+    void notifyUserActivation();
 }
diff --git a/content/public/android/junit/src/org/chromium/content/browser/remoteobjects/RemoteObjectImplTest.java b/content/public/android/junit/src/org/chromium/content/browser/remoteobjects/RemoteObjectImplTest.java
index e5ac6242..95ab21e 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/remoteobjects/RemoteObjectImplTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/remoteobjects/RemoteObjectImplTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.content.browser.remoteobjects;
 
+import static org.mockito.AdditionalMatchers.and;
 import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
@@ -22,6 +23,7 @@
 import org.chromium.blink.mojom.RemoteInvocationArgument;
 import org.chromium.blink.mojom.RemoteInvocationError;
 import org.chromium.blink.mojom.RemoteInvocationResult;
+import org.chromium.blink.mojom.RemoteInvocationResultValue;
 import org.chromium.blink.mojom.RemoteObject;
 import org.chromium.blink.mojom.SingletonJavaScriptValue;
 import org.chromium.mojo_base.mojom.String16;
@@ -295,7 +297,7 @@
         RemoteObject.InvokeMethodResponse response = mock(RemoteObject.InvokeMethodResponse.class);
         remoteObject.invokeMethod("returnsIntArray", new RemoteInvocationArgument[] {}, response);
 
-        verify(response).call(resultIsOk());
+        verify(response).call(resultIsUndefined());
     }
 
     @Test
@@ -627,6 +629,106 @@
         verify(consumer, times(2)).accept(null);
     }
 
+    @Test
+    public void testResultConversionVoid() {
+        Object target = new Object() {
+            @TestJavascriptInterface
+            public void returnsVoid() {}
+        };
+
+        RemoteObject remoteObject = new RemoteObjectImpl(target, TestJavascriptInterface.class);
+        RemoteObject.InvokeMethodResponse response = mock(RemoteObject.InvokeMethodResponse.class);
+        remoteObject.invokeMethod("returnsVoid", new RemoteInvocationArgument[] {}, response);
+
+        verify(response).call(resultIsUndefined());
+    }
+
+    @Test
+    public void testConversionResultNumber() {
+        Object target = new Object() {
+            @TestJavascriptInterface
+            public int returnsInt() {
+                return 42;
+            }
+
+            @TestJavascriptInterface
+            public float returnsFloat() {
+                return -1.5f;
+            }
+
+            @TestJavascriptInterface
+            public char returnsChar() {
+                return '\ufeed';
+            }
+        };
+
+        RemoteObject remoteObject = new RemoteObjectImpl(target, TestJavascriptInterface.class);
+        RemoteObject.InvokeMethodResponse response = mock(RemoteObject.InvokeMethodResponse.class);
+        remoteObject.invokeMethod("returnsInt", new RemoteInvocationArgument[] {}, response);
+        remoteObject.invokeMethod("returnsFloat", new RemoteInvocationArgument[] {}, response);
+        remoteObject.invokeMethod("returnsChar", new RemoteInvocationArgument[] {}, response);
+
+        verify(response).call(resultIsNumber(42));
+        verify(response).call(resultIsNumber(-1.5f));
+        verify(response).call(resultIsNumber(0xfeed));
+    }
+
+    @Test
+    public void testConversionResultBoolean() {
+        Object target = new Object() {
+            @TestJavascriptInterface
+            public boolean returnsTrue() {
+                return true;
+            }
+
+            @TestJavascriptInterface
+            public boolean returnsFalse() {
+                return false;
+            }
+        };
+
+        RemoteObject remoteObject = new RemoteObjectImpl(target, TestJavascriptInterface.class);
+        RemoteObject.InvokeMethodResponse response = mock(RemoteObject.InvokeMethodResponse.class);
+        remoteObject.invokeMethod("returnsTrue", new RemoteInvocationArgument[] {}, response);
+        remoteObject.invokeMethod("returnsFalse", new RemoteInvocationArgument[] {}, response);
+
+        InOrder inOrder = inOrder(response);
+        inOrder.verify(response).call(resultIsBoolean(true));
+        inOrder.verify(response).call(resultIsBoolean(false));
+    }
+
+    @Test
+    public void testConversionResultString() {
+        final String stringWithNonAsciiCharacterAndUnpairedSurrogate = "caf\u00e9\ud800";
+        Object target = new Object() {
+            @TestJavascriptInterface
+            public String returnsHello() {
+                return "Hello";
+            }
+
+            @TestJavascriptInterface
+            public String returnsExoticString() {
+                return stringWithNonAsciiCharacterAndUnpairedSurrogate;
+            }
+
+            @TestJavascriptInterface
+            public String returnsNull() {
+                return null;
+            }
+        };
+
+        RemoteObject remoteObject = new RemoteObjectImpl(target, TestJavascriptInterface.class);
+        RemoteObject.InvokeMethodResponse response = mock(RemoteObject.InvokeMethodResponse.class);
+        remoteObject.invokeMethod("returnsHello", new RemoteInvocationArgument[] {}, response);
+        remoteObject.invokeMethod(
+                "returnsExoticString", new RemoteInvocationArgument[] {}, response);
+        remoteObject.invokeMethod("returnsNull", new RemoteInvocationArgument[] {}, response);
+
+        verify(response).call(resultIsString("Hello"));
+        verify(response).call(resultIsString(stringWithNonAsciiCharacterAndUnpairedSurrogate));
+        verify(response).call(resultIsUndefined());
+    }
+
     private RemoteInvocationResult resultHasError(final int error) {
         return ArgumentMatchers.argThat(result -> result.error == error);
     }
@@ -635,6 +737,42 @@
         return resultHasError(RemoteInvocationError.OK);
     }
 
+    private RemoteInvocationResult resultIsUndefined() {
+        return and(resultIsOk(), ArgumentMatchers.argThat(result -> {
+            return result.value != null
+                    && result.value.which() == RemoteInvocationResultValue.Tag.SingletonValue
+                    && result.value.getSingletonValue() == SingletonJavaScriptValue.UNDEFINED;
+        }));
+    }
+
+    private RemoteInvocationResult resultIsNumber(final double numberValue) {
+        return and(resultIsOk(), ArgumentMatchers.argThat(result -> {
+            return result.value != null
+                    && result.value.which() == RemoteInvocationResultValue.Tag.NumberValue
+                    && result.value.getNumberValue() == numberValue;
+        }));
+    }
+
+    private RemoteInvocationResult resultIsBoolean(final boolean booleanValue) {
+        return and(resultIsOk(), ArgumentMatchers.argThat(result -> {
+            return result.value != null
+                    && result.value.which() == RemoteInvocationResultValue.Tag.BooleanValue
+                    && result.value.getBooleanValue() == booleanValue;
+        }));
+    }
+
+    private RemoteInvocationResult resultIsString(String stringValue) {
+        final short[] expectedData = new short[stringValue.length()];
+        for (int i = 0; i < expectedData.length; i++) {
+            expectedData[i] = (short) stringValue.charAt(i);
+        }
+        return and(resultIsOk(), ArgumentMatchers.argThat(result -> {
+            return result.value != null
+                    && result.value.which() == RemoteInvocationResultValue.Tag.StringValue
+                    && Arrays.equals(result.value.getStringValue().data, expectedData);
+        }));
+    }
+
     private RemoteInvocationArgument numberArgument(double numberValue) {
         RemoteInvocationArgument argument = new RemoteInvocationArgument();
         argument.setNumberValue(numberValue);
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index e2edb70a..8c7ab68 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -70,11 +70,6 @@
   // is present only to support Android WebView and must not be used in other
   // configurations.
   static void AllowInjectingJavaScriptForAndroidWebView();
-
-  // Temporary hack to enable data URLs on Android Webview until PlzNavigate
-  // ships.
-  static void AllowDataUrlNavigationForAndroidWebView();
-  static bool IsDataUrlNavigationAllowedForAndroidWebView();
 #endif
 
   // Returns a RenderFrameHost given its accessibility tree ID.
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 42e684b..1df8201e 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -611,9 +611,11 @@
   // provided, is used to make a request to the URL rather than using cache.
   // Format of |headers| is a new line separated list of key value pairs:
   // "<key1>: <value1>\r\n<key2>: <value2>".
-  virtual void SaveFrameWithHeaders(const GURL& url,
-                                    const Referrer& referrer,
-                                    const std::string& headers) = 0;
+  virtual void SaveFrameWithHeaders(
+      const GURL& url,
+      const Referrer& referrer,
+      const std::string& headers,
+      const base::string16& suggested_filename) = 0;
 
   // Generate an MHTML representation of the current page in the given file.
   // If |use_binary_encoding| is specified, a Content-Transfer-Encoding value of
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/mock/MockRenderFrameHost.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/mock/MockRenderFrameHost.java
index bdd9b27..7875778 100644
--- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/mock/MockRenderFrameHost.java
+++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/mock/MockRenderFrameHost.java
@@ -26,5 +26,5 @@
     }
 
     @Override
-    public void setHasReceivedUserGesture() {}
+    public void notifyUserActivation() {}
 }
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 2947844..f1ad61b 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -2483,6 +2483,16 @@
   return simple_loader->NetError();
 }
 
+void EnsureCookiesFlushed(BrowserContext* browser_context) {
+  BrowserContext::ForEachStoragePartition(
+      browser_context, base::BindRepeating([](StoragePartition* partition) {
+        base::RunLoop run_loop;
+        partition->GetCookieManagerForBrowserProcess()->FlushCookieStore(
+            run_loop.QuitClosure());
+        run_loop.Run();
+      }));
+}
+
 bool HasValidProcessForProcessGroup(const std::string& process_group_name) {
   return ServiceManagerContext::HasValidProcessForProcessGroup(
       process_group_name);
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 84201c5..36a1b4a6 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -1109,6 +1109,10 @@
                      int process_id = 0,
                      int render_frame_id = 0);
 
+// Ensures that all StoragePartitions for the given BrowserContext have their
+// cookies flushed to disk.
+void EnsureCookiesFlushed(BrowserContext* browser_context);
+
 // Returns true if there is a valid process for |process_group_name|. Must be
 // called on the IO thread.
 bool HasValidProcessForProcessGroup(const std::string& process_group_name);
diff --git a/content/public/test/test_renderer_host.cc b/content/public/test/test_renderer_host.cc
index 8ba67bab..305d531 100644
--- a/content/public/test/test_renderer_host.cc
+++ b/content/public/test/test_renderer_host.cc
@@ -16,6 +16,7 @@
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/site_instance_impl.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_widget_host_iterator.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/test/browser_side_navigation_test_utils.h"
@@ -296,8 +297,12 @@
 #endif
 
   // Delete any RenderProcessHosts before the BrowserContext goes away.
-  if (rvh_test_enabler_->rph_factory_)
+  if (rvh_test_enabler_->rph_factory_) {
+    auto render_widget_hosts = RenderWidgetHost::GetRenderWidgetHosts();
+    ASSERT_EQ(nullptr, render_widget_hosts->GetNextHost()) <<
+        "Test is leaking at least one RenderWidgetHost.";
     rvh_test_enabler_->rph_factory_.reset();
+  }
 
   rvh_test_enabler_.reset();
 
diff --git a/content/public/test/web_contents_tester.h b/content/public/test/web_contents_tester.h
index b975bcd..a8d2b2e2 100644
--- a/content/public/test/web_contents_tester.h
+++ b/content/public/test/web_contents_tester.h
@@ -114,7 +114,10 @@
 
   // Returns headers that were passed in the previous SaveFrameWithHeaders(...)
   // call.
-  virtual const std::string& GetSaveFrameHeaders() = 0;
+  virtual const std::string& GetSaveFrameHeaders() const = 0;
+
+  // Returns the suggested file name passed in the SaveFrameWithHeaders call.
+  virtual const base::string16& GetSuggestedFileName() const = 0;
 
   // Returns whether a download request triggered via DownloadImage() is in
   // progress for |url|.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 9bd0d8e..c293c90 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1823,8 +1823,8 @@
     IPC_MESSAGE_HANDLER(FrameMsg_MixedContentFound, OnMixedContentFound)
     IPC_MESSAGE_HANDLER(FrameMsg_SetOverlayRoutingToken,
                         OnSetOverlayRoutingToken)
-    IPC_MESSAGE_HANDLER(FrameMsg_SetHasReceivedUserGesture,
-                        OnSetHasReceivedUserGesture)
+    IPC_MESSAGE_HANDLER(FrameMsg_NotifyUserActivation, OnNotifyUserActivation)
+
 #if defined(OS_ANDROID)
     IPC_MESSAGE_HANDLER(FrameMsg_ActivateNearestFindResult,
                         OnActivateNearestFindResult)
@@ -3981,6 +3981,8 @@
   params.url = request.Url();
   params.referrer = RenderViewImpl::GetReferrerFromRequest(frame_, request);
   params.initiator_origin = request.RequestorOrigin();
+  if (request.GetSuggestedFilename().has_value())
+    params.suggested_name = request.GetSuggestedFilename()->Utf16();
 
   Send(new FrameHostMsg_DownloadUrl(params));
 }
@@ -6516,10 +6518,6 @@
   pending_routing_token_callbacks_.clear();
 }
 
-void RenderFrameImpl::OnSetHasReceivedUserGesture() {
-  frame_->SetHasReceivedUserGesture();
-}
-
 void RenderFrameImpl::RequestOverlayRoutingToken(
     media::RoutingTokenCallback callback) {
   if (overlay_routing_token_.has_value()) {
@@ -6534,6 +6532,10 @@
   pending_routing_token_callbacks_.push_back(std::move(callback));
 }
 
+void RenderFrameImpl::OnNotifyUserActivation() {
+  frame_->NotifyUserActivation();
+}
+
 #if BUILDFLAG(USE_EXTERNAL_POPUP_MENU)
 #if defined(OS_MACOSX)
 void RenderFrameImpl::OnSelectPopupMenuItem(int selected_index) {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 611a112..ffb8a794 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1078,7 +1078,7 @@
   void OnFindMatchRects(int current_version);
 #endif
   void OnSetOverlayRoutingToken(const base::UnguessableToken& token);
-  void OnSetHasReceivedUserGesture();
+  void OnNotifyUserActivation();
 
 #if BUILDFLAG(USE_EXTERNAL_POPUP_MENU)
 #if defined(OS_MACOSX)
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 448d5c5..36d89f7 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -724,6 +724,13 @@
 
     output_name = content_shell_framework_name
 
+    framework_version = "C"
+
+    framework_contents = [
+      "Helpers",
+      "Resources",
+    ]
+
     sources = [
       "app/shell_content_main.cc",
       "app/shell_content_main.h",
@@ -758,11 +765,11 @@
     if (is_component_build) {
       # Set up the rpath for the framework so that it can find dylibs in the
       # root output directory. The framework is at
-      # Content Shell.app/Contents/Frameworks/Content Shell Framework.framework/Content Shell Framework
+      # Content Shell.app/Contents/Frameworks/Content Shell Framework.framework/Versions/C/Content Shell Framework
       # so use loader_path to go back to the root output directory.
       ldflags += [
         "-rpath",
-        "@loader_path/../../../..",
+        "@loader_path/../../../../../..",
       ]
     }
 
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 3c2e76a..89dd771 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -200,6 +200,7 @@
 instrumentation_test_apk("content_shell_test_apk") {
   deps = [
     "//base:base_java_test_support",
+    "//base:base_javatests",
     "//content/public/android:content_javatests",
     "//net/android:net_javatests",
     "//third_party/android_support_test_runner:runner_java",
diff --git a/content/shell/test_runner/mock_web_document_subresource_filter.cc b/content/shell/test_runner/mock_web_document_subresource_filter.cc
index 2b3f68c..c0c7b12 100644
--- a/content/shell/test_runner/mock_web_document_subresource_filter.cc
+++ b/content/shell/test_runner/mock_web_document_subresource_filter.cc
@@ -50,4 +50,8 @@
   return true;
 }
 
+bool MockWebDocumentSubresourceFilter::GetIsAssociatedWithAdSubframe() const {
+  return false;
+}
+
 }  // namespace test_runner
diff --git a/content/shell/test_runner/mock_web_document_subresource_filter.h b/content/shell/test_runner/mock_web_document_subresource_filter.h
index c1aff78..4cec93f 100644
--- a/content/shell/test_runner/mock_web_document_subresource_filter.h
+++ b/content/shell/test_runner/mock_web_document_subresource_filter.h
@@ -32,6 +32,7 @@
       const blink::WebURL& url) override;
   void ReportDisallowedLoad() override;
   bool ShouldLogToConsole() override;
+  bool GetIsAssociatedWithAdSubframe() const override;
 
  private:
   LoadPolicy getLoadPolicyImpl(const blink::WebURL& url);
diff --git a/content/test/data/download/download-attribute-with-target.html b/content/test/data/download/download-attribute-with-target.html
new file mode 100644
index 0000000..e658fcc3
--- /dev/null
+++ b/content/test/data/download/download-attribute-with-target.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+<body>
+<a download="suggested-filename" href="data:application/octet-stream, ..." target="_blank">link</a>
+<script>
+  var anchorElement = document.querySelector('a[download]');
+  anchorElement.click();
+</script>
+</body>
+</html>
diff --git a/content/test/gpu/PRESUBMIT.py b/content/test/gpu/PRESUBMIT.py
index 8e711b1..649911a6 100644
--- a/content/test/gpu/PRESUBMIT.py
+++ b/content/test/gpu/PRESUBMIT.py
@@ -36,7 +36,7 @@
     cl,
     [
       'luci.chromium.try:linux_optional_gpu_tests_rel',
-      'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel',
+      'luci.chromium.try:mac_optional_gpu_tests_rel',
       'master.tryserver.chromium.win:win_optional_gpu_tests_rel',
       'master.tryserver.chromium.android:android_optional_gpu_tests_rel',
     ],
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc
index 8d73aab..69a40ee 100644
--- a/content/test/test_web_contents.cc
+++ b/content/test/test_web_contents.cc
@@ -161,10 +161,14 @@
   rfh->SendNavigateWithParams(&params, was_within_same_document);
 }
 
-const std::string& TestWebContents::GetSaveFrameHeaders() {
+const std::string& TestWebContents::GetSaveFrameHeaders() const {
   return save_frame_headers_;
 }
 
+const base::string16& TestWebContents::GetSuggestedFileName() const {
+  return suggested_filename_;
+}
+
 bool TestWebContents::HasPendingDownloadImage(const GURL& url) {
   return !pending_image_downloads_[url].empty();
 }
@@ -376,10 +380,13 @@
                                                   int route_id) {
 }
 
-void TestWebContents::SaveFrameWithHeaders(const GURL& url,
-                                           const Referrer& referrer,
-                                           const std::string& headers) {
+void TestWebContents::SaveFrameWithHeaders(
+    const GURL& url,
+    const Referrer& referrer,
+    const std::string& headers,
+    const base::string16& suggested_filename) {
   save_frame_headers_ = headers;
+  suggested_filename_ = suggested_filename;
 }
 
 void TestWebContents::SetMainFrameMimeType(const std::string& mime_type) {
diff --git a/content/test/test_web_contents.h b/content/test/test_web_contents.h
index 579ae85..73361dd 100644
--- a/content/test/test_web_contents.h
+++ b/content/test/test_web_contents.h
@@ -85,7 +85,8 @@
       NavigationHandle* navigation_handle,
       scoped_refptr<net::HttpResponseHeaders> response_headers) override;
   void SetOpener(WebContents* opener) override;
-  const std::string& GetSaveFrameHeaders() override;
+  const std::string& GetSaveFrameHeaders() const override;
+  const base::string16& GetSuggestedFileName() const override;
   bool HasPendingDownloadImage(const GURL& url) override;
   bool TestDidDownloadImage(
       const GURL& url,
@@ -170,7 +171,8 @@
   void ShowCreatedFullscreenWidget(int process_id, int route_id) override;
   void SaveFrameWithHeaders(const GURL& url,
                             const Referrer& referrer,
-                            const std::string& headers) override;
+                            const std::string& headers,
+                            const base::string16& suggested_filename) override;
 
   RenderViewHostDelegateView* delegate_view_override_;
 
@@ -179,6 +181,7 @@
   int expect_set_history_offset_and_length_history_offset_;
   int expect_set_history_offset_and_length_history_length_;
   std::string save_frame_headers_;
+  base::string16 suggested_filename_;
   // Map keyed by image URL. Values are <id, callback> pairs.
   std::map<GURL, std::list<std::pair<int, ImageDownloadCallback>>>
       pending_image_downloads_;
diff --git a/docs/linux_chromium_packages.md b/docs/linux_chromium_packages.md
index 8df344b7..775d440a6 100644
--- a/docs/linux_chromium_packages.md
+++ b/docs/linux_chromium_packages.md
@@ -17,6 +17,7 @@
 | ALT Linux  | Andrey Cherepanov (Андрей Черепанов) `cas@altlinux.org` | http://packages.altlinux.org/en/Sisyphus/srpms/chromium | http://git.altlinux.org/gears/c/chromium.git?a=tree |
 | Mageia     | Dexter Morgan `dmorgan@mageia.org` | http://svnweb.mageia.org/packages/cauldron/chromium-browser-stable/current/SPECS/ | http://svnweb.mageia.org/packages/cauldron/chromium-browser-stable/current/SOURCES/ |
 | NixOS      | aszlig `"^[0-9]+$"@regexmail.net` | http://hydra.nixos.org/search?query=pkgs.chromium | https://github.com/NixOS/nixpkgs/tree/master/pkgs/applications/networking/browsers/chromium |
+| OpenMandriva | Bernhard Rosenkraenzer `bero@lindev.ch` | n/a | https://github.com/OpenMandrivaAssociation/chromium-browser-stable https://github.com/OpenMandrivaAssociation/chromium-browser-beta https://github.com/OpenMandrivaAssociation/chromium-browser-dev |
 | Fedora     | Tom Callaway `tcallawa@redhat.com` | https://src.fedoraproject.org/rpms/chromium/ | https://src.fedoraproject.org/rpms/chromium/tree/master |
 | Yocto      | Raphael Kubo da Costa `raphael.kubo.da.costa@intel.com` | https://github.com/OSSystems/meta-browser | https://github.com/OSSystems/meta-browser/tree/master/recipes-browser/chromium/files |
 
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index bf9ba62..905631c 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -118,8 +118,6 @@
     "test/logging_timer.h",
     "test/result_catcher.cc",
     "test/result_catcher.h",
-    "test/test_content_browser_client.cc",
-    "test/test_content_browser_client.h",
     "test/test_content_utility_client.cc",
     "test/test_content_utility_client.h",
     "test/test_extension_dir.cc",
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index d1f0abef..58ab8cf 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1289,6 +1289,7 @@
   WEBRTCLOGGINGPRIVATE_STARTEVENTLOGGING,
   VIRTUALKEYBOARDPRIVATE_SETCONTAINERBEHAVIOR,
   QUICKUNLOCKPRIVATE_GETAUTHTOKEN,
+  QUICKUNLOCKPRIVATE_SETLOCKSCREENENABLED,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/browser/extensions_test.cc b/extensions/browser/extensions_test.cc
index c527c15..ef10b04a 100644
--- a/extensions/browser/extensions_test.cc
+++ b/extensions/browser/extensions_test.cc
@@ -9,14 +9,12 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service_factory.h"
 #include "components/prefs/testing_pref_store.h"
-#include "content/public/browser/content_browser_client.h"
 #include "content/public/common/content_client.h"
 #include "content/public/test/test_browser_context.h"
 #include "extensions/browser/extension_pref_value_map.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_prefs_factory.h"
 #include "extensions/browser/test_extensions_browser_client.h"
-#include "extensions/test/test_content_browser_client.h"
 #include "extensions/test/test_content_utility_client.h"
 
 namespace {
@@ -47,7 +45,6 @@
   // posted tasks may use them.
   rvh_test_enabler_.reset();
   thread_bundle_.reset();
-  content::SetBrowserClientForTesting(nullptr);
   content::SetUtilityClientForTesting(nullptr);
 }
 
@@ -59,7 +56,6 @@
 }
 
 void ExtensionsTest::SetUp() {
-  content_browser_client_ = std::make_unique<TestContentBrowserClient>();
   content_utility_client_ = std::make_unique<TestContentUtilityClient>();
   browser_context_ = std::make_unique<content::TestBrowserContext>();
   incognito_context_ = CreateTestIncognitoContext();
@@ -70,7 +66,6 @@
   }
   extensions_browser_client_->SetMainContext(browser_context_.get());
 
-  content::SetBrowserClientForTesting(content_browser_client_.get());
   content::SetUtilityClientForTesting(content_utility_client_.get());
   ExtensionsBrowserClient::Set(extensions_browser_client_.get());
   extensions_browser_client_->set_extension_system_factory(
diff --git a/extensions/browser/extensions_test.h b/extensions/browser/extensions_test.h
index c488858..c6cb8b1 100644
--- a/extensions/browser/extensions_test.h
+++ b/extensions/browser/extensions_test.h
@@ -19,7 +19,6 @@
 
 namespace content {
 class BrowserContext;
-class ContentBrowserClient;
 class ContentUtilityClient;
 class RenderViewHostTestEnabler;
 }
@@ -71,7 +70,6 @@
 
  private:
   content::TestContentClientInitializer content_client_initializer_;
-  std::unique_ptr<content::ContentBrowserClient> content_browser_client_;
   std::unique_ptr<content::ContentUtilityClient> content_utility_client_;
   std::unique_ptr<content::BrowserContext> browser_context_;
   std::unique_ptr<content::BrowserContext> incognito_context_;
diff --git a/extensions/common/api/API_OWNERS b/extensions/common/api/API_OWNERS
index aabb0dd..9853e49 100644
--- a/extensions/common/api/API_OWNERS
+++ b/extensions/common/api/API_OWNERS
@@ -4,3 +4,4 @@
 
 # For Chrome OS apps APIs.
 tbarzic@chromium.org
+stevenjb@chromium.org
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index 24ba9fd2..5c57352 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -201,7 +201,8 @@
         "chrome://chrome-signin/*",
         "chrome://media-router/*",
         "chrome://mobilesetup/*",
-        "chrome://oobe/*"
+        "chrome://oobe/*",
+        "chrome://assistant-optin/*"
       ]
     }
   ],
@@ -558,7 +559,8 @@
       "chrome://chrome-signin/*",
       "chrome://media-router/*",
       "chrome://mobilesetup/*",
-      "chrome://oobe/*"
+      "chrome://oobe/*",
+      "chrome://assistant-optin/*"
     ]
   }],
   "webViewInternal": [{
@@ -573,7 +575,8 @@
       "chrome://chrome-signin/*",
       "chrome://media-router/*",
       "chrome://mobilesetup/*",
-      "chrome://oobe/*"
+      "chrome://oobe/*",
+      "chrome://assistant-optin/*"
     ]
   }],
   "webViewRequest": [{
@@ -586,7 +589,8 @@
       "chrome://chrome-signin/*",
       "chrome://media-router/*",
       "chrome://mobilesetup/*",
-      "chrome://oobe/*"
+      "chrome://oobe/*",
+      "chrome://assistant-optin/*"
     ]
   }]
 }
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index 01f8a88..d71fb85 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -423,6 +423,8 @@
   mac_framework_bundle("app_shell_framework") {
     testonly = true
     output_name = "App Shell Framework"
+    framework_version = "A"
+    framework_contents = [ "Resources" ]
     sources = [
       "app/shell_main_mac.cc",
       "app/shell_main_mac.h",
diff --git a/extensions/test/data/OWNERS b/extensions/test/data/OWNERS
deleted file mode 100644
index d13324a..0000000
--- a/extensions/test/data/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-*
-
-per-file extensions_test_content_utility_manifest_overlay.json=set noparent
-per-file extensions_test_content_utility_manifest_overlay.json=file://ipc/SECURITY_OWNERS
diff --git a/extensions/test/data/extensions_test_content_utility_manifest_overlay.json b/extensions/test/data/extensions_test_content_utility_manifest_overlay.json
deleted file mode 100644
index 21285652..0000000
--- a/extensions/test/data/extensions_test_content_utility_manifest_overlay.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "name": "content_utility",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "provides": {
-        "browser": [
-          "extensions::mojom::ExtensionUnpacker",
-          "extensions::mojom::ManifestParser"
-        ]
-      }
-    }
-  }
-}
diff --git a/extensions/test/test_content_browser_client.cc b/extensions/test/test_content_browser_client.cc
deleted file mode 100644
index 4b83f11..0000000
--- a/extensions/test/test_content_browser_client.cc
+++ /dev/null
@@ -1,37 +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.
-
-#include "extensions/test/test_content_browser_client.h"
-
-#include "base/files/file_util.h"
-#include "base/json/json_reader.h"
-#include "base/path_service.h"
-#include "base/values.h"
-#include "content/public/common/service_names.mojom.h"
-#include "extensions/common/extension_paths.h"
-
-namespace extensions {
-
-TestContentBrowserClient::TestContentBrowserClient() = default;
-
-TestContentBrowserClient::~TestContentBrowserClient() = default;
-
-std::unique_ptr<base::Value>
-TestContentBrowserClient::GetServiceManifestOverlay(base::StringPiece name) {
-  if (name != content::mojom::kUtilityServiceName)
-    return nullptr;
-
-  base::FilePath extensions_test_data_dir;
-  CHECK(base::PathService::Get(DIR_TEST_DATA, &extensions_test_data_dir));
-
-  std::string contents;
-  CHECK(base::ReadFileToString(
-      extensions_test_data_dir.AppendASCII(
-          "extensions_test_content_utility_manifest_overlay.json"),
-      &contents));
-
-  return base::JSONReader::Read(contents);
-}
-
-}  // namespace extensions
diff --git a/extensions/test/test_content_browser_client.h b/extensions/test/test_content_browser_client.h
deleted file mode 100644
index 9ae3f01..0000000
--- a/extensions/test/test_content_browser_client.h
+++ /dev/null
@@ -1,31 +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.
-
-#ifndef EXTENSIONS_TEST_TEST_CONTENT_BROWSER_CLIENT_H_
-#define EXTENSIONS_TEST_TEST_CONTENT_BROWSER_CLIENT_H_
-
-#include <memory>
-
-#include "base/strings/string_piece.h"
-#include "content/public/browser/content_browser_client.h"
-
-namespace base {
-class Value;
-}
-
-namespace extensions {
-
-class TestContentBrowserClient : public content::ContentBrowserClient {
- public:
-  TestContentBrowserClient();
-  ~TestContentBrowserClient() override;
-
-  // content::ContentBrowserClient:
-  std::unique_ptr<base::Value> GetServiceManifestOverlay(
-      base::StringPiece name) override;
-};
-
-}  // namespace extensions
-
-#endif  // EXTENSIONS_TEST_TEST_CONTENT_BROWSER_CLIENT_H_
diff --git a/google_apis/gcm/BUILD.gn b/google_apis/gcm/BUILD.gn
index 298b5de..0a6ccbb 100644
--- a/google_apis/gcm/BUILD.gn
+++ b/google_apis/gcm/BUILD.gn
@@ -67,6 +67,7 @@
     "//base",
     "//base/third_party/dynamic_annotations",
     "//net",
+    "//services/network:network_service",
     "//third_party/leveldatabase",
     "//url",
   ]
diff --git a/google_apis/gcm/DEPS b/google_apis/gcm/DEPS
index 047dc156..a044cbe8 100644
--- a/google_apis/gcm/DEPS
+++ b/google_apis/gcm/DEPS
@@ -10,4 +10,5 @@
   "+google",  # For third_party/protobuf/src.
   "+net",
   "+third_party/leveldatabase",
+  "+services/network",
 ]
diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc
index fd11a5f32..31942da 100644
--- a/google_apis/gcm/engine/connection_factory_impl.cc
+++ b/google_apis/gcm/engine/connection_factory_impl.cc
@@ -24,6 +24,7 @@
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/client_socket_pool_manager.h"
 #include "net/ssl/ssl_config_service.h"
+#include "services/network/proxy_resolving_client_socket.h"
 
 namespace gcm {
 
@@ -61,9 +62,6 @@
       backoff_policy_(backoff_policy),
       gcm_network_session_(gcm_network_session),
       http_network_session_(http_network_session),
-      net_log_(
-          net::NetLogWithSource::Make(net_log, net::NetLogSourceType::SOCKET)),
-      proxy_resolve_request_(NULL),
       connecting_(false),
       waiting_for_backoff_(false),
       waiting_for_network_online_(false),
@@ -79,11 +77,6 @@
 ConnectionFactoryImpl::~ConnectionFactoryImpl() {
   CloseSocket();
   net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
-  if (proxy_resolve_request_) {
-    gcm_network_session_->proxy_resolution_service()->CancelRequest(
-        proxy_resolve_request_);
-    proxy_resolve_request_ = NULL;
-  }
 }
 
 void ConnectionFactoryImpl::Initialize(
@@ -299,11 +292,11 @@
 }
 
 net::IPEndPoint ConnectionFactoryImpl::GetPeerIP() {
-  if (!socket_handle_.socket())
+  if (!socket_)
     return net::IPEndPoint();
 
   net::IPEndPoint ip_endpoint;
-  int result = socket_handle_.socket()->GetPeerAddress(&ip_endpoint);
+  int result = socket_->GetPeerAddress(&ip_endpoint);
   if (result != net::OK)
     return net::IPEndPoint();
 
@@ -318,7 +311,7 @@
 void ConnectionFactoryImpl::StartConnection() {
   DCHECK(!IsEndpointReachable());
   // TODO(zea): Make this a dcheck again. crbug.com/462319
-  CHECK(!socket_handle_.socket());
+  CHECK(!socket_);
 
   // TODO(zea): if the network is offline, don't attempt to connect.
   // See crbug.com/396687
@@ -327,13 +320,14 @@
   GURL current_endpoint = GetCurrentEndpoint();
   recorder_->RecordConnectionInitiated(current_endpoint.host());
   UpdateFromHttpNetworkSession();
-  int status = gcm_network_session_->proxy_resolution_service()->ResolveProxy(
-      current_endpoint, std::string(), &proxy_info_,
-      base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
-                 weak_ptr_factory_.GetWeakPtr()),
-      &proxy_resolve_request_, NULL, net_log_);
+  net::SSLConfig ssl_config;
+  gcm_network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
+  socket_ = std::make_unique<network::ProxyResolvingClientSocket>(
+      gcm_network_session_, ssl_config, current_endpoint, true /*use_tls*/);
+  int status = socket_->Connect(base::BindRepeating(
+      &ConnectionFactoryImpl::OnConnectDone, weak_ptr_factory_.GetWeakPtr()));
   if (status != net::ERR_IO_PENDING)
-    OnProxyResolveDone(status);
+    OnConnectDone(status);
 }
 
 void ConnectionFactoryImpl::InitHandler() {
@@ -381,8 +375,7 @@
           "but does not have any effect on other Google Cloud messages."
         )");
 
-  connection_handler_->Init(login_request, traffic_annotation,
-                            socket_handle_.socket());
+  connection_handler_->Init(login_request, traffic_annotation, socket_.get());
 }
 
 std::unique_ptr<net::BackoffEntry> ConnectionFactoryImpl::CreateBackoffEntry(
@@ -405,15 +398,8 @@
 }
 
 void ConnectionFactoryImpl::OnConnectDone(int result) {
+  DCHECK_NE(net::ERR_IO_PENDING, result);
   if (result != net::OK) {
-    // If the connection fails, try another proxy.
-    result = ReconsiderProxyAfterError(result);
-    // ReconsiderProxyAfterError either returns an error (in which case it is
-    // not reconsidering a proxy) or returns ERR_IO_PENDING if it is considering
-    // another proxy.
-    DCHECK_NE(result, net::OK);
-    if (result == net::ERR_IO_PENDING)
-      return;  // Proxy reconsideration pending. Return.
     LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result;
     UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", false);
     recorder_->RecordConnectionFailure(result);
@@ -436,9 +422,6 @@
 
   UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", true);
   UMA_HISTOGRAM_COUNTS("GCM.ConnectionEndpoint", next_endpoint_);
-  UMA_HISTOGRAM_BOOLEAN("GCM.ConnectedViaProxy",
-                        !(proxy_info_.is_empty() || proxy_info_.is_direct()));
-  ReportSuccessfulProxyConnection();
   recorder_->RecordConnectionSuccess();
 
   // Reset the endpoint back to the default.
@@ -478,106 +461,15 @@
     listener_->OnConnected(GetCurrentEndpoint(), GetPeerIP());
 }
 
-// This has largely been copied from
-// HttpStreamFactoryImpl::Job::DoResolveProxyComplete. This should be
-// refactored into some common place.
-void ConnectionFactoryImpl::OnProxyResolveDone(int status) {
-  proxy_resolve_request_ = NULL;
-  DVLOG(1) << "Proxy resolution status: " << status;
-
-  DCHECK_NE(status, net::ERR_IO_PENDING);
-  if (status == net::OK) {
-    // Remove unsupported proxies from the list.
-    proxy_info_.RemoveProxiesWithoutScheme(
-        net::ProxyServer::SCHEME_DIRECT |
-        net::ProxyServer::SCHEME_HTTP | net::ProxyServer::SCHEME_HTTPS |
-        net::ProxyServer::SCHEME_SOCKS4 | net::ProxyServer::SCHEME_SOCKS5);
-
-    if (proxy_info_.is_empty()) {
-      // No proxies/direct to choose from. This happens when we don't support
-      // any of the proxies in the returned list.
-      status = net::ERR_NO_SUPPORTED_PROXIES;
-    }
-  }
-
-  if (status != net::OK) {
-    // Failed to resolve proxy. Retry later.
-    OnConnectDone(status);
-    return;
-  }
-
-  DVLOG(1) << "Resolved proxy with PAC:" << proxy_info_.ToPacString();
-
-  net::SSLConfig ssl_config;
-  gcm_network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
-  status = net::InitSocketHandleForTlsConnect(
-      net::HostPortPair::FromURL(GetCurrentEndpoint()),
-      gcm_network_session_,
-      proxy_info_,
-      ssl_config,
-      ssl_config,
-      net::PRIVACY_MODE_DISABLED,
-      net_log_,
-      &socket_handle_,
-      base::Bind(&ConnectionFactoryImpl::OnConnectDone,
-                 weak_ptr_factory_.GetWeakPtr()));
-  if (status != net::ERR_IO_PENDING)
-    OnConnectDone(status);
-}
-
-// This has largely been copied from
-// HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError. This should be
-// refactored into some common place.
-// This method reconsiders the proxy on certain errors. If it does reconsider
-// a proxy it always returns ERR_IO_PENDING and posts a call to
-// OnProxyResolveDone with the result of the reconsideration.
-int ConnectionFactoryImpl::ReconsiderProxyAfterError(int error) {
-  DCHECK(!proxy_resolve_request_);
-  DCHECK_NE(error, net::OK);
-  DCHECK_NE(error, net::ERR_IO_PENDING);
-
-  // Check if the error was a proxy failure.
-  if (!net::CanFalloverToNextProxy(&error))
-    return error;
-
-  net::SSLConfig ssl_config;
-  gcm_network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
-  if (proxy_info_.is_https() && ssl_config.send_client_cert) {
-    gcm_network_session_->ssl_client_auth_cache()->Remove(
-        proxy_info_.proxy_server().host_port_pair());
-  }
-
-  if (!proxy_info_.Fallback(error, net_log_)) {
-    // There was nothing left to fall-back to, so fail the transaction
-    // with the last connection error we got.
-    return error;
-  }
-
-  CloseSocket();
-
-  // If there is new proxy info, post OnProxyResolveDone to retry it.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
-                            weak_ptr_factory_.GetWeakPtr(), net::OK));
-
-  return net::ERR_IO_PENDING;
-}
-
-void ConnectionFactoryImpl::ReportSuccessfulProxyConnection() {
-  if (gcm_network_session_ && gcm_network_session_->proxy_resolution_service())
-    gcm_network_session_->proxy_resolution_service()->ReportSuccess(proxy_info_,
-        NULL);
-}
-
 void ConnectionFactoryImpl::CloseSocket() {
   // The connection handler needs to be reset, else it'll attempt to keep using
   // the destroyed socket.
   if (connection_handler_)
     connection_handler_->Reset();
 
-  if (socket_handle_.socket() && socket_handle_.socket()->IsConnected())
-    socket_handle_.socket()->Disconnect();
-  socket_handle_.Reset();
+  if (socket_)
+    socket_->Disconnect();
+  socket_ = nullptr;
 }
 
 void ConnectionFactoryImpl::UpdateFromHttpNetworkSession() {
diff --git a/google_apis/gcm/engine/connection_factory_impl.h b/google_apis/gcm/engine/connection_factory_impl.h
index 13885460..2b718aa 100644
--- a/google_apis/gcm/engine/connection_factory_impl.h
+++ b/google_apis/gcm/engine/connection_factory_impl.h
@@ -18,11 +18,12 @@
 #include "net/base/backoff_entry.h"
 #include "net/base/network_change_notifier.h"
 #include "net/log/net_log_with_source.h"
-#include "net/proxy_resolution/proxy_info.h"
-#include "net/proxy_resolution/proxy_resolution_service.h"
-#include "net/socket/client_socket_handle.h"
 #include "url/gurl.h"
 
+namespace network {
+class ProxyResolvingClientSocket;
+}
+
 namespace net {
 class HttpNetworkSession;
 class NetLog;
@@ -123,12 +124,6 @@
   // handshake. On connection/handshake failure, goes into backoff.
   void ConnectImpl();
 
-  // Proxy resolution and connection functions.
-  void OnProxyResolveDone(int status);
-  void OnProxyConnectDone(int status);
-  int ReconsiderProxyAfterError(int error);
-  void ReportSuccessfulProxyConnection();
-
   // Closes the local socket if one is present, and resets connection handler.
   void CloseSocket();
 
@@ -157,15 +152,8 @@
   // HTTP Network session. If set, is used for extracting proxy auth
   // credentials. If nullptr, is ignored.
   net::HttpNetworkSession* http_network_session_;
-  // Net log to use in connection attempts.
-  net::NetLogWithSource net_log_;
-  // The current proxy resolution request, if one exists. Owned by the proxy
-  // service.
-  net::ProxyResolutionService::Request* proxy_resolve_request_;
-  // The current proxy info.
-  net::ProxyInfo proxy_info_;
   // The handle to the socket for the current connection, if one exists.
-  net::ClientSocketHandle socket_handle_;
+  std::unique_ptr<network::ProxyResolvingClientSocket> socket_;
   // Current backoff entry.
   std::unique_ptr<net::BackoffEntry> backoff_entry_;
   // Backoff entry from previous connection attempt. Updated on each login
diff --git a/gpu/PRESUBMIT.py b/gpu/PRESUBMIT.py
index 0b04675f..8e1e112b 100644
--- a/gpu/PRESUBMIT.py
+++ b/gpu/PRESUBMIT.py
@@ -24,7 +24,7 @@
     cl,
     [
       'luci.chromium.try:linux_optional_gpu_tests_rel',
-      'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel',
+      'luci.chromium.try:mac_optional_gpu_tests_rel',
       'master.tryserver.chromium.win:win_optional_gpu_tests_rel',
       'master.tryserver.chromium.android:android_optional_gpu_tests_rel',
     ],
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index b04e255f..374093b 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -54,7 +54,6 @@
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/alert_coordinator",
-    "//ios/chrome/browser/ui/collection_view",
     "//ios/chrome/browser/ui/collection_view/cells",
     "//ios/chrome/browser/ui/colors",
     "//ios/chrome/browser/ui/commands",
@@ -70,6 +69,9 @@
     "//ui/gfx",
     "//url",
   ]
+  public_deps = [
+    "//ios/chrome/browser/ui/collection_view",
+  ]
   if (is_chrome_branded) {
     deps += [ "resources:signin_promo_logo_chrome_color" ]
   } else {
@@ -118,6 +120,7 @@
     "account_control_item_unittest.mm",
     "authentication_flow_unittest.mm",
     "authentication_ui_util_unittest.mm",
+    "chrome_signin_view_controller_unittest.mm",
     "re_signin_infobar_delegate_unittest.mm",
     "signed_in_accounts_view_controller_unittest.mm",
     "signin_promo_item_unittest.mm",
@@ -129,19 +132,27 @@
     ":authentication_ui",
     "//base",
     "//base/test:test_support",
+    "//components/consent_auditor",
     "//components/pref_registry",
     "//components/sync_preferences",
     "//components/sync_preferences:test_support",
+    "//components/version_info",
+    "//ios/chrome/app/strings:ios_chromium_strings_grit",
+    "//ios/chrome/app/strings:ios_strings_grit",
+    "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/consent_auditor",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/prefs:browser_prefs",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/signin:test_support",
+    "//ios/chrome/browser/sync",
     "//ios/chrome/browser/ui/colors",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/signin_interaction/public",
     "//ios/chrome/test:test_support",
     "//ios/public/provider/chrome/browser/signin:test_support",
+    "//ios/testing:ios_test_support",
     "//ios/third_party/material_components_ios",
     "//ios/web/public/test",
     "//testing/gtest",
diff --git a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h
index 31038901..6563388e 100644
--- a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h
+++ b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h
@@ -7,8 +7,10 @@
 
 #import <UIKit/UIKit.h>
 
+#include "base/timer/timer.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #import "ios/chrome/browser/signin/constants.h"
+#include "ios/chrome/browser/ui/authentication/signin_confirmation_view_controller.h"
 
 @protocol ApplicationCommands;
 @class ChromeIdentity;
@@ -18,6 +20,9 @@
 class ChromeBrowserState;
 }  // namespace ios
 
+using TimerGeneratorBlock =
+    std::unique_ptr<base::Timer> (^)(bool retain_user_task, bool is_repeating);
+
 @protocol ChromeSigninViewControllerDelegate<NSObject>
 
 // Informs the delegate that a sign-in operation will start in |controller|.
@@ -59,7 +64,8 @@
 
 // ChromeSigninViewController is a view controller that handles all the
 // sign-in UI flow.
-@interface ChromeSigninViewController : UIViewController
+@interface ChromeSigninViewController
+    : UIViewController<SigninConfirmationViewControllerDelegate>
 
 @property(nonatomic, weak) id<ChromeSigninViewControllerDelegate> delegate;
 
@@ -69,6 +75,9 @@
 
 @property(nonatomic, weak, readonly) id<ApplicationCommands> dispatcher;
 
+// Sign-in conformation view controller.
+@property(nonatomic, readonly) SigninConfirmationViewController* confirmationVC;
+
 // Designated initializer.
 // * |browserState| is the current browser state.
 // * |accessPoint| represents the access point that initiated the sign-in.
@@ -107,4 +116,12 @@
 
 @end
 
+// Exposes methods for testing.
+@interface ChromeSigninViewController (Testing)
+
+// Timer generator. Should stay nil to use the default timer class: base::Timer.
+@property(nonatomic, copy) TimerGeneratorBlock timerGenerator;
+
+@end
+
 #endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_CHROME_SIGNIN_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm
index ad75236..39f2238 100644
--- a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm
@@ -17,7 +17,6 @@
 #include "base/metrics/user_metrics.h"
 #import "base/strings/sys_string_conversions.h"
 #include "base/timer/elapsed_timer.h"
-#include "base/timer/timer.h"
 #include "components/consent_auditor/consent_auditor.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #include "components/strings/grit/components_strings.h"
@@ -118,7 +117,6 @@
     ChromeIdentityInteractionManagerDelegate,
     ChromeIdentityServiceObserver,
     SigninAccountSelectorViewControllerDelegate,
-    SigninConfirmationViewControllerDelegate,
     MDCActivityIndicatorDelegate>
 @property(nonatomic, strong) ChromeIdentity* selectedIdentity;
 
@@ -129,6 +127,7 @@
   __weak id<ChromeSigninViewControllerDelegate> _delegate;
   std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver;
   ChromeIdentity* _selectedIdentity;
+  TimerGeneratorBlock _timerGenerator;
 
   // Authentication
   AlertCoordinator* _alertCoordinator;
@@ -167,6 +166,7 @@
 @synthesize delegate = _delegate;
 @synthesize shouldClearData = _shouldClearData;
 @synthesize dispatcher = _dispatcher;
+@synthesize confirmationVC = _confirmationVC;
 
 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
                          accessPoint:(signin_metrics::AccessPoint)accessPoint
@@ -603,7 +603,16 @@
       [strongSelf->_activityIndicator stopAnimating];
       strongSelf->_leavingPendingStateTimer.reset();
     };
-    _leavingPendingStateTimer.reset(new base::Timer(false, false));
+    const bool retain_user_task = false;
+    const bool is_repeating = false;
+    if (self.timerGenerator) {
+      _leavingPendingStateTimer =
+          self.timerGenerator(retain_user_task, is_repeating);
+      DCHECK(_leavingPendingStateTimer);
+    } else {
+      _leavingPendingStateTimer =
+          std::make_unique<base::Timer>(retain_user_task, is_repeating);
+    }
     _leavingPendingStateTimer->Start(FROM_HERE, remainingTime,
                                      base::BindBlockArc(completionBlock));
   }
@@ -963,3 +972,15 @@
 }
 
 @end
+
+@implementation ChromeSigninViewController (Testing)
+
+- (TimerGeneratorBlock)timerGenerator {
+  return _timerGenerator;
+}
+
+- (void)setTimerGenerator:(TimerGeneratorBlock)timerGenerator {
+  _timerGenerator = [timerGenerator copy];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm
new file mode 100644
index 0000000..50c43226
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller_unittest.mm
@@ -0,0 +1,283 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h"
+
+#include "base/memory/ptr_util.h"
+#import "base/test/ios/wait_util.h"
+#include "base/timer/mock_timer.h"
+#include "components/consent_auditor/consent_auditor.h"
+#include "components/version_info/version_info.h"
+#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/consent_auditor/consent_auditor_factory.h"
+#import "ios/chrome/browser/signin/authentication_service_factory.h"
+#import "ios/chrome/browser/signin/authentication_service_fake.h"
+#include "ios/chrome/browser/sync/ios_user_event_service_factory.h"
+#include "ios/chrome/grit/ios_chromium_strings.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/public/provider/chrome/browser/signin/fake_chrome_identity.h"
+#import "ios/public/provider/chrome/browser/signin/fake_chrome_identity_service.h"
+#import "ios/testing/wait_util.h"
+#include "ios/web/public/test/test_web_thread_bundle.h"
+#import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "third_party/ocmock/gtest_support.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Fake consent auditor used for the tests.
+class FakeConsentAuditor : public consent_auditor::ConsentAuditor {
+ public:
+  static std::unique_ptr<KeyedService> CreateInstance(
+      web::BrowserState* context) {
+    ios::ChromeBrowserState* ios_context =
+        ios::ChromeBrowserState::FromBrowserState(context);
+    syncer::UserEventService* const user_event_service =
+        IOSUserEventServiceFactory::GetForBrowserState(ios_context);
+    return std::make_unique<FakeConsentAuditor>(
+        ios_context->GetPrefs(), user_event_service,
+        version_info::GetVersionNumber(),
+        GetApplicationContext()->GetApplicationLocale());
+  }
+
+  FakeConsentAuditor(PrefService* pref_service,
+                     syncer::UserEventService* user_event_service,
+                     const std::string& app_version,
+                     const std::string& app_locale)
+      : ConsentAuditor(pref_service,
+                       user_event_service,
+                       app_version,
+                       app_locale) {}
+  ~FakeConsentAuditor() override {}
+
+  void RecordGaiaConsent(consent_auditor::Feature feature,
+                         const std::vector<int>& description_grd_ids,
+                         int confirmation_string_id,
+                         consent_auditor::ConsentStatus status) override {
+    feature_ = feature;
+    recorded_ids_ = description_grd_ids;
+    confirmation_string_id_ = confirmation_string_id;
+    status_ = status;
+  }
+
+  consent_auditor::Feature feature() const { return feature_; }
+  const std::vector<int>& recorded_ids() const { return recorded_ids_; }
+  int confirmation_string_id() const { return confirmation_string_id_; }
+  consent_auditor::ConsentStatus status() const { return status_; }
+
+ private:
+  consent_auditor::Feature feature_;
+  std::vector<int> recorded_ids_;
+  int confirmation_string_id_ = -1;
+  consent_auditor::ConsentStatus status_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeConsentAuditor);
+};
+
+// These tests verify that Chrome correctly records user's consent to Chrome
+// Sync, which is a GDPR requirement. None of those tests should be turned off.
+// If one of those tests fails, one of the following methods should be updated
+// with the added or removed strings:
+//   - ExpectedConsentStringIds()
+//   - WhiteListLocalizedStrings()
+class ChromeSigninViewControllerTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    PlatformTest::SetUp();
+    identity_ = [FakeChromeIdentity identityWithEmail:@"foo1@gmail.com"
+                                               gaiaID:@"foo1ID"
+                                                 name:@"Fake Foo 1"];
+    // Setup services.
+    TestChromeBrowserState::Builder builder;
+    builder.AddTestingFactory(
+        AuthenticationServiceFactory::GetInstance(),
+        AuthenticationServiceFake::CreateAuthenticationService);
+    builder.AddTestingFactory(ConsentAuditorFactory::GetInstance(),
+                              FakeConsentAuditor::CreateInstance);
+    context_ = builder.Build();
+    ios::FakeChromeIdentityService* identity_service =
+        ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
+    identity_service->AddIdentity(identity_);
+    fake_consent_auditor_ = static_cast<FakeConsentAuditor*>(
+        ConsentAuditorFactory::GetForBrowserState(context_.get()));
+    // Setup view controller.
+    vc_ = [[ChromeSigninViewController alloc]
+        initWithBrowserState:context_.get()
+                 accessPoint:signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS
+                 promoAction:signin_metrics::PromoAction::
+                                 PROMO_ACTION_WITH_DEFAULT
+              signInIdentity:identity_
+                  dispatcher:nil];
+    __block base::MockTimer* mock_timer_ptr = nullptr;
+    vc_.timerGenerator = ^std::unique_ptr<base::Timer>(bool retain_user_task,
+                                                       bool is_repeating) {
+      auto mock_timer =
+          std::make_unique<base::MockTimer>(retain_user_task, is_repeating);
+      mock_timer_ptr = mock_timer.get();
+      return mock_timer;
+    };
+    UIScreen* screen = [UIScreen mainScreen];
+    UIWindow* window = [[UIWindow alloc] initWithFrame:screen.bounds];
+    [window makeKeyAndVisible];
+    [window addSubview:[vc_ view]];
+    ASSERT_TRUE(mock_timer_ptr);
+    mock_timer_ptr->Fire();
+    window_ = window;
+  }
+
+  // Adds in |string_set|, all the strings displayed by |view| and its subviews,
+  // recursively.
+  static void AddStringsFromView(NSMutableSet<NSString*>* string_set,
+                                 UIView* view) {
+    for (UIView* subview in view.subviews)
+      AddStringsFromView(string_set, subview);
+    if ([view isKindOfClass:[UIButton class]]) {
+      UIButton* button = static_cast<UIButton*>(view);
+      if (button.currentTitle)
+        [string_set addObject:button.currentTitle];
+    } else if ([view isKindOfClass:[UILabel class]]) {
+      UILabel* label = static_cast<UILabel*>(view);
+      if (label.text)
+        [string_set addObject:label.text];
+    } else {
+      NSString* view_name = NSStringFromClass([view class]);
+      // Views that don't display strings.
+      NSArray* other_views = @[
+        @"AccountControlCell", @"CollectionViewFooterCell", @"UIButtonLabel",
+        @"UICollectionView", @"UICollectionViewControllerWrapperView",
+        @"UIImageView", @"UIView", @"MDCActivityIndicator", @"MDCButtonBar",
+        @"MDCFlexibleHeaderView", @"MDCHeaderStackView", @"MDCInkView",
+        @"MDCNavigationBar"
+      ];
+      // If this test fails, the unknown class should be added in other_views if
+      // it doesn't display any strings, otherwise the strings diplay by this
+      // class should be added in string_set.
+      EXPECT_TRUE([other_views containsObject:view_name]);
+    }
+  }
+
+  // Returns the set of strings displayed on the screen based on the views
+  // displayed by the .
+  NSSet<NSString*>* LocalizedStringOnScreen() const {
+    NSMutableSet* string_set = [NSMutableSet set];
+    AddStringsFromView(string_set, vc_.view);
+    return string_set;
+  }
+
+  // Returns a localized string based on the string id.
+  NSString* LocalizedStringFromID(int string_id) const {
+    NSString* string = l10n_util::GetNSString(string_id);
+    string = [string stringByReplacingOccurrencesOfString:@"BEGIN_LINK"
+                                               withString:@""];
+    string = [string stringByReplacingOccurrencesOfString:@"END_LINK"
+                                               withString:@""];
+    return string;
+  }
+
+  // Returns all the strings on screen that should be part of the user consent
+  // and part of the white list strings.
+  NSSet<NSString*>* LocalizedExpectedStringsOnScreen() const {
+    const std::vector<int>& string_ids = ExpectedConsentStringIds();
+    NSMutableSet<NSString*>* set = [NSMutableSet set];
+    for (auto it = string_ids.begin(); it != string_ids.end(); ++it) {
+      [set addObject:LocalizedStringFromID(*it)];
+    }
+    [set unionSet:WhiteListLocalizedStrings()];
+    return set;
+  }
+
+  // Returns the list of string id that should be given to RecordGaiaConsent()
+  // then the consent is given. The list is ordered according to the position
+  // on the screen.
+  const std::vector<int> ExpectedConsentStringIds() const {
+    return {
+        IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SYNC_TITLE,
+        IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SYNC_DESCRIPTION,
+        IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SERVICES_TITLE,
+        IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SERVICES_DESCRIPTION,
+        IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_OPEN_SETTINGS,
+    };
+  }
+
+  // Returns the white list of strings that can be displayed on screen but
+  // should not be part of ExpectedConsentStringIds().
+  NSSet<NSString*>* WhiteListLocalizedStrings() const {
+    return [NSSet setWithObjects:@"Hi, Fake Foo 1", @"foo1@gmail.com",
+                                 @"OK, GOT IT", @"UNDO", nil];
+  }
+
+  // Waits until all expected strings are on the screen.
+  void WaitAndExpectAllStringsOnScreen() {
+    ConditionBlock condition = ^bool() {
+      return [LocalizedStringOnScreen()
+          isEqual:LocalizedExpectedStringsOnScreen()];
+    };
+    EXPECT_TRUE(testing::WaitUntilConditionOrTimeout(10, condition));
+  }
+
+  web::TestWebThreadBundle thread_bundle_;
+  std::unique_ptr<TestChromeBrowserState> context_;
+  FakeChromeIdentity* identity_;
+  UIWindow* window_;
+  ChromeSigninViewController* vc_;
+  FakeConsentAuditor* fake_consent_auditor_;
+};
+
+// Tests that all strings on the screen are either part of the consent string
+// list defined in FakeConsentAuditor::ExpectedConsentStringIds()), or are part
+// of the white list strings defined in
+// FakeConsentAuditor::WhiteListLocalizedStrings().
+TEST_F(ChromeSigninViewControllerTest, TestAllStrings) {
+  WaitAndExpectAllStringsOnScreen();
+}
+
+// Tests when the user taps on "OK GOT IT", that RecordGaiaConsent() is called
+// with the expected list of string ids, and
+// IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_OK_BUTTON for the confirmation grd
+// id.
+TEST_F(ChromeSigninViewControllerTest, TestConsentWithOKGOTIT) {
+  WaitAndExpectAllStringsOnScreen();
+  [vc_.primaryButton sendActionsForControlEvents:UIControlEventTouchUpInside];
+  const std::vector<int>& recorded_ids = fake_consent_auditor_->recorded_ids();
+  EXPECT_EQ(ExpectedConsentStringIds(), recorded_ids);
+  EXPECT_EQ(IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_OK_BUTTON,
+            fake_consent_auditor_->confirmation_string_id());
+  EXPECT_EQ(consent_auditor::ConsentStatus::GIVEN,
+            fake_consent_auditor_->status());
+  EXPECT_EQ(consent_auditor::Feature::CHROME_SYNC,
+            fake_consent_auditor_->feature());
+}
+
+// Tests that RecordGaiaConsent() is not called when the user taps on UNDO.
+TEST_F(ChromeSigninViewControllerTest, TestRefusingConsent) {
+  WaitAndExpectAllStringsOnScreen();
+  [vc_.secondaryButton sendActionsForControlEvents:UIControlEventTouchUpInside];
+  const std::vector<int>& recorded_ids = fake_consent_auditor_->recorded_ids();
+  EXPECT_EQ(0ul, recorded_ids.size());
+  EXPECT_EQ(-1, fake_consent_auditor_->confirmation_string_id());
+}
+
+// Tests that RecordGaiaConsent() is called with the expected list of string
+// ids, and IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_OPEN_SETTINGS for the
+// confirmation grd id.
+TEST_F(ChromeSigninViewControllerTest, TestConsentWithSettings) {
+  WaitAndExpectAllStringsOnScreen();
+  [vc_ signinConfirmationControllerDidTapSettingsLink:vc_.confirmationVC];
+  const std::vector<int>& recorded_ids = fake_consent_auditor_->recorded_ids();
+  EXPECT_EQ(ExpectedConsentStringIds(), recorded_ids);
+  EXPECT_EQ(IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_OPEN_SETTINGS,
+            fake_consent_auditor_->confirmation_string_id());
+  EXPECT_EQ(consent_auditor::ConsentStatus::GIVEN,
+            fake_consent_auditor_->status());
+  EXPECT_EQ(consent_auditor::Feature::CHROME_SYNC,
+            fake_consent_auditor_->feature());
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/ui/download/download_manager_coordinator.mm b/ios/chrome/browser/ui/download/download_manager_coordinator.mm
index e3a0478..301eade 100644
--- a/ios/chrome/browser/ui/download/download_manager_coordinator.mm
+++ b/ios/chrome/browser/ui/download/download_manager_coordinator.mm
@@ -22,6 +22,7 @@
 #import "ios/chrome/browser/ui/presenters/contained_presenter_delegate.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/web/public/download/download_task.h"
+#include "net/base/net_errors.h"
 #include "net/url_request/url_fetcher_response_writer.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -188,6 +189,9 @@
 
 - (void)downloadManagerViewControllerDidStartDownload:
     (DownloadManagerViewController*)controller {
+  if (_downloadTask->GetErrorCode() != net::OK) {
+    base::RecordAction(base::UserMetricsAction("MobileDownloadRetryDownload"));
+  }
   _mediator.StartDowloading();
 }
 
diff --git a/ios/chrome/browser/ui/download/download_manager_coordinator_unittest.mm b/ios/chrome/browser/ui/download/download_manager_coordinator_unittest.mm
index ff07aa9..3b735d0 100644
--- a/ios/chrome/browser/ui/download/download_manager_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/download/download_manager_coordinator_unittest.mm
@@ -24,6 +24,7 @@
 #import "ios/web/public/test/fakes/fake_download_task.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
+#include "net/base/net_errors.h"
 #include "net/url_request/url_fetcher_response_writer.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
@@ -491,6 +492,38 @@
   base::FilePath download_dir;
   ASSERT_TRUE(GetDownloadsDirectory(&download_dir));
   EXPECT_TRUE(download_dir.IsParent(file));
+
+  ASSERT_EQ(0,
+            user_action_tester_.GetActionCount("MobileDownloadRetryDownload"));
+}
+
+// Tests retrying the download. Verifies that kDownloadManagerRetryDownload UMA
+// metric is logged.
+TEST_F(DownloadManagerCoordinatorTest, RetryingDownload) {
+  web::FakeDownloadTask task(GURL(kTestUrl), kTestMimeType);
+  task.SetSuggestedFilename(base::SysNSStringToUTF16(kTestSuggestedFileName));
+  task.SetErrorCode(net::ERR_INTERNET_DISCONNECTED);
+  web::DownloadTask* task_ptr = &task;
+  coordinator_.downloadTask = &task;
+  [coordinator_ start];
+
+  DownloadManagerViewController* viewController =
+      base_view_controller_.childViewControllers.firstObject;
+  ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
+  @autoreleasepool {
+    // This call will retain coordinator, which should outlive thread bundle.
+    [viewController.delegate
+        downloadManagerViewControllerDidStartDownload:viewController];
+  }
+
+  // Starting download is async for model.
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(testing::kWaitForDownloadTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    return task_ptr->GetState() == web::DownloadTask::State::kInProgress;
+  }));
+
+  ASSERT_EQ(1,
+            user_action_tester_.GetActionCount("MobileDownloadRetryDownload"));
 }
 
 // Tests that viewController returns correct view controller if coordinator is
diff --git a/ios/chrome/browser/ui/first_run/BUILD.gn b/ios/chrome/browser/ui/first_run/BUILD.gn
index 1971620..2424abaf 100644
--- a/ios/chrome/browser/ui/first_run/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/BUILD.gn
@@ -99,6 +99,7 @@
     "//ios/chrome/browser/geolocation:test_support",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/sync",
+    "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/authentication:eg_test_support",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_view_controller.mm
index e24083c4..35317cb 100644
--- a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_table_view_controller.mm
@@ -74,7 +74,7 @@
 // Key for saving whether the Recently Closed section is collapsed.
 NSString* const kRecentlyClosedCollapsedKey = @"RecentlyClosedCollapsed";
 // There are 2 static sections before the first SessionSection.
-int const kNumberOfSectionsBeforeSessions = 2;
+int const kNumberOfSectionsBeforeSessions = 1;
 // Estimated Table Row height.
 const CGFloat kEstimatedRowHeight = 56;
 // The UI displays relative time for up to this number of hours and then
@@ -160,14 +160,11 @@
   [super loadModel];
   [self addRecentlyClosedSection];
 
-  // The other Devices header section is always present regardless if it has any
-  // items or not.
-  [self addOtherDevicesSectionHeader];
   if (self.sessionState ==
       SessionsSyncUserState::USER_SIGNED_IN_SYNC_ON_WITH_SESSIONS) {
     [self addSessionSections];
   } else {
-    [self addOtherDevicesItemForState:self.sessionState];
+    [self addOtherDevicesSectionForState:self.sessionState];
   }
 }
 
@@ -257,14 +254,11 @@
   SessionsSyncUserState previousState = self.sessionState;
   if (previousState !=
       SessionsSyncUserState::USER_SIGNED_IN_SYNC_ON_WITH_SESSIONS) {
-    // The previous state was one of the OtherDevices states, remove it to clean
-    // it up and re-add just the header.
+    // The previous state was one of the OtherDevices states, remove it.
+    [self.tableView deleteSections:[self otherDevicesSectionIndexSet]
+                  withRowAnimation:UITableViewRowAnimationNone];
     [self.tableViewModel
         removeSectionWithIdentifier:SectionIdentifierOtherDevices];
-    [self addOtherDevicesSectionHeader];
-    // Reload the TableViews section.
-    [self.tableView reloadSections:[self otherDevicesSectionIndexSet]
-                  withRowAnimation:UITableViewRowAnimationNone];
   }
 
   // Clean up any previously added SessionSections.
@@ -349,6 +343,8 @@
 // called inside a [UITableView beginUpdates] block on iOS10, or
 // performBatchUpdates on iOS11+.
 - (void)updateOtherDevicesSectionForState:(SessionsSyncUserState)newState {
+  DCHECK(newState !=
+         SessionsSyncUserState::USER_SIGNED_IN_SYNC_ON_WITH_SESSIONS);
   TableViewModel* model = self.tableViewModel;
   SessionsSyncUserState previousState = self.sessionState;
   switch (previousState) {
@@ -357,25 +353,22 @@
     case SessionsSyncUserState::USER_SIGNED_OUT:
     case SessionsSyncUserState::USER_SIGNED_IN_SYNC_IN_PROGRESS:
       // The OtherDevices section will be updated, remove it in order to clean
-      // it up. After the clean up add just the header.
+      // it up.
       [model removeSectionWithIdentifier:SectionIdentifierOtherDevices];
-      [self addOtherDevicesSectionHeader];
       break;
     case SessionsSyncUserState::USER_SIGNED_IN_SYNC_ON_WITH_SESSIONS:
       // The Sessions Section exists, remove it.
       [self removeSessionSections];
       break;
   }
-  // Add updated OtherDevices Item.
-  [self addOtherDevicesItemForState:newState];
-
-  // Update OtherDevices TableView Section.
+  // Add an updated OtherDevices Section and then reload the TableView section.
+  [self addOtherDevicesSectionForState:newState];
   [self.tableView reloadSections:[self otherDevicesSectionIndexSet]
                 withRowAnimation:UITableViewRowAnimationNone];
 }
 
 // Adds Other Devices Section and its header.
-- (void)addOtherDevicesSectionHeader {
+- (void)addOtherDevicesSectionForState:(SessionsSyncUserState)state {
   TableViewModel* model = self.tableViewModel;
   [model addSectionWithIdentifier:SectionIdentifierOtherDevices];
   [model setSectionIdentifier:SectionIdentifierOtherDevices
@@ -385,11 +378,8 @@
   header.text = l10n_util::GetNSString(IDS_IOS_RECENT_TABS_OTHER_DEVICES);
   [model setHeader:header
       forSectionWithIdentifier:SectionIdentifierOtherDevices];
-}
 
-// Adds Other Devices item for |state|.
-- (void)addOtherDevicesItemForState:(SessionsSyncUserState)state {
-  // Add item depending on |state|.
+  // Adds Other Devices item for |state|.
   TableViewTextItem* dummyCell = nil;
   switch (state) {
     case SessionsSyncUserState::USER_SIGNED_IN_SYNC_ON_WITH_SESSIONS:
@@ -666,6 +656,9 @@
 }
 
 - (synced_sessions::DistantSession const*)sessionForSection:(NSInteger)section {
+  NSInteger sectionIdentifer =
+      [self.tableViewModel sectionIdentifierForSection:section];
+  DCHECK([self isSessionSectionIdentifier:sectionIdentifer]);
   return _syncedSessions->GetSession(section - kNumberOfSectionsBeforeSessions);
 }
 
@@ -893,15 +886,11 @@
       syncService->GetOpenTabsUIDelegate();
 
   [self.tableViewModel removeSectionWithIdentifier:sectionIdentifier];
-  _syncedSessions->EraseSession(sectionIdentifier -
-                                kFirstSessionSectionIdentifier);
+  _syncedSessions->EraseSession(section - kNumberOfSectionsBeforeSessions);
 
   void (^tableUpdates)(void) = ^{
-    [self.tableView
-          deleteSections:[NSIndexSet
-                             indexSetWithIndex:sectionIdentifier -
-                                               kSectionIdentifierEnumZero]
-        withRowAnimation:UITableViewRowAnimationLeft];
+    [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:section]
+                  withRowAnimation:UITableViewRowAnimationLeft];
   };
 
   // If iOS11+ use performBatchUpdates: instead of beginUpdates/endUpdates.
diff --git a/ios/chrome/browser/ui/promos/BUILD.gn b/ios/chrome/browser/ui/promos/BUILD.gn
index 7b12027a..a6dfa0f 100644
--- a/ios/chrome/browser/ui/promos/BUILD.gn
+++ b/ios/chrome/browser/ui/promos/BUILD.gn
@@ -17,12 +17,14 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/signin",
-    "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/commands",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/signin",
     "//net",
   ]
+  public_deps = [
+    "//ios/chrome/browser/ui/authentication",
+  ]
   libs = [ "UIKit.framework" ]
 }
 
diff --git a/ios/chrome/browser/ui/tab_grid/BUILD.gn b/ios/chrome/browser/ui/tab_grid/BUILD.gn
index 01a0f42..0b52790da 100644
--- a/ios/chrome/browser/ui/tab_grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_grid/BUILD.gn
@@ -18,6 +18,7 @@
 
   deps = [
     ":tab_grid_ui",
+    "grid:grid_ui",
     "//base",
     "//components/favicon/ios",
     "//ios/chrome/browser",
@@ -37,20 +38,6 @@
 
 source_set("tab_grid_ui") {
   sources = [
-    "grid_cell.h",
-    "grid_cell.mm",
-    "grid_commands.h",
-    "grid_constants.h",
-    "grid_constants.mm",
-    "grid_consumer.h",
-    "grid_image_data_source.h",
-    "grid_item.h",
-    "grid_item.mm",
-    "grid_layout.h",
-    "grid_layout.mm",
-    "grid_theme.h",
-    "grid_view_controller.h",
-    "grid_view_controller.mm",
     "tab_grid_bottom_toolbar.h",
     "tab_grid_bottom_toolbar.mm",
     "tab_grid_constants.h",
@@ -68,18 +55,17 @@
     "tab_grid_transition_handler.mm",
     "tab_grid_view_controller.h",
     "tab_grid_view_controller.mm",
-    "top_aligned_image_view.h",
-    "top_aligned_image_view.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
 
   deps = [
-    "resources:grid_cell_close_button",
+    "grid:grid_ui",
     "resources:new_tab_floating_button",
     "resources:new_tab_floating_button_incognito",
     "resources:new_tab_toolbar_button",
     "resources:new_tab_toolbar_button_incognito",
+    "resources:page_control_assets",
     "//base",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
@@ -100,6 +86,7 @@
   deps = [
     ":tab_grid",
     ":tab_grid_ui",
+    "grid:grid_ui",
     "//base",
     "//base/test:test_support",
     "//ios/chrome/browser",
@@ -127,6 +114,7 @@
 
   deps = [
     ":tab_grid_ui",
+    "grid:grid_ui",
     "//base",
     "//components/strings",
     "//ios/chrome/app:app_internal",
diff --git a/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn b/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn
new file mode 100644
index 0000000..cfd8120
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/grid/BUILD.gn
@@ -0,0 +1,39 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//ios/public/provider/chrome/browser/build_config.gni")
+
+source_set("grid_ui") {
+  sources = [
+    "grid_cell.h",
+    "grid_cell.mm",
+    "grid_commands.h",
+    "grid_constants.h",
+    "grid_constants.mm",
+    "grid_consumer.h",
+    "grid_image_data_source.h",
+    "grid_item.h",
+    "grid_item.mm",
+    "grid_layout.h",
+    "grid_layout.mm",
+    "grid_theme.h",
+    "grid_view_controller.h",
+    "grid_view_controller.mm",
+    "top_aligned_image_view.h",
+    "top_aligned_image_view.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    "resources:grid_cell_close_button",
+    "//base",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/ui:ui_util",
+    "//ios/chrome/browser/ui/tab_grid/transitions",
+    "//ios/chrome/browser/ui/util:constraints_ui",
+    "//ui/base",
+  ]
+}
diff --git a/ios/chrome/browser/ui/tab_grid/grid/OWNERS b/ios/chrome/browser/ui/tab_grid/grid/OWNERS
new file mode 100644
index 0000000..b98e1d4
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/grid/OWNERS
@@ -0,0 +1,2 @@
+edchin@chromium.org
+marq@chromium.org
diff --git a/ios/chrome/browser/ui/tab_grid/grid_cell.h b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.h
similarity index 83%
rename from ios/chrome/browser/ui/tab_grid/grid_cell.h
rename to ios/chrome/browser/ui/tab_grid/grid/grid_cell.h
index 9778ed2..02208e72 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_cell.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_CELL_H_
-#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_CELL_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_CELL_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_CELL_H_
 
 #import <UIKit/UIKit.h>
 
-#import "ios/chrome/browser/ui/tab_grid/grid_theme.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_theme.h"
 
 @class GridCell;
 
@@ -37,4 +37,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_CELL_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_CELL_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_cell.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
similarity index 97%
rename from ios/chrome/browser/ui/tab_grid/grid_cell.mm
rename to ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
index 98c02ff4..28462c1 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_cell.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/tab_grid/grid_cell.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_cell.h"
 
 #import "base/logging.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_constants.h"
-#import "ios/chrome/browser/ui/tab_grid/top_aligned_image_view.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_constants.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/top_aligned_image_view.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/ios/chrome/browser/ui/tab_grid/grid_commands.h b/ios/chrome/browser/ui/tab_grid/grid/grid_commands.h
similarity index 84%
rename from ios/chrome/browser/ui/tab_grid/grid_commands.h
rename to ios/chrome/browser/ui/tab_grid/grid/grid_commands.h
index 02db60f5..56416df 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_commands.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_commands.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_COMMANDS_H_
-#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_COMMANDS_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_COMMANDS_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_COMMANDS_H_
 
 // Commands issued to a model backing a grid UI.
 @protocol GridCommands
@@ -22,4 +22,4 @@
 - (void)closeAllItems;
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_COMMANDS_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_constants.h b/ios/chrome/browser/ui/tab_grid/grid/grid_constants.h
similarity index 94%
rename from ios/chrome/browser/ui/tab_grid/grid_constants.h
rename to ios/chrome/browser/ui/tab_grid/grid/grid_constants.h
index 39c54d5..824bae8 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_constants.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_constants.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_CONSTANTS_H_
-#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_CONSTANTS_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_CONSTANTS_H_
 
 #import <UIKit/UIKit.h>
 
@@ -77,4 +77,4 @@
 extern const CGFloat kGridCellSelectionRingGapWidth;
 extern const CGFloat kGridCellSelectionRingTintWidth;
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_CONSTANTS_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_constants.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_constants.mm
similarity index 97%
rename from ios/chrome/browser/ui/tab_grid/grid_constants.mm
rename to ios/chrome/browser/ui/tab_grid/grid/grid_constants.mm
index 38091bc..9b27224 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_constants.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_constants.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/tab_grid/grid_constants.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_constants.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/tab_grid/grid_consumer.h b/ios/chrome/browser/ui/tab_grid/grid/grid_consumer.h
similarity index 91%
rename from ios/chrome/browser/ui/tab_grid/grid_consumer.h
rename to ios/chrome/browser/ui/tab_grid/grid/grid_consumer.h
index 4a9cc15..595c4ad 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_consumer.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_consumer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_CONSUMER_H_
-#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_CONSUMER_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_CONSUMER_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_CONSUMER_H_
 
 @class GridItem;
 
@@ -48,4 +48,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_CONSUMER_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_CONSUMER_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_image_data_source.h b/ios/chrome/browser/ui/tab_grid/grid/grid_image_data_source.h
similarity index 79%
rename from ios/chrome/browser/ui/tab_grid/grid_image_data_source.h
rename to ios/chrome/browser/ui/tab_grid/grid/grid_image_data_source.h
index 4b0516a2..0686309 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_image_data_source.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_image_data_source.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_IMAGE_DATA_SOURCE_H_
-#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_IMAGE_DATA_SOURCE_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_IMAGE_DATA_SOURCE_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_IMAGE_DATA_SOURCE_H_
 
 #import <UIKit/UIKit.h>
 
@@ -20,4 +20,4 @@
                   completion:(void (^)(UIImage*))completion;
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_IMAGE_DATA_SOURCE_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_IMAGE_DATA_SOURCE_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_item.h b/ios/chrome/browser/ui/tab_grid/grid/grid_item.h
similarity index 68%
rename from ios/chrome/browser/ui/tab_grid/grid_item.h
rename to ios/chrome/browser/ui/tab_grid/grid/grid_item.h
index 2a839e2..de68348f 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_item.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_item.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_ITEM_H_
-#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_ITEM_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_ITEM_H_
 
 #import <Foundation/Foundation.h>
 
@@ -13,4 +13,4 @@
 @property(nonatomic, copy) NSString* title;
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_ITEM_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_ITEM_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_item.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_item.mm
similarity index 86%
rename from ios/chrome/browser/ui/tab_grid/grid_item.mm
rename to ios/chrome/browser/ui/tab_grid/grid/grid_item.mm
index afc827a..2559d053 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_item.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_item.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/tab_grid/grid_item.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_item.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/tab_grid/grid_layout.h b/ios/chrome/browser/ui/tab_grid/grid/grid_layout.h
similarity index 70%
rename from ios/chrome/browser/ui/tab_grid/grid_layout.h
rename to ios/chrome/browser/ui/tab_grid/grid/grid_layout.h
index 8a1b478..d851ae6 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_layout.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_layout.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_LAYOUT_H_
-#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_LAYOUT_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_LAYOUT_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_LAYOUT_H_
 
 #import <UIKit/UIKit.h>
 
@@ -13,4 +13,4 @@
 @interface GridLayout : UICollectionViewFlowLayout
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_LAYOUT_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_LAYOUT_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_layout.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_layout.mm
similarity index 96%
rename from ios/chrome/browser/ui/tab_grid/grid_layout.mm
rename to ios/chrome/browser/ui/tab_grid/grid/grid_layout.mm
index 81460b5f..c889be4 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_layout.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_layout.mm
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/tab_grid/grid_layout.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_layout.h"
 
-#import "ios/chrome/browser/ui/tab_grid/grid_constants.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_constants.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/tab_grid/grid_theme.h b/ios/chrome/browser/ui/tab_grid/grid/grid_theme.h
similarity index 62%
rename from ios/chrome/browser/ui/tab_grid/grid_theme.h
rename to ios/chrome/browser/ui/tab_grid/grid/grid_theme.h
index df91777..eec1323 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_theme.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_theme.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_THEME_H_
-#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_THEME_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_THEME_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_THEME_H_
 
 // Theme describing the look of the grid.
 typedef NS_ENUM(NSUInteger, GridTheme) {
@@ -11,4 +11,4 @@
   GridThemeDark,
 };
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_THEME_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_THEME_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_view_controller.h b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.h
similarity index 85%
rename from ios/chrome/browser/ui/tab_grid/grid_view_controller.h
rename to ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.h
index 6523f6d..bc0095f 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_view_controller.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_VIEW_CONTROLLER_H_
 
 #import <UIKit/UIKit.h>
 
-#import "ios/chrome/browser/ui/tab_grid/grid_consumer.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_theme.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_consumer.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_theme.h"
 
 @protocol GridImageDataSource;
 @class GridTransitionLayout;
@@ -53,4 +53,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/tab_grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
similarity index 96%
rename from ios/chrome/browser/ui/tab_grid/grid_view_controller.mm
rename to ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
index be1a5e6..1c266e31 100644
--- a/ios/chrome/browser/ui/tab_grid/grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.mm
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/tab_grid/grid_view_controller.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.h"
 
 #import "base/mac/foundation_util.h"
 #import "base/numerics/safe_conversions.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_cell.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_constants.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_image_data_source.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_item.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_layout.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_cell.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_constants.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_image_data_source.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_item.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_layout.h"
 #import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 
diff --git a/ios/chrome/browser/ui/tab_grid/grid/resources/BUILD.gn b/ios/chrome/browser/ui/tab_grid/grid/resources/BUILD.gn
new file mode 100644
index 0000000..2e88b9ff9
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/grid/resources/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ios/asset_catalog.gni")
+
+imageset("grid_cell_close_button") {
+  sources = [
+    "grid_cell_close_button.imageset/Contents.json",
+    "grid_cell_close_button.imageset/grid_cell_close_button.png",
+    "grid_cell_close_button.imageset/grid_cell_close_button@2x.png",
+    "grid_cell_close_button.imageset/grid_cell_close_button@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/tab_grid/resources/grid_cell_close_button.imageset/Contents.json b/ios/chrome/browser/ui/tab_grid/grid/resources/grid_cell_close_button.imageset/Contents.json
similarity index 100%
rename from ios/chrome/browser/ui/tab_grid/resources/grid_cell_close_button.imageset/Contents.json
rename to ios/chrome/browser/ui/tab_grid/grid/resources/grid_cell_close_button.imageset/Contents.json
diff --git a/ios/chrome/browser/ui/tab_grid/resources/grid_cell_close_button.imageset/grid_cell_close_button.png b/ios/chrome/browser/ui/tab_grid/grid/resources/grid_cell_close_button.imageset/grid_cell_close_button.png
similarity index 100%
rename from ios/chrome/browser/ui/tab_grid/resources/grid_cell_close_button.imageset/grid_cell_close_button.png
rename to ios/chrome/browser/ui/tab_grid/grid/resources/grid_cell_close_button.imageset/grid_cell_close_button.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/grid_cell_close_button.imageset/grid_cell_close_button@2x.png b/ios/chrome/browser/ui/tab_grid/grid/resources/grid_cell_close_button.imageset/grid_cell_close_button@2x.png
similarity index 100%
rename from ios/chrome/browser/ui/tab_grid/resources/grid_cell_close_button.imageset/grid_cell_close_button@2x.png
rename to ios/chrome/browser/ui/tab_grid/grid/resources/grid_cell_close_button.imageset/grid_cell_close_button@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/grid_cell_close_button.imageset/grid_cell_close_button@3x.png b/ios/chrome/browser/ui/tab_grid/grid/resources/grid_cell_close_button.imageset/grid_cell_close_button@3x.png
similarity index 100%
rename from ios/chrome/browser/ui/tab_grid/resources/grid_cell_close_button.imageset/grid_cell_close_button@3x.png
rename to ios/chrome/browser/ui/tab_grid/grid/resources/grid_cell_close_button.imageset/grid_cell_close_button@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/top_aligned_image_view.h b/ios/chrome/browser/ui/tab_grid/grid/top_aligned_image_view.h
similarity index 75%
rename from ios/chrome/browser/ui/tab_grid/top_aligned_image_view.h
rename to ios/chrome/browser/ui/tab_grid/grid/top_aligned_image_view.h
index 3ba5daf..8997250 100644
--- a/ios/chrome/browser/ui/tab_grid/top_aligned_image_view.h
+++ b/ios/chrome/browser/ui/tab_grid/grid/top_aligned_image_view.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_TOP_ALIGNED_IMAGE_VIEW_H_
-#define IOS_CHROME_BROWSER_UI_TAB_GRID_TOP_ALIGNED_IMAGE_VIEW_H_
+#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_TOP_ALIGNED_IMAGE_VIEW_H_
+#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_TOP_ALIGNED_IMAGE_VIEW_H_
 
 #import <UIKit/UIKit.h>
 
@@ -19,4 +19,4 @@
 - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_TOP_ALIGNED_IMAGE_VIEW_H_
+#endif  // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_TOP_ALIGNED_IMAGE_VIEW_H_
diff --git a/ios/chrome/browser/ui/tab_grid/top_aligned_image_view.mm b/ios/chrome/browser/ui/tab_grid/grid/top_aligned_image_view.mm
similarity index 96%
rename from ios/chrome/browser/ui/tab_grid/top_aligned_image_view.mm
rename to ios/chrome/browser/ui/tab_grid/grid/top_aligned_image_view.mm
index 9745b5b..8f1b919 100644
--- a/ios/chrome/browser/ui/tab_grid/top_aligned_image_view.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/top_aligned_image_view.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/tab_grid/top_aligned_image_view.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/top_aligned_image_view.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/tab_grid/resources/BUILD.gn b/ios/chrome/browser/ui/tab_grid/resources/BUILD.gn
index 7642f48..b05d9520 100644
--- a/ios/chrome/browser/ui/tab_grid/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_grid/resources/BUILD.gn
@@ -4,15 +4,6 @@
 
 import("//build/config/ios/asset_catalog.gni")
 
-imageset("grid_cell_close_button") {
-  sources = [
-    "grid_cell_close_button.imageset/Contents.json",
-    "grid_cell_close_button.imageset/grid_cell_close_button.png",
-    "grid_cell_close_button.imageset/grid_cell_close_button@2x.png",
-    "grid_cell_close_button.imageset/grid_cell_close_button@3x.png",
-  ]
-}
-
 imageset("new_tab_floating_button") {
   sources = [
     "new_tab_floating_button.imageset/Contents.json",
@@ -48,3 +39,68 @@
     "new_tab_toolbar_button_incognito.imageset/new_tab_toolbar_button_incognito@3x.png",
   ]
 }
+
+imageset("page_control_incognito_tabs") {
+  sources = [
+    "page_control_incognito_tabs.imageset/Contents.json",
+    "page_control_incognito_tabs.imageset/page_control_incognito_tabs.png",
+    "page_control_incognito_tabs.imageset/page_control_incognito_tabs@2x.png",
+    "page_control_incognito_tabs.imageset/page_control_incognito_tabs@3x.png",
+  ]
+}
+
+imageset("page_control_incognito_tabs_selected") {
+  sources = [
+    "page_control_incognito_tabs_selected.imageset/Contents.json",
+    "page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected.png",
+    "page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected@2x.png",
+    "page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected@3x.png",
+  ]
+}
+
+imageset("page_control_regular_tabs") {
+  sources = [
+    "page_control_regular_tabs.imageset/Contents.json",
+    "page_control_regular_tabs.imageset/page_control_regular_tabs.png",
+    "page_control_regular_tabs.imageset/page_control_regular_tabs@2x.png",
+    "page_control_regular_tabs.imageset/page_control_regular_tabs@3x.png",
+  ]
+}
+
+imageset("page_control_regular_tabs_selected") {
+  sources = [
+    "page_control_regular_tabs_selected.imageset/Contents.json",
+    "page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected.png",
+    "page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected@2x.png",
+    "page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected@3x.png",
+  ]
+}
+
+imageset("page_control_remote_tabs") {
+  sources = [
+    "page_control_remote_tabs.imageset/Contents.json",
+    "page_control_remote_tabs.imageset/page_control_remote_tabs.png",
+    "page_control_remote_tabs.imageset/page_control_remote_tabs@2x.png",
+    "page_control_remote_tabs.imageset/page_control_remote_tabs@3x.png",
+  ]
+}
+
+imageset("page_control_remote_tabs_selected") {
+  sources = [
+    "page_control_remote_tabs_selected.imageset/Contents.json",
+    "page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected.png",
+    "page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected@2x.png",
+    "page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected@3x.png",
+  ]
+}
+
+group("page_control_assets") {
+  public_deps = [
+    ":page_control_incognito_tabs",
+    ":page_control_incognito_tabs_selected",
+    ":page_control_regular_tabs",
+    ":page_control_regular_tabs_selected",
+    ":page_control_remote_tabs",
+    ":page_control_remote_tabs_selected",
+  ]
+}
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/Contents.json b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/Contents.json
new file mode 100644
index 0000000..71c94c0
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "1x",
+            "filename": "page_control_incognito_tabs.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "page_control_incognito_tabs@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "page_control_incognito_tabs@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/page_control_incognito_tabs.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/page_control_incognito_tabs.png
new file mode 100644
index 0000000..7cf9bcf
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/page_control_incognito_tabs.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/page_control_incognito_tabs@2x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/page_control_incognito_tabs@2x.png
new file mode 100644
index 0000000..6f162bd3
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/page_control_incognito_tabs@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/page_control_incognito_tabs@3x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/page_control_incognito_tabs@3x.png
new file mode 100644
index 0000000..af72534
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs.imageset/page_control_incognito_tabs@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/Contents.json b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/Contents.json
new file mode 100644
index 0000000..e8ebcee
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "1x",
+            "filename": "page_control_incognito_tabs_selected.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "page_control_incognito_tabs_selected@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "page_control_incognito_tabs_selected@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected.png
new file mode 100644
index 0000000..70a81dc
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected@2x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected@2x.png
new file mode 100644
index 0000000..7e27dec
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected@3x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected@3x.png
new file mode 100644
index 0000000..f045ea1
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_incognito_tabs_selected.imageset/page_control_incognito_tabs_selected@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/Contents.json b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/Contents.json
new file mode 100644
index 0000000..78a0ebe
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "1x",
+            "filename": "page_control_regular_tabs.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "page_control_regular_tabs@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "page_control_regular_tabs@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/page_control_regular_tabs.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/page_control_regular_tabs.png
new file mode 100644
index 0000000..847cd45f
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/page_control_regular_tabs.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/page_control_regular_tabs@2x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/page_control_regular_tabs@2x.png
new file mode 100644
index 0000000..0b55cc3
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/page_control_regular_tabs@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/page_control_regular_tabs@3x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/page_control_regular_tabs@3x.png
new file mode 100644
index 0000000..20d6a04
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs.imageset/page_control_regular_tabs@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/Contents.json b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/Contents.json
new file mode 100644
index 0000000..ef60fff0
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "1x",
+            "filename": "page_control_regular_tabs_selected.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "page_control_regular_tabs_selected@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "page_control_regular_tabs_selected@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected.png
new file mode 100644
index 0000000..7b776fa9
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected@2x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected@2x.png
new file mode 100644
index 0000000..b632f46
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected@3x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected@3x.png
new file mode 100644
index 0000000..1122c3b
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_regular_tabs_selected.imageset/page_control_regular_tabs_selected@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/Contents.json b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/Contents.json
new file mode 100644
index 0000000..fcd836c
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "1x",
+            "filename": "page_control_remote_tabs.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "page_control_remote_tabs@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "page_control_remote_tabs@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/page_control_remote_tabs.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/page_control_remote_tabs.png
new file mode 100644
index 0000000..cf4488db
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/page_control_remote_tabs.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/page_control_remote_tabs@2x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/page_control_remote_tabs@2x.png
new file mode 100644
index 0000000..73f042b
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/page_control_remote_tabs@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/page_control_remote_tabs@3x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/page_control_remote_tabs@3x.png
new file mode 100644
index 0000000..4419bde
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs.imageset/page_control_remote_tabs@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/Contents.json b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/Contents.json
new file mode 100644
index 0000000..36bf578e
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "1x",
+            "filename": "page_control_remote_tabs_selected.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "page_control_remote_tabs_selected@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "page_control_remote_tabs_selected@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected.png
new file mode 100644
index 0000000..2002a21
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected@2x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected@2x.png
new file mode 100644
index 0000000..9ed594d
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected@3x.png b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected@3x.png
new file mode 100644
index 0000000..54ddc93
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_grid/resources/page_control_remote_tabs_selected.imageset/page_control_remote_tabs_selected@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm
index 9d8c75f..f6d2fa84 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_egtest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/tab_grid/grid_constants.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_constants.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.h b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.h
index 4b5a372..6e2618d 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.h
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.h
@@ -7,8 +7,8 @@
 
 #import <Foundation/Foundation.h>
 
-#import "ios/chrome/browser/ui/tab_grid/grid_commands.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_image_data_source.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_commands.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_image_data_source.h"
 
 @protocol GridConsumer;
 @class TabModel;
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
index 415ac5c..98dc3fd 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
@@ -13,8 +13,8 @@
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_consumer.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_item.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_consumer.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_item.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
index 219614e1..63882f1 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
@@ -11,9 +11,9 @@
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_commands.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_consumer.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_item.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_commands.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_consumer.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_item.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
 #include "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.h b/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.h
index 8cc28e3..29c106d 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.h
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.h
@@ -33,7 +33,6 @@
 // The numbers that the control should display in the appropriate sections.
 // Numbers less than 1 are not displayed.
 // Numbers greated than 99 are displayed as ':-)'.
-@property(nonatomic, assign) NSUInteger incognitoTabCount;
 @property(nonatomic, assign) NSUInteger regularTabCount;
 
 // Create and return a new instance of this control. This is the preferred way
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm
index 3a883a8c..a886fbb 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm
@@ -29,17 +29,14 @@
 //
 //  * The background view, a grey roundrect with vertical transparent bars.
 //  * The background image views.
-//  * The numeric labels on the incognito and regular tab icons.
+//  * The numeric label on the regular tab icon.
 //  * The "slider" view -- a white roundrect that's taller and wider than each
 //    of the background segments. It clips its subview to its bounds, and it
 //    adjusts its subview's frame so that it (the subview) remains fixed
 //    relative to the background.
 //     * The selected image view, which contains the selected images and labels
 //       and is a subview of the slider.
-//        * The selected images and labels.
-//
-// (Note that currently only labels are used; images will be added once assets
-// are available).
+//        * The selected images and label.
 
 // Notes on layout:
 // This control has an intrinsic size, and generally ignores frame changes. It's
@@ -126,13 +123,15 @@
 @property(nonatomic, weak) UIView* selectedImageView;
 // The labels for the incognito and regular sections, in regular and selected
 // variants.
-@property(nonatomic, weak) UILabel* incognitoLabel;
-@property(nonatomic, weak) UILabel* incognitoSelectedLabel;
+@property(nonatomic, weak) UIView* incognitoIcon;
+@property(nonatomic, weak) UIView* incognitoSelectedIcon;
+@property(nonatomic, weak) UIView* regularIcon;
+@property(nonatomic, weak) UIView* regularSelectedIcon;
 @property(nonatomic, weak) UILabel* regularLabel;
 @property(nonatomic, weak) UILabel* regularSelectedLabel;
-// Temporary labels for the remote tabs section, to be replaced by image assets.
-@property(nonatomic, weak) UILabel* remoteLabel;
-@property(nonatomic, weak) UILabel* remoteSelectedLabel;
+
+@property(nonatomic, weak) UIView* remoteIcon;
+@property(nonatomic, weak) UIView* remoteSelectedIcon;
 // The center point for the slider corresponding to a |sliderPosition| of 0.
 @property(nonatomic) CGFloat sliderOrigin;
 // The (signed) x-coordinate distance the slider moves over. The slider's
@@ -147,7 +146,6 @@
 // Public properties
 @synthesize selectedPage = _selectedPage;
 @synthesize sliderPosition = _sliderPosition;
-@synthesize incognitoTabCount = _incognitoTabCount;
 @synthesize regularTabCount = _regularTabCount;
 // Private properties
 @synthesize incognitoGuide = _incognitoGuide;
@@ -155,12 +153,14 @@
 @synthesize remoteGuide = _remoteGuide;
 @synthesize sliderView = _sliderView;
 @synthesize selectedImageView = _selectedImageView;
-@synthesize incognitoLabel = _incognitoLabel;
-@synthesize incognitoSelectedLabel = _incognitoSelectedLabel;
+@synthesize incognitoIcon = _incognitoIcon;
+@synthesize incognitoSelectedIcon = _incognitoSelectedIconl;
+@synthesize regularIcon = _regularIcon;
+@synthesize regularSelectedIcon = _regularSelectedIcon;
 @synthesize regularLabel = _regularLabel;
 @synthesize regularSelectedLabel = _regularSelectedLabel;
-@synthesize remoteLabel = _remoteLabel;
-@synthesize remoteSelectedLabel = _remoteSelectedLabel;
+@synthesize remoteIcon = _remoteIcon;
+@synthesize remoteSelectedIcon = _remoteSelectedIcon;
 @synthesize sliderOrigin = _sliderOrigin;
 @synthesize sliderRange = _sliderRange;
 
@@ -201,13 +201,6 @@
 // the text in both labels (the regular and the  "selected" versions that's
 // visible when the slider is over a segment), and an ivar to store values that
 // are set before the labels are created.
-- (void)setIncognitoTabCount:(NSUInteger)incognitoTabCount {
-  NSString* incognitoText = StringForItemCount(incognitoTabCount);
-  self.incognitoLabel.text = incognitoText;
-  self.incognitoSelectedLabel.text = incognitoText;
-  _incognitoTabCount = incognitoTabCount;
-}
-
 - (void)setRegularTabCount:(NSUInteger)regularTabCount {
   NSString* regularText = StringForItemCount(regularTabCount);
   self.regularLabel.text = regularText;
@@ -269,17 +262,19 @@
   // guides can be set correctly.
   [super layoutSubviews];
   // Position the section images and labels, which depend on the layout guides.
-  self.incognitoLabel.center = [self centerOfSegment:TabGridPageIncognitoTabs];
-  self.incognitoSelectedLabel.center =
+  self.incognitoIcon.center = [self centerOfSegment:TabGridPageIncognitoTabs];
+  self.incognitoSelectedIcon.center =
       [self centerOfSegment:TabGridPageIncognitoTabs];
 
+  self.regularIcon.center = [self centerOfSegment:TabGridPageRegularTabs];
+  self.regularSelectedIcon.center =
+      [self centerOfSegment:TabGridPageRegularTabs];
   self.regularLabel.center = [self centerOfSegment:TabGridPageRegularTabs];
   self.regularSelectedLabel.center =
       [self centerOfSegment:TabGridPageRegularTabs];
 
-  self.remoteLabel.center = [self centerOfSegment:TabGridPageRemoteTabs];
-  self.remoteSelectedLabel.center =
-      [self centerOfSegment:TabGridPageRemoteTabs];
+  self.remoteIcon.center = [self centerOfSegment:TabGridPageRemoteTabs];
+  self.remoteSelectedIcon.center = [self centerOfSegment:TabGridPageRemoteTabs];
 
   // Determine the slider origin and range; this is based on the layout guides
   // and can't be computed until they are determined.
@@ -338,15 +333,6 @@
         constraintEqualToAnchor:regularGuide.trailingAnchor]
   ]];
 
-  // Create the section images and labels and add them below the slider.
-  UILabel* incognitoLabel = [self labelSelected:NO incognito:YES];
-  [self addSubview:incognitoLabel];
-  self.incognitoLabel = incognitoLabel;
-
-  UILabel* regularLabel = [self labelSelected:NO incognito:NO];
-  [self addSubview:regularLabel];
-  self.regularLabel = regularLabel;
-
   // Add the slider above the section images and labels.
   CGRect sliderFrame = CGRectMake(0, 0, kSliderWidth, kSliderHeight);
   UIView* slider = [[UIView alloc] initWithFrame:sliderFrame];
@@ -357,36 +343,62 @@
   [self addSubview:slider];
   self.sliderView = slider;
 
+  // Selected images and labels are added to the selected image view so they
+  // will be clipped by the slider.
   UIView* selectedImageView = [[UIView alloc]
       initWithFrame:(CGRectMake(0, 0, kOverallWidth, kOverallHeight))];
   [self.sliderView addSubview:selectedImageView];
   self.selectedImageView = selectedImageView;
 
-  // Add the selected images and labels to the selected image view so they
-  // will be clipped by it.
-  UILabel* incognitoSelectedLabel = [self labelSelected:YES incognito:YES];
-  [self.selectedImageView addSubview:incognitoSelectedLabel];
-  self.incognitoSelectedLabel = incognitoSelectedLabel;
+  // Scale the selected images, since the current assets don't have a larger
+  // size for the selected state.
+  // TODO(crbug.com/804500): Remove this once the correct assets are available.
+  CGFloat scale = kSelectedLabelSize / kLabelSize;
+  CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scale, scale);
 
-  UILabel* regularSelectedLabel = [self labelSelected:YES incognito:NO];
+  // Icons and labels for the regular tabs.
+  UIImageView* regularIcon = [[UIImageView alloc]
+      initWithImage:[UIImage imageNamed:@"page_control_regular_tabs"]];
+  [self insertSubview:regularIcon belowSubview:self.sliderView];
+  self.regularIcon = regularIcon;
+  UIImageView* regularSelectedIcon = [[UIImageView alloc]
+      initWithImage:[UIImage imageNamed:@"page_control_regular_tabs_selected"]];
+  regularSelectedIcon.transform = scaleTransform;
+  [self.selectedImageView addSubview:regularSelectedIcon];
+  self.regularSelectedIcon = regularSelectedIcon;
+  UILabel* regularLabel = [self labelSelected:NO];
+  [self insertSubview:regularLabel belowSubview:self.sliderView];
+  self.regularLabel = regularLabel;
+  UILabel* regularSelectedLabel = [self labelSelected:YES];
   [self.selectedImageView addSubview:regularSelectedLabel];
   self.regularSelectedLabel = regularSelectedLabel;
 
-  // Create a temporary label for the remote tabs section.
-  // TODO(crbug.com/804500): Remove this when assets are available.
-  UILabel* remoteLabel = [self labelSelected:NO incognito:NO];
-  [self insertSubview:remoteLabel belowSubview:self.sliderView];
-  self.remoteLabel = remoteLabel;
-  UILabel* remoteSelectedLabel = [self labelSelected:YES incognito:NO];
-  [self.selectedImageView addSubview:remoteSelectedLabel];
-  self.remoteSelectedLabel = remoteSelectedLabel;
-  remoteLabel.text = @"R";
-  remoteSelectedLabel.text = @"R";
+  // Icons for the incognito tabs section.
+  UIImageView* incognitoIcon = [[UIImageView alloc]
+      initWithImage:[UIImage imageNamed:@"page_control_incognito_tabs"]];
+  [self insertSubview:incognitoIcon belowSubview:self.sliderView];
+  self.incognitoIcon = incognitoIcon;
+  UIImageView* incognitoSelectedIcon = [[UIImageView alloc]
+      initWithImage:[UIImage
+                        imageNamed:@"page_control_incognito_tabs_selected"]];
+  incognitoSelectedIcon.transform = scaleTransform;
+  [self.selectedImageView addSubview:incognitoSelectedIcon];
+  self.incognitoSelectedIcon = incognitoSelectedIcon;
+
+  // Icons for the remote tabs section.
+  UIImageView* remoteIcon = [[UIImageView alloc]
+      initWithImage:[UIImage imageNamed:@"page_control_remote_tabs"]];
+  [self insertSubview:remoteIcon belowSubview:self.sliderView];
+  self.remoteIcon = remoteIcon;
+  UIImageView* remoteSelectedIcon = [[UIImageView alloc]
+      initWithImage:[UIImage imageNamed:@"page_control_remote_tabs_selected"]];
+  remoteSelectedIcon.transform = scaleTransform;
+  [self.selectedImageView addSubview:remoteSelectedIcon];
+  self.remoteSelectedIcon = remoteSelectedIcon;
 
   // Update the label text, in case these properties have been set before the
   // views were set up.
   self.regularTabCount = _regularTabCount;
-  self.incognitoTabCount = _incognitoTabCount;
 
   // Mark the control's layout as dirty so the the guides will be computed, then
   // force a layout now so it won't be triggered later (perhaps during an
@@ -403,22 +415,14 @@
 
 // Creates a label for use in this control.
 // Selected labels use a different size and are black.
-// Incognito labels have a solid background and use inverted text.
-- (UILabel*)labelSelected:(BOOL)selected incognito:(BOOL)incognito {
+- (UILabel*)labelSelected:(BOOL)selected {
   CGFloat size = selected ? kSelectedLabelSize : kLabelSize;
-  UIColor* color = selected ? UIColor.blackColor : UIColor.lightGrayColor;
+  UIColor* color = selected ? UIColor.blackColor : UIColorFromRGB(kSliderColor);
   UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, size, size)];
-  label.backgroundColor = incognito ? color : UIColor.clearColor;
-  label.layer.borderWidth = 2.5;
-  label.layer.borderColor = color.CGColor;
-  label.layer.masksToBounds = YES;
-  label.layer.cornerRadius = 6.0;
+  label.backgroundColor = UIColor.clearColor;
   label.textAlignment = NSTextAlignmentCenter;
-  label.textColor = incognito ? (selected ? UIColorFromRGB(kSliderColor)
-                                          : UIColorFromRGB(kBackgroundColor))
-                              : color;
-  label.font =
-      [UIFont systemFontOfSize:size * .6 weight:UIFontWeightBold];  // ?
+  label.textColor = color;
+  label.font = [UIFont systemFontOfSize:size * .6 weight:UIFontWeightBold];
   return label;
 }
 
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
index 5382884..8ccd3bf 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -5,10 +5,10 @@
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h"
 
 #include "base/strings/sys_string_conversions.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_commands.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_consumer.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_image_data_source.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_view_controller.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_commands.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_consumer.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_image_data_source.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_bottom_toolbar.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_constants.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_empty_state_view.h"
@@ -622,8 +622,6 @@
   [self configureButtonsForCurrentPage];
   if (gridViewController == self.regularTabsViewController) {
     self.topToolbar.pageControl.regularTabCount = count;
-  } else if (gridViewController == self.incognitoTabsViewController) {
-    self.topToolbar.pageControl.incognitoTabCount = count;
   }
 }
 
diff --git a/ios/public/provider/chrome/browser/signin/BUILD.gn b/ios/public/provider/chrome/browser/signin/BUILD.gn
index d6d697d..a931d2c 100644
--- a/ios/public/provider/chrome/browser/signin/BUILD.gn
+++ b/ios/public/provider/chrome/browser/signin/BUILD.gn
@@ -40,6 +40,7 @@
     "//base",
     "//google_apis",
     "//ios/public/provider/chrome/browser",
+    "//ui/base:test_support",
   ]
   public_deps = [
     "//testing/gmock",
diff --git a/ios/public/provider/chrome/browser/signin/DEPS b/ios/public/provider/chrome/browser/signin/DEPS
new file mode 100644
index 0000000..eb0f651b
--- /dev/null
+++ b/ios/public/provider/chrome/browser/signin/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "test_signin_resources_provider\.mm": [
+    "+ui/base/test/ios/ui_image_test_utils.h",
+  ],
+}
diff --git a/ios/public/provider/chrome/browser/signin/test_signin_resources_provider.mm b/ios/public/provider/chrome/browser/signin/test_signin_resources_provider.mm
index 2e82643..461cdc6 100644
--- a/ios/public/provider/chrome/browser/signin/test_signin_resources_provider.mm
+++ b/ios/public/provider/chrome/browser/signin/test_signin_resources_provider.mm
@@ -6,12 +6,18 @@
 
 #import <UIKit/UIKit.h>
 
+#include "ui/base/test/ios/ui_image_test_utils.h"
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
 TestSigninResourcesProvider::TestSigninResourcesProvider()
-    : default_avatar_([[UIImage alloc] init]) {}
+    : default_avatar_(ui::test::uiimage_utils::UIImageWithSizeAndSolidColor(
+          CGSizeMake(32, 32),
+          UIColor.whiteColor)) {
+  // A real UIImage for default_avatar_ is required to be cached/resized.
+}
 
 TestSigninResourcesProvider::~TestSigninResourcesProvider() {}
 
diff --git a/ios/showcase/tab_grid/BUILD.gn b/ios/showcase/tab_grid/BUILD.gn
index 8cbb1a6..cae774f 100644
--- a/ios/showcase/tab_grid/BUILD.gn
+++ b/ios/showcase/tab_grid/BUILD.gn
@@ -18,6 +18,7 @@
   deps = [
     "//base",
     "//ios/chrome/browser/ui/tab_grid:tab_grid_ui",
+    "//ios/chrome/browser/ui/tab_grid/grid:grid_ui",
     "//ios/showcase/common",
   ]
   libs = [ "UIKit.framework" ]
diff --git a/ios/showcase/tab_grid/sc_grid_cell_view_controller.mm b/ios/showcase/tab_grid/sc_grid_cell_view_controller.mm
index 899a47d..fd4930e 100644
--- a/ios/showcase/tab_grid/sc_grid_cell_view_controller.mm
+++ b/ios/showcase/tab_grid/sc_grid_cell_view_controller.mm
@@ -6,8 +6,8 @@
 
 #import "base/mac/foundation_util.h"
 #import "base/numerics/safe_conversions.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_cell.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_theme.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_cell.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_theme.h"
 #import "ios/showcase/common/protocol_alerter.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/showcase/tab_grid/sc_grid_coordinator.mm b/ios/showcase/tab_grid/sc_grid_coordinator.mm
index 367151c7..7c38a681 100644
--- a/ios/showcase/tab_grid/sc_grid_coordinator.mm
+++ b/ios/showcase/tab_grid/sc_grid_coordinator.mm
@@ -4,9 +4,9 @@
 
 #import "ios/showcase/tab_grid/sc_grid_coordinator.h"
 
-#import "ios/chrome/browser/ui/tab_grid/grid_image_data_source.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_item.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_view_controller.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_image_data_source.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_item.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_view_controller.h"
 #import "ios/showcase/common/protocol_alerter.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/showcase/tab_grid/sc_image_view_controller.mm b/ios/showcase/tab_grid/sc_image_view_controller.mm
index 1fbf6de..42bc16ad 100644
--- a/ios/showcase/tab_grid/sc_image_view_controller.mm
+++ b/ios/showcase/tab_grid/sc_image_view_controller.mm
@@ -4,7 +4,7 @@
 
 #import "ios/showcase/tab_grid/sc_image_view_controller.h"
 
-#import "ios/chrome/browser/ui/tab_grid/top_aligned_image_view.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/top_aligned_image_view.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm b/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm
index 0690430..bdec3e44 100644
--- a/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm
+++ b/ios/showcase/tab_grid/sc_tab_grid_coordinator.mm
@@ -4,9 +4,9 @@
 
 #import "ios/showcase/tab_grid/sc_tab_grid_coordinator.h"
 
-#import "ios/chrome/browser/ui/tab_grid/grid_commands.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_consumer.h"
-#import "ios/chrome/browser/ui/tab_grid/grid_item.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_commands.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_consumer.h"
+#import "ios/chrome/browser/ui/tab_grid/grid/grid_item.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.h"
 #import "ios/showcase/common/protocol_alerter.h"
 
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm
index ffa82c9..3156c963 100644
--- a/ios/web/web_state/ui/crw_context_menu_controller.mm
+++ b/ios/web/web_state/ui/crw_context_menu_controller.mm
@@ -338,6 +338,9 @@
     [_pendingElementFetchRequests removeObjectForKey:requestID];
     [self logElementFetchDurationWithStartTime:fetchRequest.creationTime];
     [fetchRequest runHandlerWithResponse:response];
+  } else {
+    UMA_HISTOGRAM_BOOLEAN(
+        "ContextMenu.UnexpectedFindElementResultHandlerMessage", true);
   }
 }
 
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc
index cb86bda..e1007508 100644
--- a/ipc/ipc_mojo_bootstrap.cc
+++ b/ipc/ipc_mojo_bootstrap.cc
@@ -4,10 +4,12 @@
 
 #include "ipc/ipc_mojo_bootstrap.h"
 
+#include <inttypes.h>
 #include <stdint.h>
 
 #include <map>
 #include <memory>
+#include <set>
 #include <utility>
 #include <vector>
 
@@ -16,10 +18,15 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
 #include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_dump_provider.h"
 #include "mojo/public/cpp/bindings/associated_group.h"
 #include "mojo/public/cpp/bindings/associated_group_controller.h"
 #include "mojo/public/cpp/bindings/connector.h"
@@ -37,6 +44,50 @@
 
 namespace {
 
+class ChannelAssociatedGroupController;
+
+// Used to track some internal Channel state in pursuit of message leaks.
+//
+// TODO(https://crbug.com/813045): Remove this.
+class ControllerMemoryDumpProvider
+    : public base::trace_event::MemoryDumpProvider {
+ public:
+  ControllerMemoryDumpProvider() {
+    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+        this, "IPCChannel", nullptr);
+  }
+
+  ~ControllerMemoryDumpProvider() override {
+    base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+        this);
+  }
+
+  void AddController(ChannelAssociatedGroupController* controller) {
+    base::AutoLock lock(lock_);
+    controllers_.insert(controller);
+  }
+
+  void RemoveController(ChannelAssociatedGroupController* controller) {
+    base::AutoLock lock(lock_);
+    controllers_.erase(controller);
+  }
+
+  // base::trace_event::MemoryDumpProvider:
+  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+                    base::trace_event::ProcessMemoryDump* pmd) override;
+
+ private:
+  base::Lock lock_;
+  std::set<ChannelAssociatedGroupController*> controllers_;
+
+  DISALLOW_COPY_AND_ASSIGN(ControllerMemoryDumpProvider);
+};
+
+ControllerMemoryDumpProvider& GetMemoryDumpProvider() {
+  static base::NoDestructor<ControllerMemoryDumpProvider> provider;
+  return *provider;
+}
+
 class ChannelAssociatedGroupController
     : public mojo::AssociatedGroupController,
       public mojo::MessageReceiver,
@@ -58,6 +109,13 @@
         "IPC::mojom::Bootstrap [master] PipeControlMessageHandler");
     filters_.Append<mojo::MessageHeaderValidator>(
         "IPC::mojom::Bootstrap [master] MessageHeaderValidator");
+
+    GetMemoryDumpProvider().AddController(this);
+  }
+
+  size_t GetQueuedMessageCount() {
+    base::AutoLock lock(outgoing_messages_lock_);
+    return outgoing_messages_.size();
   }
 
   void Bind(mojo::ScopedMessagePipeHandle handle) {
@@ -86,7 +144,10 @@
 
   void FlushOutgoingMessages() {
     std::vector<mojo::Message> outgoing_messages;
-    std::swap(outgoing_messages, outgoing_messages_);
+    {
+      base::AutoLock lock(outgoing_messages_lock_);
+      std::swap(outgoing_messages, outgoing_messages_);
+    }
     for (auto& message : outgoing_messages)
       SendMessage(&message);
   }
@@ -127,6 +188,8 @@
     connector_->CloseMessagePipe();
     OnPipeError();
     connector_.reset();
+
+    base::AutoLock lock(outgoing_messages_lock_);
     outgoing_messages_.clear();
   }
 
@@ -581,6 +644,8 @@
     }
 
     DCHECK(endpoints_.empty());
+
+    GetMemoryDumpProvider().RemoveController(this);
   }
 
   bool SendMessage(mojo::Message* message) {
@@ -588,6 +653,7 @@
       DCHECK(thread_checker_.CalledOnValidThread());
       if (!connector_ || paused_) {
         if (!shut_down_) {
+          base::AutoLock lock(outgoing_messages_lock_);
           outgoing_messages_.emplace_back(std::move(*message));
 
           // TODO(https://crbug.com/813045): Remove this. Typically this queue
@@ -871,6 +937,10 @@
   // NOTE: It is unsafe to call into this object while holding |lock_|.
   mojo::PipeControlMessageProxy control_message_proxy_;
 
+  // Guards access to |outgoing_messages_| only. Used to support memory dumps
+  // which may be triggered from any thread.
+  base::Lock outgoing_messages_lock_;
+
   // Outgoing messages that were sent before this controller was bound to a
   // real message pipe.
   std::vector<mojo::Message> outgoing_messages_;
@@ -889,6 +959,22 @@
   DISALLOW_COPY_AND_ASSIGN(ChannelAssociatedGroupController);
 };
 
+bool ControllerMemoryDumpProvider::OnMemoryDump(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* pmd) {
+  base::AutoLock lock(lock_);
+  for (auto* controller : controllers_) {
+    base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(
+        base::StringPrintf("mojo/queued_ipc_channel_message/0x%" PRIxPTR,
+                           reinterpret_cast<uintptr_t>(controller)));
+    dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
+                    base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                    controller->GetQueuedMessageCount());
+  }
+
+  return true;
+}
+
 class MojoBootstrapImpl : public MojoBootstrap {
  public:
   MojoBootstrapImpl(
diff --git a/jingle/glue/xmpp_client_socket_factory.cc b/jingle/glue/xmpp_client_socket_factory.cc
index 20b26db2..6e8af3a 100644
--- a/jingle/glue/xmpp_client_socket_factory.cc
+++ b/jingle/glue/xmpp_client_socket_factory.cc
@@ -39,7 +39,8 @@
     const net::HostPortPair& host_and_port) {
   // TODO(akalin): Use socket pools.
   auto transport_socket = proxy_resolving_socket_factory_.CreateSocket(
-      ssl_config_, GURL("https://" + host_and_port.ToString()));
+      ssl_config_, GURL("https://" + host_and_port.ToString()),
+      false /*use_tls*/);
   return (use_fake_ssl_client_socket_
               ? std::unique_ptr<net::StreamSocket>(
                     new FakeSSLClientSocket(std::move(transport_socket)))
diff --git a/media/audio/PRESUBMIT.py b/media/audio/PRESUBMIT.py
index dde9444..4cd32d6 100644
--- a/media/audio/PRESUBMIT.py
+++ b/media/audio/PRESUBMIT.py
@@ -21,7 +21,7 @@
     cl,
     [
       'luci.chromium.try:linux_optional_gpu_tests_rel',
-      'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel',
+      'luci.chromium.try:mac_optional_gpu_tests_rel',
       'master.tryserver.chromium.win:win_optional_gpu_tests_rel',
       'master.tryserver.chromium.android:android_optional_gpu_tests_rel',
     ],
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 9b5271e..5d84014 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -602,6 +602,51 @@
 }
 
 // static
+int VideoFrame::BytesPerElement(VideoPixelFormat format, size_t plane) {
+  DCHECK(IsValidPlane(plane, format));
+  switch (format) {
+    case PIXEL_FORMAT_ARGB:
+    case PIXEL_FORMAT_XRGB:
+    case PIXEL_FORMAT_RGB32:
+      return 4;
+    case PIXEL_FORMAT_RGB24:
+      return 3;
+    case PIXEL_FORMAT_Y16:
+    case PIXEL_FORMAT_UYVY:
+    case PIXEL_FORMAT_YUY2:
+    case PIXEL_FORMAT_YUV420P9:
+    case PIXEL_FORMAT_YUV422P9:
+    case PIXEL_FORMAT_YUV444P9:
+    case PIXEL_FORMAT_YUV420P10:
+    case PIXEL_FORMAT_YUV422P10:
+    case PIXEL_FORMAT_YUV444P10:
+    case PIXEL_FORMAT_YUV420P12:
+    case PIXEL_FORMAT_YUV422P12:
+    case PIXEL_FORMAT_YUV444P12:
+      return 2;
+    case PIXEL_FORMAT_NV12:
+    case PIXEL_FORMAT_NV21:
+    case PIXEL_FORMAT_MT21: {
+      static const int bytes_per_element[] = {1, 2};
+      DCHECK_LT(plane, arraysize(bytes_per_element));
+      return bytes_per_element[plane];
+    }
+    case PIXEL_FORMAT_YV12:
+    case PIXEL_FORMAT_I420:
+    case PIXEL_FORMAT_I422:
+    case PIXEL_FORMAT_I420A:
+    case PIXEL_FORMAT_I444:
+      return 1;
+    case PIXEL_FORMAT_MJPEG:
+      return 0;
+    case PIXEL_FORMAT_UNKNOWN:
+      break;
+  }
+  NOTREACHED();
+  return 0;
+}
+
+// static
 size_t VideoFrame::Rows(size_t plane, VideoPixelFormat format, int height) {
   DCHECK(IsValidPlane(plane, format));
   const int sample_height = SampleSize(format, plane).height();
@@ -882,9 +927,10 @@
 
   // TODO(miu): This function should support any pixel format.
   // http://crbug.com/555909
-  if (format != PIXEL_FORMAT_I420 && format != PIXEL_FORMAT_Y16) {
-    LOG(DFATAL) << "Only PIXEL_FORMAT_I420 and PIXEL_FORMAT_Y16 formats are"
-                   "supported: "
+  if (format != PIXEL_FORMAT_I420 && format != PIXEL_FORMAT_Y16 &&
+      format != PIXEL_FORMAT_ARGB) {
+    LOG(DFATAL) << "Only PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16, and "
+                   "PIXEL_FORMAT_ARGB formats are supported: "
                 << VideoPixelFormatToString(format);
     return nullptr;
   }
@@ -1130,51 +1176,6 @@
 }
 
 // static
-int VideoFrame::BytesPerElement(VideoPixelFormat format, size_t plane) {
-  DCHECK(IsValidPlane(plane, format));
-  switch (format) {
-    case PIXEL_FORMAT_ARGB:
-    case PIXEL_FORMAT_XRGB:
-    case PIXEL_FORMAT_RGB32:
-      return 4;
-    case PIXEL_FORMAT_RGB24:
-      return 3;
-    case PIXEL_FORMAT_Y16:
-    case PIXEL_FORMAT_UYVY:
-    case PIXEL_FORMAT_YUY2:
-    case PIXEL_FORMAT_YUV420P9:
-    case PIXEL_FORMAT_YUV422P9:
-    case PIXEL_FORMAT_YUV444P9:
-    case PIXEL_FORMAT_YUV420P10:
-    case PIXEL_FORMAT_YUV422P10:
-    case PIXEL_FORMAT_YUV444P10:
-    case PIXEL_FORMAT_YUV420P12:
-    case PIXEL_FORMAT_YUV422P12:
-    case PIXEL_FORMAT_YUV444P12:
-      return 2;
-    case PIXEL_FORMAT_NV12:
-    case PIXEL_FORMAT_NV21:
-    case PIXEL_FORMAT_MT21: {
-      static const int bytes_per_element[] = {1, 2};
-      DCHECK_LT(plane, arraysize(bytes_per_element));
-      return bytes_per_element[plane];
-    }
-    case PIXEL_FORMAT_YV12:
-    case PIXEL_FORMAT_I420:
-    case PIXEL_FORMAT_I422:
-    case PIXEL_FORMAT_I420A:
-    case PIXEL_FORMAT_I444:
-      return 1;
-    case PIXEL_FORMAT_MJPEG:
-      return 0;
-    case PIXEL_FORMAT_UNKNOWN:
-      break;
-  }
-  NOTREACHED();
-  return 0;
-}
-
-// static
 gfx::Size VideoFrame::CommonAlignment(VideoPixelFormat format) {
   int max_sample_width = 0;
   int max_sample_height = 0;
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index b0c6273..ea98a28 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -275,6 +275,9 @@
   // The width may be aligned to format requirements.
   static size_t RowBytes(size_t plane, VideoPixelFormat format, int width);
 
+  // Returns the number of bytes per element for given |plane| and |format|.
+  static int BytesPerElement(VideoPixelFormat format, size_t plane);
+
   // Returns the number of rows for the given plane, format, and height.
   // The height may be aligned to format requirements.
   static size_t Rows(size_t plane, VideoPixelFormat format, int height);
@@ -489,9 +492,6 @@
   // E.g. 2x2 for the U-plane in PIXEL_FORMAT_I420.
   static gfx::Size SampleSize(VideoPixelFormat format, size_t plane);
 
-  // Returns the number of bytes per element for given |plane| and |format|.
-  static int BytesPerElement(VideoPixelFormat format, size_t plane);
-
   // Return the alignment for the whole frame, calculated as the max of the
   // alignment for each individual plane.
   static gfx::Size CommonAlignment(VideoPixelFormat format);
diff --git a/media/base/video_util.cc b/media/base/video_util.cc
index 4f1f396..425a0ba 100644
--- a/media/base/video_util.cc
+++ b/media/base/video_util.cc
@@ -113,12 +113,18 @@
 
 static void LetterboxPlane(VideoFrame* frame,
                            int plane,
-                           const gfx::Rect& view_area,
+                           const gfx::Rect& view_area_in_pixels,
                            uint8_t fill_byte) {
   uint8_t* ptr = frame->data(plane);
   const int rows = frame->rows(plane);
   const int row_bytes = frame->row_bytes(plane);
   const int stride = frame->stride(plane);
+  const int bytes_per_element =
+      VideoFrame::BytesPerElement(frame->format(), plane);
+  gfx::Rect view_area(view_area_in_pixels.x() * bytes_per_element,
+                      view_area_in_pixels.y(),
+                      view_area_in_pixels.width() * bytes_per_element,
+                      view_area_in_pixels.height());
 
   CHECK_GE(stride, row_bytes);
   CHECK_GE(view_area.x(), 0);
@@ -153,20 +159,27 @@
   }
 }
 
-void LetterboxYUV(VideoFrame* frame, const gfx::Rect& view_area) {
-  DCHECK(!(view_area.x() & 1));
-  DCHECK(!(view_area.y() & 1));
-  DCHECK(!(view_area.width() & 1));
-  DCHECK(!(view_area.height() & 1));
-  DCHECK(frame->format() == PIXEL_FORMAT_YV12 ||
-         frame->format() == PIXEL_FORMAT_I420);
-  LetterboxPlane(frame, VideoFrame::kYPlane, view_area, 0x00);
-  gfx::Rect half_view_area(view_area.x() / 2,
-                           view_area.y() / 2,
-                           view_area.width() / 2,
-                           view_area.height() / 2);
-  LetterboxPlane(frame, VideoFrame::kUPlane, half_view_area, 0x80);
-  LetterboxPlane(frame, VideoFrame::kVPlane, half_view_area, 0x80);
+void LetterboxVideoFrame(VideoFrame* frame, const gfx::Rect& view_area) {
+  switch (frame->format()) {
+    case PIXEL_FORMAT_ARGB:
+      LetterboxPlane(frame, VideoFrame::kARGBPlane, view_area, 0x00);
+      break;
+    case PIXEL_FORMAT_YV12:
+    case PIXEL_FORMAT_I420: {
+      DCHECK(!(view_area.x() & 1));
+      DCHECK(!(view_area.y() & 1));
+      DCHECK(!(view_area.width() & 1));
+      DCHECK(!(view_area.height() & 1));
+      LetterboxPlane(frame, VideoFrame::kYPlane, view_area, 0x00);
+      gfx::Rect half_view_area(view_area.x() / 2, view_area.y() / 2,
+                               view_area.width() / 2, view_area.height() / 2);
+      LetterboxPlane(frame, VideoFrame::kUPlane, half_view_area, 0x80);
+      LetterboxPlane(frame, VideoFrame::kVPlane, half_view_area, 0x80);
+      break;
+    }
+    default:
+      NOTREACHED();
+  }
 }
 
 void RotatePlaneByPixels(const uint8_t* src,
@@ -378,7 +391,7 @@
   const int uv_stride = frame->stride(kU);
 
   if (region_in_frame != gfx::Rect(frame->coded_size())) {
-    LetterboxYUV(frame, region_in_frame);
+    LetterboxVideoFrame(frame, region_in_frame);
   }
 
   const int y_offset =
diff --git a/media/base/video_util.h b/media/base/video_util.h
index ac255e5..6db9f79c 100644
--- a/media/base/video_util.h
+++ b/media/base/video_util.h
@@ -31,12 +31,12 @@
                            uint8_t v,
                            uint8_t a);
 
-// Creates a border in |frame| such that all pixels outside of
-// |view_area| are black. The size and position of |view_area|
-// must be even to align correctly with the color planes.
-// Only YV12 format video frames are currently supported.
-MEDIA_EXPORT void LetterboxYUV(VideoFrame* frame,
-                               const gfx::Rect& view_area);
+// Creates a border in |frame| such that all pixels outside of |view_area| are
+// black. Only YV12 and ARGB format video frames are currently supported. If
+// format is YV12, the size and position of |view_area| must be even to align
+// correctly with the color planes.
+MEDIA_EXPORT void LetterboxVideoFrame(VideoFrame* frame,
+                                      const gfx::Rect& view_area);
 
 // Rotates |src| plane by |rotation| degree with possible flipping vertically
 // and horizontally.
diff --git a/media/base/video_util_unittest.cc b/media/base/video_util_unittest.cc
index bab5b34..f11828c0 100644
--- a/media/base/video_util_unittest.cc
+++ b/media/base/video_util_unittest.cc
@@ -476,7 +476,7 @@
       gfx::Size(40000, 30000), gfx::Size(0, 0)).IsEmpty());
 }
 
-TEST_F(VideoUtilTest, LetterboxYUV) {
+TEST_F(VideoUtilTest, LetterboxVideoFrame) {
   int width = 40;
   int height = 30;
   gfx::Size size(width, height);
@@ -491,7 +491,7 @@
                               width - left_margin - right_margin,
                               height - top_margin - bottom_margin);
           FillYUV(frame.get(), 0x1, 0x2, 0x3);
-          LetterboxYUV(frame.get(), view_area);
+          LetterboxVideoFrame(frame.get(), view_area);
           for (int x = 0; x < width; x++) {
             for (int y = 0; y < height; y++) {
               bool inside = x >= view_area.x() &&
diff --git a/media/capture/video/fake_video_capture_device_factory.cc b/media/capture/video/fake_video_capture_device_factory.cc
index 48578c92..ded8650 100644
--- a/media/capture/video/fake_video_capture_device_factory.cc
+++ b/media/capture/video/fake_video_capture_device_factory.cc
@@ -242,6 +242,12 @@
   }
 }
 
+void FakeVideoCaptureDeviceFactory::GetCameraLocationsAsync(
+    std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+    DeviceDescriptorsCallback result_callback) {
+  base::ResetAndReturn(&result_callback).Run(std::move(device_descriptors));
+}
+
 // static
 void FakeVideoCaptureDeviceFactory::ParseFakeDevicesConfigFromOptionsString(
     const std::string options_string,
diff --git a/media/capture/video/fake_video_capture_device_factory.h b/media/capture/video/fake_video_capture_device_factory.h
index fdfe942..6c8f68b 100644
--- a/media/capture/video/fake_video_capture_device_factory.h
+++ b/media/capture/video/fake_video_capture_device_factory.h
@@ -74,6 +74,9 @@
   void GetSupportedFormats(
       const VideoCaptureDeviceDescriptor& device_descriptor,
       VideoCaptureFormats* supported_formats) override;
+  void GetCameraLocationsAsync(
+      std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+      DeviceDescriptorsCallback result_callback) override;
 
   int number_of_devices() {
     DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/media/capture/video/file_video_capture_device_factory.cc b/media/capture/video/file_video_capture_device_factory.cc
index ff643d8..6205b57 100644
--- a/media/capture/video/file_video_capture_device_factory.cc
+++ b/media/capture/video/file_video_capture_device_factory.cc
@@ -78,4 +78,10 @@
   supported_formats->push_back(capture_format);
 }
 
+void FileVideoCaptureDeviceFactory::GetCameraLocationsAsync(
+    std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+    DeviceDescriptorsCallback result_callback) {
+  base::ResetAndReturn(&result_callback).Run(std::move(device_descriptors));
+}
+
 }  // namespace media
diff --git a/media/capture/video/file_video_capture_device_factory.h b/media/capture/video/file_video_capture_device_factory.h
index 8dfd3060b..0799e9c 100644
--- a/media/capture/video/file_video_capture_device_factory.h
+++ b/media/capture/video/file_video_capture_device_factory.h
@@ -28,6 +28,9 @@
   void GetSupportedFormats(
       const VideoCaptureDeviceDescriptor& device_descriptor,
       VideoCaptureFormats* supported_formats) override;
+  void GetCameraLocationsAsync(
+      std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+      DeviceDescriptorsCallback result_callback) override;
 };
 
 }  // namespace media
diff --git a/media/capture/video/video_capture_device_factory.cc b/media/capture/video/video_capture_device_factory.cc
index 89f37c4..4dd4f7b 100644
--- a/media/capture/video/video_capture_device_factory.cc
+++ b/media/capture/video/video_capture_device_factory.cc
@@ -69,4 +69,10 @@
 }
 #endif
 
+void VideoCaptureDeviceFactory::GetCameraLocationsAsync(
+    std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+    DeviceDescriptorsCallback result_callback) {
+  NOTIMPLEMENTED();
+}
+
 }  // namespace media
diff --git a/media/capture/video/video_capture_device_factory.h b/media/capture/video/video_capture_device_factory.h
index 73a8c48..1cd174ff 100644
--- a/media/capture/video/video_capture_device_factory.h
+++ b/media/capture/video/video_capture_device_factory.h
@@ -62,6 +62,16 @@
   virtual void GetDeviceDescriptors(
       VideoCaptureDeviceDescriptors* device_descriptors) = 0;
 
+  // Gets the location of all cameras of a device asynchronously.
+  // Used for platforms where camera location enumeration is asynchronous
+  // operation, i.e. UWP API on Windows 10.
+  // This method should be called before allocating or starting a device.
+  using DeviceDescriptorsCallback = base::OnceCallback<void(
+      std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors)>;
+  virtual void GetCameraLocationsAsync(
+      std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+      DeviceDescriptorsCallback result_callback);
+
  protected:
   base::ThreadChecker thread_checker_;
 
diff --git a/media/capture/video/video_capture_system_impl.cc b/media/capture/video/video_capture_system_impl.cc
index 7f75104..98cbe01 100644
--- a/media/capture/video/video_capture_system_impl.cc
+++ b/media/capture/video/video_capture_system_impl.cc
@@ -4,6 +4,9 @@
 
 #include "media/capture/video/video_capture_system_impl.h"
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "build/build_config.h"
 #include "media/base/bind_to_current_loop.h"
 
 namespace {
@@ -68,27 +71,31 @@
 void VideoCaptureSystemImpl::GetDeviceInfosAsync(
     DeviceInfoCallback result_callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  VideoCaptureDeviceDescriptors descriptors;
-  factory_->GetDeviceDescriptors(&descriptors);
-  // For devices for which we already have an entry in |devices_info_cache_|,
-  // we do not want to query the |factory_| for supported formats again. We
-  // simply copy them from |devices_info_cache_|.
-  std::vector<VideoCaptureDeviceInfo> new_devices_info_cache;
-  new_devices_info_cache.reserve(descriptors.size());
-  for (const auto& descriptor : descriptors) {
-    if (auto* cached_info = LookupDeviceInfoFromId(descriptor.device_id)) {
-      new_devices_info_cache.push_back(*cached_info);
-    } else {
-      // Query for supported formats in order to create the entry.
-      VideoCaptureDeviceInfo device_info(descriptor);
-      factory_->GetSupportedFormats(descriptor, &device_info.supported_formats);
-      ConsolidateCaptureFormats(&device_info.supported_formats);
-      new_devices_info_cache.push_back(device_info);
-    }
+
+  device_enum_request_queue_.push_back(std::move(result_callback));
+  if (device_enum_request_queue_.size() == 1) {
+    ProcessDeviceInfoRequest();
+  }
+}
+
+void VideoCaptureSystemImpl::ProcessDeviceInfoRequest() {
+  DeviceEnumQueue::iterator request = device_enum_request_queue_.begin();
+  if (request == device_enum_request_queue_.end()) {
+    return;
   }
 
-  devices_info_cache_.swap(new_devices_info_cache);
-  base::ResetAndReturn(&result_callback).Run(devices_info_cache_);
+  std::unique_ptr<VideoCaptureDeviceDescriptors> descriptors =
+      std::make_unique<VideoCaptureDeviceDescriptors>();
+  factory_->GetDeviceDescriptors(descriptors.get());
+
+#if defined(OS_WIN)
+  factory_->GetCameraLocationsAsync(
+      std::move(descriptors),
+      base::BindOnce(&VideoCaptureSystemImpl::DeviceInfosReady,
+                     base::Unretained(this)));
+#else
+  DeviceInfosReady(std::move(descriptors));
+#endif
 }
 
 // Creates a VideoCaptureDevice object. Returns NULL if something goes wrong.
@@ -114,4 +121,32 @@
   return &(*iter);
 }
 
+void VideoCaptureSystemImpl::DeviceInfosReady(
+    std::unique_ptr<VideoCaptureDeviceDescriptors> descriptors) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!device_enum_request_queue_.empty());
+  // For devices for which we already have an entry in |devices_info_cache_|,
+  // we do not want to query the |factory_| for supported formats again. We
+  // simply copy them from |devices_info_cache_|.
+  std::vector<VideoCaptureDeviceInfo> new_devices_info_cache;
+  new_devices_info_cache.reserve(descriptors->size());
+  for (const auto& descriptor : *descriptors) {
+    if (auto* cached_info = LookupDeviceInfoFromId(descriptor.device_id)) {
+      new_devices_info_cache.push_back(*cached_info);
+    } else {
+      // Query for supported formats in order to create the entry.
+      VideoCaptureDeviceInfo device_info(descriptor);
+      factory_->GetSupportedFormats(descriptor, &device_info.supported_formats);
+      ConsolidateCaptureFormats(&device_info.supported_formats);
+      new_devices_info_cache.push_back(device_info);
+    }
+  }
+
+  devices_info_cache_.swap(new_devices_info_cache);
+  base::ResetAndReturn(&device_enum_request_queue_.front())
+      .Run(devices_info_cache_);
+
+  device_enum_request_queue_.pop_front();
+  ProcessDeviceInfoRequest();
+}
 }  // namespace media
diff --git a/media/capture/video/video_capture_system_impl.h b/media/capture/video/video_capture_system_impl.h
index dfaaf418d..581979a 100644
--- a/media/capture/video/video_capture_system_impl.h
+++ b/media/capture/video/video_capture_system_impl.h
@@ -23,12 +23,19 @@
       const std::string& device_id) override;
 
  private:
+  using DeviceEnumQueue = std::list<DeviceInfoCallback>;
+
   // Returns nullptr if no descriptor found.
   const VideoCaptureDeviceInfo* LookupDeviceInfoFromId(
       const std::string& device_id);
 
+  void ProcessDeviceInfoRequest();
+  void DeviceInfosReady(
+      std::unique_ptr<VideoCaptureDeviceDescriptors> descriptors);
+
   const std::unique_ptr<VideoCaptureDeviceFactory> factory_;
   std::vector<VideoCaptureDeviceInfo> devices_info_cache_;
+  DeviceEnumQueue device_enum_request_queue_;
 
   base::ThreadChecker thread_checker_;
 };
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index 4475bbbe..863fdad6 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -8,6 +8,9 @@
 #include <mferror.h>
 #include <objbase.h>
 #include <stddef.h>
+#include <windows.devices.enumeration.h>
+#include <windows.foundation.collections.h>
+#include <wrl.h>
 #include <wrl/client.h>
 
 #include "base/command_line.h"
@@ -17,17 +20,26 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/win/core_winrt_util.h"
 #include "base/win/scoped_co_mem.h"
 #include "base/win/scoped_variant.h"
+#include "base/win/windows_version.h"
 #include "media/base/media_switches.h"
 #include "media/base/win/mf_initializer.h"
 #include "media/capture/video/win/metrics.h"
 #include "media/capture/video/win/video_capture_device_mf_win.h"
 #include "media/capture/video/win/video_capture_device_win.h"
 
+using namespace ABI::Windows::Devices::Enumeration;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace Microsoft::WRL;
+
 using Descriptor = media::VideoCaptureDeviceDescriptor;
 using Descriptors = media::VideoCaptureDeviceDescriptors;
+using base::win::GetActivationFactory;
 using base::win::ScopedCoMem;
+using base::win::ScopedHString;
 using base::win::ScopedVariant;
 using Microsoft::WRL::ComPtr;
 
@@ -53,6 +65,11 @@
   BLACKLISTED_CAMERA_MAX = BLACKLISTED_CAMERA_EPOCCAM,
 };
 
+#define UWP_ENUM_ERROR_HANDLER(hr, err_log)                         \
+  DLOG(WARNING) << err_log << logging::SystemErrorCodeToString(hr); \
+  origin_task_runner_->PostTask(FROM_HERE,                          \
+                                base::BindOnce(device_info_callback, nullptr))
+
 // Blacklisted devices are identified by a characteristic prefix of the name.
 // This prefix is used case-insensitively. This list must be kept in sync with
 // |BlacklistedCameraNames|.
@@ -390,9 +407,25 @@
   }
 }
 
+bool IsEnclosureLocationSupported() {
+  // DeviceInformation class is only available in Win10 onwards (v10.0.10240.0).
+  if (base::win::GetVersion() < base::win::VERSION_WIN10) {
+    DVLOG(1) << "DeviceInformation not supported before Windows 10";
+    return false;
+  }
+
+  if (!(base::win::ResolveCoreWinRTDelayload() &&
+        ScopedHString::ResolveCoreWinRTStringDelayload())) {
+    DLOG(ERROR) << "Failed loading functions from combase.dll";
+    return false;
+  }
+
+  return true;
+}
+
 }  // namespace
 
-// Returns true iff the current platform supports the Media Foundation API
+// Returns true if the current platform supports the Media Foundation API
 // and that the DLLs are available.  On Vista this API is an optional download
 // but the API is advertised as a part of Windows 7 and onwards.  However,
 // we've seen that the required DLLs are not available in some Win7
@@ -405,7 +438,9 @@
 
 VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin()
     : use_media_foundation_(
-          base::FeatureList::IsEnabled(media::kMediaFoundationVideoCapture)) {
+          base::FeatureList::IsEnabled(media::kMediaFoundationVideoCapture)),
+      com_thread_("Windows Video Capture COM Thread"),
+      weak_ptr_factory_(this) {
   mf_enum_device_sources_func_ =
       PlatformSupportsMediaFoundation() ? MFEnumDeviceSources : nullptr;
   if (!PlatformSupportsMediaFoundation()) {
@@ -421,6 +456,8 @@
   }
 }
 
+VideoCaptureDeviceFactoryWin::~VideoCaptureDeviceFactoryWin() = default;
+
 std::unique_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::CreateDevice(
     const Descriptor& device_descriptor) {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -435,7 +472,7 @@
         break;
       }
       std::unique_ptr<VideoCaptureDevice> device(
-          new VideoCaptureDeviceMFWin(source));
+          new VideoCaptureDeviceMFWin(device_descriptor, source));
       DVLOG(1) << " MediaFoundation Device: "
                << device_descriptor.display_name();
       if (static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init())
@@ -460,6 +497,7 @@
 void VideoCaptureDeviceFactoryWin::GetDeviceDescriptors(
     VideoCaptureDeviceDescriptors* device_descriptors) {
   DCHECK(thread_checker_.CalledOnValidThread());
+
   if (use_media_foundation_) {
     GetDeviceDescriptorsMediaFoundation(mf_enum_device_sources_func_,
                                         device_descriptors);
@@ -468,6 +506,170 @@
   }
 }
 
+void VideoCaptureDeviceFactoryWin::GetCameraLocationsAsync(
+    std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+    DeviceDescriptorsCallback result_callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (IsEnclosureLocationSupported()) {
+    origin_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+    com_thread_.init_com_with_mta(true);
+    com_thread_.Start();
+    com_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&VideoCaptureDeviceFactoryWin::EnumerateDevicesUWP,
+                       base::Unretained(this), std::move(device_descriptors),
+                       std::move(result_callback)));
+  } else {
+    DeviceInfoReady(std::move(device_descriptors), std::move(result_callback));
+  }
+}
+
+void VideoCaptureDeviceFactoryWin::EnumerateDevicesUWP(
+    std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+    DeviceDescriptorsCallback result_callback) {
+  DCHECK_GE(base::win::OSInfo::GetInstance()->version_number().build, 10240);
+
+  VideoCaptureDeviceFactoryWin* factory = this;
+  scoped_refptr<base::SingleThreadTaskRunner> com_thread_runner =
+      com_thread_.task_runner();
+
+  // The |device_info_callback| created by base::BindRepeating() is copyable,
+  // which is necessary for the below lambda function of |callback| for the
+  // asynchronous operation. The reason is to permanently capture anything in a
+  // lambda, it must be copyable, merely movable is insufficient.
+  auto device_info_callback = base::BindRepeating(
+      &VideoCaptureDeviceFactoryWin::FoundAllDevicesUWP,
+      base::Unretained(factory), base::Passed(&device_descriptors),
+      base::Passed(&result_callback));
+  auto callback =
+      Callback<IAsyncOperationCompletedHandler<DeviceInformationCollection*>>(
+          [factory, com_thread_runner, device_info_callback](
+              IAsyncOperation<DeviceInformationCollection*>* operation,
+              AsyncStatus status) -> HRESULT {
+            com_thread_runner->PostTask(
+                FROM_HERE, base::BindOnce(device_info_callback,
+                                          base::Unretained(operation)));
+            return S_OK;
+          });
+
+  ComPtr<IDeviceInformationStatics> dev_info_statics;
+  HRESULT hr = GetActivationFactory<
+      IDeviceInformationStatics,
+      RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(
+      &dev_info_statics);
+  if (FAILED(hr)) {
+    UWP_ENUM_ERROR_HANDLER(hr, "DeviceInformation factory failed: ");
+    return;
+  }
+
+  IAsyncOperation<DeviceInformationCollection*>* async_op;
+  hr = dev_info_statics->FindAllAsyncDeviceClass(DeviceClass_VideoCapture,
+                                                 &async_op);
+  if (FAILED(hr)) {
+    UWP_ENUM_ERROR_HANDLER(hr, "Find all devices asynchronously failed: ");
+    return;
+  }
+
+  hr = async_op->put_Completed(callback.Get());
+  if (FAILED(hr)) {
+    UWP_ENUM_ERROR_HANDLER(hr, "Register async operation callback failed: ");
+    return;
+  }
+
+  // Keep a reference to incomplete |asyn_op| for releasing later.
+  async_ops_.insert(async_op);
+}
+
+void VideoCaptureDeviceFactoryWin::FoundAllDevicesUWP(
+    std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+    DeviceDescriptorsCallback result_callback,
+    IAsyncOperation<DeviceInformationCollection*>* operation) {
+  if (!operation) {
+    origin_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&VideoCaptureDeviceFactoryWin::DeviceInfoReady,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       std::move(device_descriptors),
+                       std::move(result_callback)));
+    return;
+  }
+
+  ComPtr<IVectorView<DeviceInformation*>> devices;
+  operation->GetResults(devices.GetAddressOf());
+
+  unsigned int count = 0;
+  devices->get_Size(&count);
+
+  for (unsigned int j = 0; j < count; ++j) {
+    ComPtr<IDeviceInformation> device_info;
+    HRESULT hr = devices->GetAt(j, device_info.GetAddressOf());
+    if (SUCCEEDED(hr)) {
+      HSTRING id;
+      device_info->get_Id(&id);
+
+      std::string device_id = ScopedHString(id).GetAsUTF8();
+      transform(device_id.begin(), device_id.end(), device_id.begin(),
+                ::tolower);
+      const std::string model_id = GetDeviceModelId(device_id);
+
+      ComPtr<IEnclosureLocation> enclosure_location;
+      hr =
+          device_info->get_EnclosureLocation(enclosure_location.GetAddressOf());
+      if (FAILED(hr)) {
+        break;
+      }
+
+      VideoFacingMode facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE;
+      if (enclosure_location) {
+        Panel panel;
+        enclosure_location->get_Panel(&panel);
+        switch (panel) {
+          case Panel_Unknown:
+            facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE;
+            break;
+          case Panel_Front:
+            facing = VideoFacingMode::MEDIA_VIDEO_FACING_USER;
+            break;
+          case Panel_Back:
+            facing = VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT;
+            break;
+          default:
+            facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE;
+        }
+      }
+
+      for (Descriptor& descriptor : *device_descriptors) {
+        if (!descriptor.model_id.compare(model_id)) {
+          descriptor.facing = facing;
+          break;
+        }
+      }
+    }
+  }
+
+  origin_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&VideoCaptureDeviceFactoryWin::DeviceInfoReady,
+                     base::Unretained(this), std::move(device_descriptors),
+                     std::move(result_callback)));
+
+  auto it = async_ops_.find(operation);
+  DCHECK(it != async_ops_.end());
+  (*it)->Release();
+  async_ops_.erase(it);
+}
+
+void VideoCaptureDeviceFactoryWin::DeviceInfoReady(
+    std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+    DeviceDescriptorsCallback result_callback) {
+  if (com_thread_.IsRunning()) {
+    com_thread_.Stop();
+  }
+
+  base::ResetAndReturn(&result_callback).Run(std::move(device_descriptors));
+}
+
 void VideoCaptureDeviceFactoryWin::GetSupportedFormats(
     const Descriptor& device,
     VideoCaptureFormats* formats) {
diff --git a/media/capture/video/win/video_capture_device_factory_win.h b/media/capture/video/win/video_capture_device_factory_win.h
index 64cf2a5c..91135b8 100644
--- a/media/capture/video/win/video_capture_device_factory_win.h
+++ b/media/capture/video/win/video_capture_device_factory_win.h
@@ -8,11 +8,17 @@
 #define MEDIA_CAPTURE_VIDEO_WIN_VIDEO_CAPTURE_DEVICE_FACTORY_WIN_H_
 
 #include <mfidl.h>
+#include <windows.devices.enumeration.h>
+
 #include "base/macros.h"
+#include "base/threading/thread.h"
 #include "media/capture/video/video_capture_device_factory.h"
 
 namespace media {
 
+using ABI::Windows::Foundation::IAsyncOperation;
+using ABI::Windows::Devices::Enumeration::DeviceInformationCollection;
+
 // Extension of VideoCaptureDeviceFactory to create and manipulate Windows
 // devices, via either DirectShow or MediaFoundation APIs.
 class CAPTURE_EXPORT VideoCaptureDeviceFactoryWin
@@ -21,7 +27,7 @@
   static bool PlatformSupportsMediaFoundation();
 
   VideoCaptureDeviceFactoryWin();
-  ~VideoCaptureDeviceFactoryWin() override {}
+  ~VideoCaptureDeviceFactoryWin() override;
 
   using MFEnumDeviceSourcesFunc = decltype(&MFEnumDeviceSources);
 
@@ -32,6 +38,9 @@
   void GetSupportedFormats(
       const VideoCaptureDeviceDescriptor& device_descriptor,
       VideoCaptureFormats* supported_formats) override;
+  void GetCameraLocationsAsync(
+      std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+      DeviceDescriptorsCallback result_callback) override;
 
   void set_use_media_foundation_for_testing(bool use) {
     use_media_foundation_ = use;
@@ -42,12 +51,29 @@
   }
 
  private:
+  void EnumerateDevicesUWP(
+      std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+      DeviceDescriptorsCallback result_callback);
+  void FoundAllDevicesUWP(
+      std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+      DeviceDescriptorsCallback result_callback,
+      IAsyncOperation<DeviceInformationCollection*>* operation);
+  void DeviceInfoReady(
+      std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
+      DeviceDescriptorsCallback result_callback);
+
   bool use_media_foundation_;
   // In production code, when Media Foundation libraries are available,
   // |mf_enum_device_sources_func_| points to MFEnumDeviceSources. It enables
   // mock of Media Foundation API in unit tests.
   MFEnumDeviceSourcesFunc mf_enum_device_sources_func_ = nullptr;
 
+  // For calling WinRT methods on a COM initiated thread.
+  base::Thread com_thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
+  std::unordered_set<IAsyncOperation<DeviceInformationCollection*>*> async_ops_;
+  base::WeakPtrFactory<VideoCaptureDeviceFactoryWin> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(VideoCaptureDeviceFactoryWin);
 };
 
diff --git a/media/capture/video/win/video_capture_device_mf_win.cc b/media/capture/video/win/video_capture_device_mf_win.cc
index 7230966..198fa98 100644
--- a/media/capture/video/win/video_capture_device_mf_win.cc
+++ b/media/capture/video/win/video_capture_device_mf_win.cc
@@ -394,7 +394,7 @@
     base::TimeDelta timestamp =
         base::TimeDelta::FromMicroseconds(raw_time_stamp / 10);
     if (!sample) {
-      observer_->OnIncomingCapturedData(NULL, 0, 0, reference_time, timestamp);
+      observer_->OnIncomingCapturedData(NULL, 0, reference_time, timestamp);
       return S_OK;
     }
 
@@ -408,8 +408,8 @@
         DWORD length = 0, max_length = 0;
         BYTE* data = NULL;
         buffer->Lock(&data, &max_length, &length);
-        observer_->OnIncomingCapturedData(data, length, GetCameraRotation(),
-                                          reference_time, timestamp);
+        observer_->OnIncomingCapturedData(data, length, reference_time,
+                                          timestamp);
         buffer->Unlock();
       }
     }
@@ -553,13 +553,17 @@
   return hr;
 }
 
-VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(ComPtr<IMFMediaSource> source)
-    : VideoCaptureDeviceMFWin(source, nullptr) {}
+VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(
+    const VideoCaptureDeviceDescriptor& device_descriptor,
+    ComPtr<IMFMediaSource> source)
+    : VideoCaptureDeviceMFWin(device_descriptor, source, nullptr) {}
 
 VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(
+    const VideoCaptureDeviceDescriptor& device_descriptor,
     ComPtr<IMFMediaSource> source,
     ComPtr<IMFCaptureEngine> engine)
-    : create_mf_photo_callback_(base::BindRepeating(&CreateMFPhotoCallback)),
+    : facing_mode_(device_descriptor.facing),
+      create_mf_photo_callback_(base::BindRepeating(&CreateMFPhotoCallback)),
       is_initialized_(false),
       max_retry_count_(200),
       retry_delay_in_ms_(50),
@@ -945,7 +949,6 @@
 void VideoCaptureDeviceMFWin::OnIncomingCapturedData(
     const uint8_t* data,
     int length,
-    int rotation,
     base::TimeTicks reference_time,
     base::TimeDelta timestamp) {
   base::AutoLock lock(lock_);
@@ -955,8 +958,8 @@
 
   if (client_.get()) {
     client_->OnIncomingCapturedData(
-        data, length, selected_video_capability_->supported_format, rotation,
-        reference_time, timestamp);
+        data, length, selected_video_capability_->supported_format,
+        GetCameraRotation(facing_mode_), reference_time, timestamp);
   }
 
   while (!video_stream_take_photo_callbacks_.empty()) {
diff --git a/media/capture/video/win/video_capture_device_mf_win.h b/media/capture/video/win/video_capture_device_mf_win.h
index c43b143b..a4a4acf 100644
--- a/media/capture/video/win/video_capture_device_mf_win.h
+++ b/media/capture/video/win/video_capture_device_mf_win.h
@@ -41,8 +41,10 @@
                                                      VideoPixelFormat* format);
 
   explicit VideoCaptureDeviceMFWin(
+      const VideoCaptureDeviceDescriptor& device_descriptor,
       Microsoft::WRL::ComPtr<IMFMediaSource> source);
   explicit VideoCaptureDeviceMFWin(
+      const VideoCaptureDeviceDescriptor& device_descriptor,
       Microsoft::WRL::ComPtr<IMFMediaSource> source,
       Microsoft::WRL::ComPtr<IMFCaptureEngine> engine);
 
@@ -64,7 +66,6 @@
   // Captured new video data.
   void OnIncomingCapturedData(const uint8_t* data,
                               int length,
-                              int rotation,
                               base::TimeTicks reference_time,
                               base::TimeDelta timestamp);
   void OnEvent(IMFMediaEvent* media_event);
@@ -110,6 +111,7 @@
   void OnError(const base::Location& from_here, HRESULT hr);
   void OnError(const base::Location& from_here, const char* message);
 
+  VideoFacingMode facing_mode_;
   CreateMFPhotoCallbackCB create_mf_photo_callback_;
   scoped_refptr<MFVideoCallback> video_callback_;
   bool is_initialized_;
diff --git a/media/capture/video/win/video_capture_device_mf_win_unittest.cc b/media/capture/video/win/video_capture_device_mf_win_unittest.cc
index 7ef9f343..cc7c0de 100644
--- a/media/capture/video/win/video_capture_device_mf_win_unittest.cc
+++ b/media/capture/video/win/video_capture_device_mf_win_unittest.cc
@@ -809,11 +809,13 @@
 class VideoCaptureDeviceMFWinTest : public ::testing::Test {
  protected:
   VideoCaptureDeviceMFWinTest()
-      : media_source_(new MockMFMediaSource()),
+      : descriptor_(VideoCaptureDeviceDescriptor()),
+        media_source_(new MockMFMediaSource()),
         engine_(new MockMFCaptureEngine()),
         client_(new MockClient()),
         image_capture_client_(new MockImageCaptureClient()),
-        device_(new VideoCaptureDeviceMFWin(media_source_, engine_)),
+        device_(
+            new VideoCaptureDeviceMFWin(descriptor_, media_source_, engine_)),
         capture_source_(new MockMFCaptureSource()),
         capture_preview_sink_(new MockCapturePreviewSink()),
         media_foundation_supported_(
@@ -1022,6 +1024,7 @@
         .WillRepeatedly(Invoke(get_device_media_type));
   }
 
+  VideoCaptureDeviceDescriptor descriptor_;
   Microsoft::WRL::ComPtr<MockMFMediaSource> media_source_;
   Microsoft::WRL::ComPtr<MockMFCaptureEngine> engine_;
   std::unique_ptr<MockClient> client_;
diff --git a/media/capture/video/win/video_capture_device_utils_win.cc b/media/capture/video/win/video_capture_device_utils_win.cc
index d0ce2c5..d661a62 100644
--- a/media/capture/video/win/video_capture_device_utils_win.cc
+++ b/media/capture/video/win/video_capture_device_utils_win.cc
@@ -6,18 +6,27 @@
 
 #include <iostream>
 
+#include "base/win/windows_version.h"
+
 namespace media {
 
 // Note: Because we can't find a solid way to detect camera location (front/back
 // or external USB camera) with Win32 APIs, assume it's always front camera when
 // auto rotation is enabled for now.
-int GetCameraRotation() {
+int GetCameraRotation(VideoFacingMode facing) {
   int rotation = 0;
 
   if (!IsAutoRotationEnabled()) {
     return rotation;
   }
 
+  // Before Win10, we can't distinguish if the selected camera is an internal or
+  // external one. So we assume it's internal and do the frame rotation if the
+  // auto rotation is enabled to cover most user cases.
+  if (!IsInternalCamera(facing)) {
+    return rotation;
+  }
+
   // When display is only on external monitors, the auto-rotation state still
   // may be ENALBED on the target device. In that case, we shouldn't query the
   // display orientation and the built-in camera will be treated as an external
@@ -77,6 +86,19 @@
   return false;
 }
 
+bool IsInternalCamera(VideoFacingMode facing) {
+  if (base::win::GetVersion() < base::win::VERSION_WIN10) {
+    return true;
+  }
+
+  if (facing == MEDIA_VIDEO_FACING_USER ||
+      facing == MEDIA_VIDEO_FACING_ENVIRONMENT) {
+    return true;
+  }
+
+  return false;
+}
+
 bool HasActiveInternalDisplayDevice(DISPLAY_DEVICE* internal_display_device) {
   DISPLAY_DEVICE display_device;
   display_device.cb = sizeof(display_device);
diff --git a/media/capture/video/win/video_capture_device_utils_win.h b/media/capture/video/win/video_capture_device_utils_win.h
index 56276260..84c6b1a 100644
--- a/media/capture/video/win/video_capture_device_utils_win.h
+++ b/media/capture/video/win/video_capture_device_utils_win.h
@@ -7,13 +7,16 @@
 
 #include <windows.h>
 
+#include "media/base/video_facing.h"
+
 namespace media {
 
 // Returns the rotation of the camera. Returns 0 if it's not a built-in camera,
 // or auto-rotation is not enabled, or only displays on external monitors.
-int GetCameraRotation();
+int GetCameraRotation(VideoFacingMode facing);
 
 bool IsAutoRotationEnabled();
+bool IsInternalCamera(VideoFacingMode facing);
 
 // Returns true if target device has active internal display panel, e.g. the
 // screen attached to tablets or laptops, and stores its device info in
diff --git a/media/capture/video/win/video_capture_device_win.cc b/media/capture/video/win/video_capture_device_win.cc
index b016957..2a562b36 100644
--- a/media/capture/video/win/video_capture_device_win.cc
+++ b/media/capture/video/win/video_capture_device_win.cc
@@ -858,7 +858,8 @@
   if (timestamp == kNoTimestamp)
     timestamp = base::TimeTicks::Now() - first_ref_time_;
 
-  client_->OnIncomingCapturedData(buffer, length, format, GetCameraRotation(),
+  client_->OnIncomingCapturedData(buffer, length, format,
+                                  GetCameraRotation(device_descriptor_.facing),
                                   base::TimeTicks::Now(), timestamp);
 
   while (!take_photo_callbacks_.empty()) {
diff --git a/media/cdm/stub/stub_cdm.cc b/media/cdm/stub/stub_cdm.cc
deleted file mode 100644
index faf1ab2..0000000
--- a/media/cdm/stub/stub_cdm.cc
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2015 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/cdm/stub/stub_cdm.h"
-
-#include "base/logging.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/strings/string_number_conversions.h"
-
-// Version number for this stub. The third number represents the
-// cdm::ContentDecryptionModule version.
-const char kStubCdmVersion[] = "1.4.8.0";
-
-void INITIALIZE_CDM_MODULE() {
-}
-
-void DeinitializeCdmModule() {
-}
-
-void* CreateCdmInstance(int cdm_interface_version,
-                        const char* /* key_system */,
-                        uint32_t /* key_system_size */,
-                        GetCdmHostFunc get_cdm_host_func,
-                        void* user_data) {
-  DVLOG(1) << "CreateCdmInstance()";
-
-  if (cdm_interface_version != media::StubCdmInterface::kVersion)
-    return nullptr;
-
-  media::StubCdmInterface::Host* host =
-      static_cast<media::StubCdmInterface::Host*>(get_cdm_host_func(
-          media::StubCdmInterface::Host::kVersion, user_data));
-  if (!host)
-    return nullptr;
-
-  return new media::StubCdm(host);
-}
-
-const char* GetCdmVersion() {
-  return kStubCdmVersion;
-}
-
-namespace media {
-
-StubCdm::StubCdm(Host* host) : host_(host), next_session_id_(0) {
-}
-
-StubCdm::~StubCdm() {
-}
-
-void StubCdm::Initialize(bool /* allow_distinctive_identifier */,
-                         bool /* allow_persistent_state */) {
-}
-
-void StubCdm::CreateSessionAndGenerateRequest(
-    uint32_t promise_id,
-    cdm::SessionType /* session_type */,
-    cdm::InitDataType /* init_data_type */,
-    const uint8_t* /* init_data */,
-    uint32_t /* init_data_size */) {
-  // Provide a dummy message (with a trivial session ID) to enable some testing
-  // and be consistent with existing testing without a license server.
-  std::string session_id(base::UintToString(next_session_id_++));
-  host_->OnResolveNewSessionPromise(
-      promise_id, session_id.data(),
-      base::checked_cast<uint32_t>(session_id.length()));
-  host_->OnSessionMessage(session_id.data(),
-                          base::checked_cast<uint32_t>(session_id.length()),
-                          cdm::kLicenseRequest, nullptr, 0, nullptr, 0);
-}
-
-void StubCdm::LoadSession(uint32_t promise_id,
-                          cdm::SessionType /* session_type */,
-                          const char* /* session_id */,
-                          uint32_t /* session_id_length */) {
-  FailRequest(promise_id);
-}
-
-void StubCdm::UpdateSession(uint32_t promise_id,
-                            const char* /* session_id */,
-                            uint32_t /* session_id_length */,
-                            const uint8_t* /* response */,
-                            uint32_t /* response_size */) {
-  FailRequest(promise_id);
-}
-
-void StubCdm::CloseSession(uint32_t promise_id,
-                           const char* /* session_id */,
-                           uint32_t /* session_id_length */) {
-  FailRequest(promise_id);
-}
-
-void StubCdm::RemoveSession(uint32_t promise_id,
-                            const char* /* session_id */,
-                            uint32_t /* session_id_length */) {
-  FailRequest(promise_id);
-}
-
-void StubCdm::SetServerCertificate(
-    uint32_t promise_id,
-    const uint8_t* /* server_certificate_data */,
-    uint32_t /* server_certificate_data_size */) {
-  FailRequest(promise_id);
-}
-
-void StubCdm::TimerExpired(void* /* context */) {
-}
-
-cdm::Status StubCdm::Decrypt(const cdm::InputBuffer& /* encrypted_buffer */,
-                             cdm::DecryptedBlock* /* decrypted_block */) {
-  return cdm::kDecryptError;
-}
-
-cdm::Status StubCdm::InitializeAudioDecoder(
-    const cdm::AudioDecoderConfig& /* audio_decoder_config */) {
-  return cdm::kSuccess;
-}
-
-cdm::Status StubCdm::InitializeVideoDecoder(
-    const cdm::VideoDecoderConfig& /* video_decoder_config */) {
-  return cdm::kSuccess;
-}
-
-void StubCdm::ResetDecoder(cdm::StreamType /* decoder_type */) {
-}
-
-void StubCdm::DeinitializeDecoder(cdm::StreamType /* decoder_type */) {
-}
-
-cdm::Status StubCdm::DecryptAndDecodeFrame(
-    const cdm::InputBuffer& /* encrypted_buffer */,
-    cdm::VideoFrame* /* decoded_frame */) {
-  return cdm::kNoKey;
-}
-
-cdm::Status StubCdm::DecryptAndDecodeSamples(
-    const cdm::InputBuffer& /* encrypted_buffer */,
-    cdm::AudioFrames* /* audio_frames */) {
-  return cdm::kNoKey;
-}
-
-void StubCdm::Destroy() {
-  delete this;
-}
-
-void StubCdm::OnPlatformChallengeResponse(
-    const cdm::PlatformChallengeResponse& /* response */) {
-  NOTREACHED();
-}
-
-void StubCdm::OnQueryOutputProtectionStatus(
-    cdm::QueryResult /* result */,
-    uint32_t /* link_mask */,
-    uint32_t /* output_protection_mask */) {
-  NOTREACHED();
-};
-
-void StubCdm::FailRequest(uint32_t promise_id) {
-  std::string message("Operation not supported by stub CDM.");
-  host_->OnRejectPromise(promise_id, cdm::kInvalidAccessError, 0,
-                         message.data(),
-                         base::checked_cast<uint32_t>(message.length()));
-}
-
-}  // namespace media
diff --git a/media/cdm/stub/stub_cdm.h b/media/cdm/stub/stub_cdm.h
deleted file mode 100644
index 8d7623a..0000000
--- a/media/cdm/stub/stub_cdm.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_CDM_STUB_STUB_CDM_H_
-#define MEDIA_CDM_STUB_STUB_CDM_H_
-
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "media/cdm/api/content_decryption_module.h"
-
-namespace media {
-
-typedef cdm::ContentDecryptionModule_8 StubCdmInterface;
-
-// Dummy implementation of the cdm::ContentDecryptionModule interface.
-class StubCdm : public StubCdmInterface {
- public:
-  explicit StubCdm(Host* host);
-  ~StubCdm() override;
-
-  // StubCdmInterface implementation.
-  void Initialize(bool allow_distinctive_identifier,
-                  bool allow_persistent_state) override;
-  void CreateSessionAndGenerateRequest(uint32_t promise_id,
-                                       cdm::SessionType session_type,
-                                       cdm::InitDataType init_data_type,
-                                       const uint8_t* init_data,
-                                       uint32_t init_data_size) override;
-  void LoadSession(uint32_t promise_id,
-                   cdm::SessionType session_type,
-                   const char* session_id,
-                   uint32_t session_id_length) override;
-  void UpdateSession(uint32_t promise_id,
-                     const char* session_id,
-                     uint32_t session_id_length,
-                     const uint8_t* response,
-                     uint32_t response_size) override;
-  void CloseSession(uint32_t promise_id,
-                    const char* session_id,
-                    uint32_t session_id_length) override;
-  void RemoveSession(uint32_t promise_id,
-                     const char* session_id,
-                     uint32_t session_id_length) override;
-  void SetServerCertificate(uint32_t promise_id,
-                            const uint8_t* server_certificate_data,
-                            uint32_t server_certificate_data_size) override;
-  void TimerExpired(void* context) override;
-  cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
-                      cdm::DecryptedBlock* decrypted_block) override;
-  cdm::Status InitializeAudioDecoder(
-      const cdm::AudioDecoderConfig& audio_decoder_config) override;
-  cdm::Status InitializeVideoDecoder(
-      const cdm::VideoDecoderConfig& video_decoder_config) override;
-  void DeinitializeDecoder(cdm::StreamType decoder_type) override;
-  void ResetDecoder(cdm::StreamType decoder_type) override;
-  cdm::Status DecryptAndDecodeFrame(const cdm::InputBuffer& encrypted_buffer,
-                                    cdm::VideoFrame* video_frame) override;
-  cdm::Status DecryptAndDecodeSamples(const cdm::InputBuffer& encrypted_buffer,
-                                      cdm::AudioFrames* audio_frames) override;
-  void Destroy() override;
-  void OnPlatformChallengeResponse(
-      const cdm::PlatformChallengeResponse& response) override;
-  void OnQueryOutputProtectionStatus(cdm::QueryResult result,
-                                     uint32_t link_mask,
-                                     uint32_t output_protection_mask) override;
-
- private:
-  // Helper function that rejects the promise specified by |promise_id|.
-  void FailRequest(uint32_t promise_id);
-
-  Host* host_;
-
-  uint32_t next_session_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(StubCdm);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_CDM_STUB_STUB_CDM_H_
diff --git a/media/filters/PRESUBMIT.py b/media/filters/PRESUBMIT.py
index 6cdf17c4..e8b1f08 100644
--- a/media/filters/PRESUBMIT.py
+++ b/media/filters/PRESUBMIT.py
@@ -21,7 +21,7 @@
     cl,
     [
       'luci.chromium.try:linux_optional_gpu_tests_rel',
-      'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel',
+      'luci.chromium.try:mac_optional_gpu_tests_rel',
       'master.tryserver.chromium.win:win_optional_gpu_tests_rel',
       'master.tryserver.chromium.android:android_optional_gpu_tests_rel',
     ],
diff --git a/media/gpu/PRESUBMIT.py b/media/gpu/PRESUBMIT.py
index fab19b6..6d49ed3 100644
--- a/media/gpu/PRESUBMIT.py
+++ b/media/gpu/PRESUBMIT.py
@@ -21,7 +21,7 @@
     cl,
     [
       'luci.chromium.try:linux_optional_gpu_tests_rel',
-      'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel',
+      'luci.chromium.try:mac_optional_gpu_tests_rel',
       'master.tryserver.chromium.win:win_optional_gpu_tests_rel',
       'master.tryserver.chromium.android:android_optional_gpu_tests_rel',
     ],
diff --git a/net/cert/ct_policy_enforcer_unittest.cc b/net/cert/ct_policy_enforcer_unittest.cc
index d39017f..906cfc9cb 100644
--- a/net/cert/ct_policy_enforcer_unittest.cc
+++ b/net/cert/ct_policy_enforcer_unittest.cc
@@ -431,7 +431,7 @@
     // Create a self-signed certificate with exactly the validity period.
     std::string cert_data;
     ASSERT_TRUE(x509_util::CreateSelfSignedCert(
-        private_key.get(), x509_util::DIGEST_SHA256, "CN=test",
+        private_key->key(), x509_util::DIGEST_SHA256, "CN=test",
         i * 10 + required_scts, start, end, &cert_data));
     scoped_refptr<X509Certificate> cert(
         X509Certificate::CreateFromBytes(cert_data.data(), cert_data.size()));
diff --git a/net/cert/x509_util.cc b/net/cert/x509_util.cc
index b055874..6d2ed5552 100644
--- a/net/cert/x509_util.cc
+++ b/net/cert/x509_util.cc
@@ -202,10 +202,10 @@
                                 std::string* der_cert) {
   std::unique_ptr<crypto::RSAPrivateKey> new_key(
       crypto::RSAPrivateKey::Create(kRSAKeyLength));
-  if (!new_key.get())
+  if (!new_key)
     return false;
 
-  bool success = CreateSelfSignedCert(new_key.get(),
+  bool success = CreateSelfSignedCert(new_key->key(),
                                       kSignatureDigestAlgorithm,
                                       subject,
                                       serial_number,
@@ -218,7 +218,7 @@
   return success;
 }
 
-bool CreateSelfSignedCert(crypto::RSAPrivateKey* key,
+bool CreateSelfSignedCert(EVP_PKEY* key,
                           DigestAlgorithm alg,
                           const std::string& subject,
                           uint32_t serial_number,
@@ -256,7 +256,7 @@
       !AddTime(&validity, not_valid_before) ||
       !AddTime(&validity, not_valid_after) ||
       !AddNameWithCommonName(&tbs_cert, common_name) ||  // subject
-      !EVP_marshal_public_key(&tbs_cert, key->key()) ||  // subjectPublicKeyInfo
+      !EVP_marshal_public_key(&tbs_cert, key) ||  // subjectPublicKeyInfo
       !CBB_finish(cbb.get(), &tbs_cert_bytes, &tbs_cert_len)) {
     return false;
   }
@@ -275,8 +275,7 @@
       !AddRSASignatureAlgorithm(&cert, alg) ||
       !CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING) ||
       !CBB_add_u8(&signature, 0 /* no unused bits */) ||
-      !EVP_DigestSignInit(ctx.get(), nullptr, ToEVP(alg), nullptr,
-                          key->key()) ||
+      !EVP_DigestSignInit(ctx.get(), nullptr, ToEVP(alg), nullptr, key) ||
       // Compute the maximum signature length.
       !EVP_DigestSign(ctx.get(), nullptr, &sig_len, tbs_cert_bytes,
                       tbs_cert_len) ||
diff --git a/net/cert/x509_util.h b/net/cert/x509_util.h
index 6f0b8b215..32639e3 100644
--- a/net/cert/x509_util.h
+++ b/net/cert/x509_util.h
@@ -15,6 +15,7 @@
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "net/base/net_export.h"
+#include "third_party/boringssl/src/include/openssl/base.h"
 #include "third_party/boringssl/src/include/openssl/pool.h"
 
 namespace crypto {
@@ -66,7 +67,7 @@
 
 // Creates a self-signed certificate from a provided key, using the specified
 // hash algorithm.
-NET_EXPORT bool CreateSelfSignedCert(crypto::RSAPrivateKey* key,
+NET_EXPORT bool CreateSelfSignedCert(EVP_PKEY* key,
                                      DigestAlgorithm alg,
                                      const std::string& subject,
                                      uint32_t serial_number,
diff --git a/net/cert/x509_util_unittest.cc b/net/cert/x509_util_unittest.cc
index c5d6b36..cbaadc8 100644
--- a/net/cert/x509_util_unittest.cc
+++ b/net/cert/x509_util_unittest.cc
@@ -137,7 +137,7 @@
 
   std::string der_cert;
   ASSERT_TRUE(x509_util::CreateSelfSignedCert(
-      private_key.get(), x509_util::DIGEST_SHA256, "CN=subject", 1,
+      private_key->key(), x509_util::DIGEST_SHA256, "CN=subject", 1,
       base::Time::Now(), base::Time::Now() + base::TimeDelta::FromDays(1),
       &der_cert));
 
diff --git a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
index 4ae3ab0a..23343c0 100644
--- a/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/chromium/bidirectional_stream_quic_impl_unittest.cc
@@ -28,6 +28,7 @@
 #include "net/quic/chromium/quic_chromium_packet_writer.h"
 #include "net/quic/chromium/quic_http_utils.h"
 #include "net/quic/chromium/quic_server_info.h"
+#include "net/quic/chromium/quic_stream_factory.h"
 #include "net/quic/chromium/quic_test_packet_maker.h"
 #include "net/quic/chromium/test_task_runner.h"
 #include "net/quic/core/crypto/crypto_protocol.h"
diff --git a/net/quic/chromium/quic_chromium_packet_writer.cc b/net/quic/chromium/quic_chromium_packet_writer.cc
index af3b7c0a..5786a7f0 100644
--- a/net/quic/chromium/quic_chromium_packet_writer.cc
+++ b/net/quic/chromium/quic_chromium_packet_writer.cc
@@ -38,6 +38,32 @@
                              count, kMaxRetries + 1);
 }
 
+const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+    net::DefineNetworkTrafficAnnotation("quic_chromium_packet_writer", R"(
+        semantics {
+          sender: "QUIC Packet Writer"
+          description:
+            "A QUIC packet is written to the wire based on a request from "
+            "a QUIC stream."
+          trigger:
+            "A request from QUIC stream."
+          data: "Any data sent by the stream."
+          destination: OTHER
+          destination_other: "Any destination choosen by the stream."
+        }
+        policy {
+          cookies_allowed: NO
+          setting: "This feature cannot be disabled in settings."
+          policy_exception_justification:
+            "Essential for network access."
+        }
+        comments:
+          "All requests that are received by QUIC streams have network traffic "
+          "annotation, but the annotation is not passed to the writer function "
+          "due to technial overheads. Please see QuicChromiumClientSession and "
+          "QuicChromiumClientStream classes for references."
+    )");
+
 }  // namespace
 
 QuicChromiumPacketWriter::ReusableIOBuffer::ReusableIOBuffer(size_t capacity)
@@ -108,34 +134,9 @@
 
 WriteResult QuicChromiumPacketWriter::WritePacketToSocketImpl() {
   base::TimeTicks now = base::TimeTicks::Now();
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("quic_chromium_packet_writer", R"(
-        semantics {
-          sender: "QUIC Packet Writer"
-          description:
-            "A QUIC packet is written to the wire based on a request from "
-            "a QUIC stream."
-          trigger:
-            "A request from QUIC stream."
-          data: "Any data sent by the stream."
-          destination: OTHER
-          destination_other: "Any destination choosen by the stream."
-        }
-        policy {
-          cookies_allowed: NO
-          setting: "This feature cannot be disabled in settings."
-          policy_exception_justification:
-            "Essential for network access."
-        }
-        comments:
-          "All requests that are received by QUIC streams have network traffic "
-          "annotation, but the annotation is not passed to the writer function "
-          "due to technial overheads. Please see QuicChromiumClientSession and "
-          "QuicChromiumClientStream classes for references."
-    )");
 
   int rv = socket_->Write(packet_.get(), packet_->size(), write_callback_,
-                          traffic_annotation);
+                          kTrafficAnnotation);
 
   if (MaybeRetryAfterWriteError(rv))
     return WriteResult(WRITE_STATUS_BLOCKED, ERR_IO_PENDING);
diff --git a/net/quic/chromium/quic_http_stream_test.cc b/net/quic/chromium/quic_http_stream_test.cc
index b1a0f683..4d68b36 100644
--- a/net/quic/chromium/quic_http_stream_test.cc
+++ b/net/quic/chromium/quic_http_stream_test.cc
@@ -34,6 +34,7 @@
 #include "net/quic/chromium/quic_chromium_packet_writer.h"
 #include "net/quic/chromium/quic_http_utils.h"
 #include "net/quic/chromium/quic_server_info.h"
+#include "net/quic/chromium/quic_stream_factory.h"
 #include "net/quic/chromium/quic_test_packet_maker.h"
 #include "net/quic/chromium/test_task_runner.h"
 #include "net/quic/core/congestion_control/send_algorithm_interface.h"
diff --git a/net/quic/chromium/quic_proxy_client_socket_unittest.cc b/net/quic/chromium/quic_proxy_client_socket_unittest.cc
index d1c489ea..cd205c9 100644
--- a/net/quic/chromium/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/chromium/quic_proxy_client_socket_unittest.cc
@@ -25,6 +25,7 @@
 #include "net/quic/chromium/quic_chromium_packet_writer.h"
 #include "net/quic/chromium/quic_http_utils.h"
 #include "net/quic/chromium/quic_server_info.h"
+#include "net/quic/chromium/quic_stream_factory.h"
 #include "net/quic/chromium/quic_test_packet_maker.h"
 #include "net/quic/chromium/test_task_runner.h"
 #include "net/quic/core/crypto/null_encrypter.h"
diff --git a/net/quic/chromium/quic_stream_factory.h b/net/quic/chromium/quic_stream_factory.h
index 706c7199..041bd2e 100644
--- a/net/quic/chromium/quic_stream_factory.h
+++ b/net/quic/chromium/quic_stream_factory.h
@@ -77,6 +77,14 @@
 // When a connection is idle for 30 seconds it will be closed.
 const int kIdleConnectionTimeoutSeconds = 30;
 
+// The default maximum time QUIC session could be on non-default network before
+// migrate back to default network.
+const int64_t kMaxTimeOnNonDefaultNetworkSecs = 128;
+
+// The default maximum number of migrations to non default network on path
+// degrading per network. Used in chromium only.
+const int64_t kMaxMigrationsToNonDefaultNetworkOnPathDegrading = 5;
+
 enum QuicPlatformNotification {
   NETWORK_CONNECTED,
   NETWORK_MADE_DEFAULT,
diff --git a/net/quic/core/quic_constants.h b/net/quic/core/quic_constants.h
index c1ac1f0..e61b4d3 100644
--- a/net/quic/core/quic_constants.h
+++ b/net/quic/core/quic_constants.h
@@ -115,14 +115,6 @@
 // The default timeout for a connection until the crypto handshake succeeds.
 const int64_t kMaxTimeForCryptoHandshakeSecs = 10;  // 10 secs.
 
-// The default maximum time QUIC session could be on non-default network before
-// migrate back to default network.
-const int64_t kMaxTimeOnNonDefaultNetworkSecs = 128;
-
-// The default maximum number of migrations to non default network on path
-// degrading per network.
-const int64_t kMaxMigrationsToNonDefaultNetworkOnPathDegrading = 5;
-
 // Default limit on the number of undecryptable packets the connection buffers
 // before the CHLO/SHLO arrive.
 const size_t kDefaultMaxUndecryptablePackets = 10;
diff --git a/net/socket/client_socket_pool_manager.cc b/net/socket/client_socket_pool_manager.cc
index b737ffe..e818616 100644
--- a/net/socket/client_socket_pool_manager.cc
+++ b/net/socket/client_socket_pool_manager.cc
@@ -427,6 +427,8 @@
 
 int InitSocketHandleForTlsConnect(const HostPortPair& endpoint,
                                   HttpNetworkSession* session,
+                                  int request_load_flags,
+                                  RequestPriority request_priority,
                                   const ProxyInfo& proxy_info,
                                   const SSLConfig& ssl_config_for_origin,
                                   const SSLConfig& ssl_config_for_proxy,
@@ -436,8 +438,6 @@
                                   const CompletionCallback& callback) {
   DCHECK(socket_handle);
   HttpRequestHeaders request_extra_headers;
-  int request_load_flags = 0;
-  RequestPriority request_priority = MEDIUM;
   return InitSocketPoolHelper(
       ClientSocketPoolManager::SSL_GROUP, endpoint, request_extra_headers,
       request_load_flags, request_priority, session, proxy_info,
diff --git a/net/socket/client_socket_pool_manager.h b/net/socket/client_socket_pool_manager.h
index df54c05..8837701 100644
--- a/net/socket/client_socket_pool_manager.h
+++ b/net/socket/client_socket_pool_manager.h
@@ -146,6 +146,7 @@
     const OnHostResolutionCallback& resolution_callback,
     const CompletionCallback& callback);
 
+// Deprecated: Please do not use this outside of //net and //services/network.
 // A helper method that uses the passed in proxy information to initialize a
 // ClientSocketHandle with the relevant socket pool. Use this method for
 // a raw socket connection to a host-port pair (that needs to tunnel through
@@ -163,6 +164,7 @@
     ClientSocketHandle* socket_handle,
     const CompletionCallback& callback);
 
+// Deprecated: Please do not use this outside of //net and //services/network.
 // A helper method that uses the passed in proxy information to initialize a
 // ClientSocketHandle with the relevant socket pool. Use this method for
 // a raw socket connection with TLS negotiation to a host-port pair (that needs
@@ -170,6 +172,8 @@
 NET_EXPORT int InitSocketHandleForTlsConnect(
     const HostPortPair& host_port_pair,
     HttpNetworkSession* session,
+    int request_load_flags,
+    RequestPriority request_priority,
     const ProxyInfo& proxy_info,
     const SSLConfig& ssl_config_for_origin,
     const SSLConfig& ssl_config_for_proxy,
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 85d9fc67..bad07ee 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -1212,6 +1212,7 @@
 
 int MockSSLClientSocket::Connect(const CompletionCallback& callback) {
   DCHECK(transport_->socket()->IsConnected());
+  data_->is_connect_data_consumed = true;
   if (data_->connect.result == OK)
     connected_ = true;
   if (data_->connect.mode == ASYNC) {
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 906d6be..dd6a24d 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -360,6 +360,9 @@
   SSLSocketDataProvider(const SSLSocketDataProvider& other);
   ~SSLSocketDataProvider();
 
+  // Returns whether MockConnect data has been consumed.
+  bool ConnectDataConsumed() const { return is_connect_data_consumed; }
+
   // Result for Connect().
   MockConnect connect;
 
@@ -374,6 +377,8 @@
 
   ChannelIDService* channel_id_service;
   base::Optional<NextProtoVector> next_protos_expected_in_ssl_config;
+
+  bool is_connect_data_consumed = false;
 };
 
 // Uses the sequence_number field in the mock reads and writes to
diff --git a/net/ssl/client_cert_identity_unittest.cc b/net/ssl/client_cert_identity_unittest.cc
index fbd60d6..36a5583 100644
--- a/net/ssl/client_cert_identity_unittest.cc
+++ b/net/ssl/client_cert_identity_unittest.cc
@@ -25,7 +25,7 @@
   std::string der_cert;
 
   ASSERT_TRUE(x509_util::CreateSelfSignedCert(
-      key.get(), x509_util::DIGEST_SHA256, "CN=expired", 1,
+      key->key(), x509_util::DIGEST_SHA256, "CN=expired", 1,
       base::Time::UnixEpoch(), base::Time::UnixEpoch(), &der_cert));
   cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
   ASSERT_TRUE(cert);
@@ -34,7 +34,7 @@
   const base::Time now = base::Time::Now();
 
   ASSERT_TRUE(x509_util::CreateSelfSignedCert(
-      key.get(), x509_util::DIGEST_SHA256, "CN=not yet valid", 2,
+      key->key(), x509_util::DIGEST_SHA256, "CN=not yet valid", 2,
       now + base::TimeDelta::FromDays(10), now + base::TimeDelta::FromDays(15),
       &der_cert));
   cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
@@ -42,7 +42,7 @@
   certs.push_back(std::make_unique<FakeClientCertIdentity>(cert, nullptr));
 
   ASSERT_TRUE(x509_util::CreateSelfSignedCert(
-      key.get(), x509_util::DIGEST_SHA256, "CN=older cert", 3,
+      key->key(), x509_util::DIGEST_SHA256, "CN=older cert", 3,
       now - base::TimeDelta::FromDays(5), now + base::TimeDelta::FromDays(5),
       &der_cert));
   cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
@@ -50,7 +50,7 @@
   certs.push_back(std::make_unique<FakeClientCertIdentity>(cert, nullptr));
 
   ASSERT_TRUE(x509_util::CreateSelfSignedCert(
-      key.get(), x509_util::DIGEST_SHA256, "CN=newer cert", 2,
+      key->key(), x509_util::DIGEST_SHA256, "CN=newer cert", 2,
       now - base::TimeDelta::FromDays(3), now + base::TimeDelta::FromDays(5),
       &der_cert));
   cert = X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size());
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 63e48b2..75b2e7a 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -1821,6 +1821,10 @@
   PostMessage(message);
 }
 
+float OutOfProcessInstance::GetToolbarHeightInScreenCoords() {
+  return top_toolbar_height_in_viewport_coords_ * device_scale_;
+}
+
 void OutOfProcessInstance::ProcessPreviewPageInfo(const std::string& url,
                                                   int dest_page_index) {
   DCHECK(IsPrintPreview());
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h
index 99b58b2..3c0a252 100644
--- a/pdf/out_of_process_instance.h
+++ b/pdf/out_of_process_instance.h
@@ -147,6 +147,7 @@
   void IsSelectingChanged(bool is_selecting) override;
   void SelectionChanged(const pp::Rect& left, const pp::Rect& right) override;
   void IsEditModeChanged(bool is_edit_mode) override;
+  float GetToolbarHeightInScreenCoords() override;
 
   // PreviewModeClient::Client implementation.
   void PreviewDocumentLoadComplete() override;
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h
index 049c5b7..216a8f0 100644
--- a/pdf/pdf_engine.h
+++ b/pdf/pdf_engine.h
@@ -270,6 +270,10 @@
 
     // Sets edit mode state.
     virtual void IsEditModeChanged(bool is_edit_mode) {}
+
+    // Gets the height of the top toolbar in screen coordinates. This is
+    // independent of whether it is hidden or not at the moment.
+    virtual float GetToolbarHeightInScreenCoords() = 0;
   };
 
   // Factory method to create an instance of the PDF Engine.
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 31e5d89..feacb2a 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -906,13 +906,58 @@
                                         double* right,
                                         double* bottom) {
   PDFiumEngine* engine = static_cast<PDFiumEngine*>(param);
-  int page_index = engine->GetMostVisiblePage();
+  int page_index = engine->GetVisiblePageIndex(page);
+  if (!engine->PageIndexInBounds(page_index)) {
+    *left = 0;
+    *right = 0;
+    *top = 0;
+    *bottom = 0;
+    return;
+  }
+
   pp::Rect page_view_rect = engine->GetPageContentsRect(page_index);
 
-  *left = page_view_rect.x();
-  *right = page_view_rect.right();
-  *top = page_view_rect.y();
-  *bottom = page_view_rect.bottom();
+  float toolbar_height_in_screen_coords =
+      engine->GetToolbarHeightInScreenCoords();
+
+  float page_width = FPDF_GetPageWidth(page);
+  float page_height = FPDF_GetPageHeight(page);
+
+  // To convert from a screen scale to a page scale, we multiply by
+  // (page_height / page_view_rect.height()) and
+  // (page_width / page_view_rect.width()),
+  // The base point of the page in screen coords is (page_view_rect.x(),
+  // page_view_rect.y()).
+  // Therefore, to convert an x position from screen to page
+  // coords, we use (page_width * (x - base_x) / page_view_rect.width()).
+  // For y positions, (page_height * (y - base_y) / page_view_rect.height()).
+
+  // The top-most y position that can be relied to be visible on the screen is
+  // the bottom of the toolbar, which is y = toolbar_height_in_screen_coords.
+  float screen_top_in_page_coords =
+      page_height * (toolbar_height_in_screen_coords - page_view_rect.y()) /
+      page_view_rect.height();
+  // The bottom-most y position that is visible on the screen is the bottom of
+  // the plugin area, which is y = engine->plugin_size_.height().
+  float screen_bottom_in_page_coords =
+      page_height * (engine->plugin_size_.height() - page_view_rect.y()) /
+      page_view_rect.height();
+  // The left-most x position that is visible on the screen is the left of the
+  // plugin area, which is x = 0.
+  float screen_left_in_page_coords =
+      page_width * (0 - page_view_rect.x()) / page_view_rect.width();
+  // The right-most x position that is visible on the screen is the right of the
+  // plugin area, which is x = engine->plugin_size_.width().
+  float screen_right_in_page_coords =
+      page_width * (engine->plugin_size_.width() - page_view_rect.x()) /
+      page_view_rect.width();
+
+  // Return the edge of the screen or of the page, since we're restricted to
+  // both.
+  *left = std::max(screen_left_in_page_coords, 0.0f);
+  *right = std::min(screen_right_in_page_coords, page_width);
+  *top = std::max(screen_top_in_page_coords, 0.0f);
+  *bottom = std::min(screen_bottom_in_page_coords, page_height);
 }
 
 int PDFiumEngine::Form_GetPlatform(FPDF_FORMFILLINFO* param,
@@ -4039,6 +4084,10 @@
   return index >= 0 && index < static_cast<int>(pages_.size());
 }
 
+float PDFiumEngine::GetToolbarHeightInScreenCoords() {
+  return client_->GetToolbarHeightInScreenCoords();
+}
+
 void PDFiumEngine::Form_Invalidate(FPDF_FORMFILLINFO* param,
                                    FPDF_PAGE page,
                                    double left,
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index c0a4833..1c115cc 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -464,6 +464,10 @@
 
   bool PageIndexInBounds(int index) const;
 
+  // Gets the height of the top toolbar in screen coordinates. This is
+  // independent of whether it is hidden or not at the moment.
+  float GetToolbarHeightInScreenCoords();
+
   void ScheduleTouchTimer(const pp::TouchInputEvent& event);
   void KillTouchTimer(int timer_id);
   void HandleLongPress(const pp::TouchInputEvent& event);
diff --git a/pdf/preview_mode_client.cc b/pdf/preview_mode_client.cc
index c824fdaf..4bfa859 100644
--- a/pdf/preview_mode_client.cc
+++ b/pdf/preview_mode_client.cc
@@ -167,6 +167,10 @@
 
 void PreviewModeClient::CancelBrowserDownload() {}
 
+float PreviewModeClient::GetToolbarHeightInScreenCoords() {
+  return 0.0f;
+}
+
 uint32_t PreviewModeClient::GetBackgroundColor() {
   NOTREACHED();
   return 0;
diff --git a/pdf/preview_mode_client.h b/pdf/preview_mode_client.h
index 807cf80..e1c06529 100644
--- a/pdf/preview_mode_client.h
+++ b/pdf/preview_mode_client.h
@@ -74,6 +74,7 @@
   void FormTextFieldFocusChange(bool in_focus) override;
   bool IsPrintPreview() override;
   void CancelBrowserDownload() override;
+  float GetToolbarHeightInScreenCoords() override;
   uint32_t GetBackgroundColor() override;
 
  private:
diff --git a/remoting/base/rsa_key_pair.cc b/remoting/base/rsa_key_pair.cc
index 943c5636..1e2e84d 100644
--- a/remoting/base/rsa_key_pair.cc
+++ b/remoting/base/rsa_key_pair.cc
@@ -97,7 +97,7 @@
 std::string RsaKeyPair::GenerateCertificate() const {
   std::string der_cert;
   net::x509_util::CreateSelfSignedCert(
-      key_.get(), net::x509_util::DIGEST_SHA256, "CN=chromoting",
+      key_->key(), net::x509_util::DIGEST_SHA256, "CN=chromoting",
       base::RandInt(1, std::numeric_limits<int>::max()), base::Time::Now(),
       base::Time::Now() + base::TimeDelta::FromDays(1), &der_cert);
   return der_cert;
diff --git a/remoting/signaling/xmpp_signal_strategy.cc b/remoting/signaling/xmpp_signal_strategy.cc
index 99fbc1a..1f09bf4 100644
--- a/remoting/signaling/xmpp_signal_strategy.cc
+++ b/remoting/signaling/xmpp_signal_strategy.cc
@@ -190,7 +190,8 @@
       net::SSLConfig(),
       GURL("https://" +
            net::HostPortPair(xmpp_server_config_.host, xmpp_server_config_.port)
-               .ToString()));
+               .ToString()),
+      false /*use_tls*/);
 
   int result = socket_->Connect(base::Bind(
       &Core::OnSocketConnected, base::Unretained(this)));
diff --git a/services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles.cc b/services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles.cc
index f60837a..4c66319 100644
--- a/services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles.cc
+++ b/services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles.cc
@@ -22,6 +22,15 @@
                                       double* y,
                                       double* z,
                                       double* w) {
+  if (std::isnan(alpha_in_degrees)) {
+    // The RelativeOrientationEulerAnglesFusionAlgorithmUsingAccelerometer
+    // algorithm cannot measure rotation around the z-axis because it only
+    // measures the direction of Earth's gravitational field through the
+    // accelerometer. It sets |alpha| to NaN to reflect that. There is no
+    // analogue in the world of quaternions so we set |alpha| to 0 to choose
+    // an arbitrary fixed orientation around the z-axis.
+    alpha_in_degrees = 0.0;
+  }
   double alpha_in_radians = gfx::DegToRad(alpha_in_degrees);
   double beta_in_radians = gfx::DegToRad(beta_in_degrees);
   double gamma_in_radians = gfx::DegToRad(gamma_in_degrees);
diff --git a/services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles_unittest.cc b/services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles_unittest.cc
index 039d5b58..2bd9b05a 100644
--- a/services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles_unittest.cc
+++ b/services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <cmath>
+
 #include "services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
@@ -82,6 +84,47 @@
     EXPECT_NEAR(1.0, x * x + y * y + z * z + w * w, kEpsilon)
         << "on test value " << i;
   }
+
+  // Test when alpha is NAN.
+  for (size_t i = 0; i < euler_angles_in_degrees_test_values.size(); ++i) {
+    if (euler_angles_in_degrees_test_values[i][0] != 0.0) {
+      // Here we need to test when alpha is NAN, it is considered the same as
+      // its value is 0.0 in
+      // OrientationQuaternionFusionAlgorithmUsingEulerAngles fusion sensor
+      // algorithm. So we reuse the test data entries in
+      // |euler_angles_in_degrees_test_values| whose alpha value is 0.0, and
+      // skip other test data that has non-zero alpha values.
+      continue;
+    }
+
+    // alpha
+    reading.orientation_euler.z = NAN;
+    // beta
+    reading.orientation_euler.x = euler_angles_in_degrees_test_values[i][1];
+    // gamma
+    reading.orientation_euler.y = euler_angles_in_degrees_test_values[i][2];
+
+    fake_fusion_sensor_->SetSensorReading(source_type, reading,
+                                          true /* sensor_reading_success */);
+    EXPECT_TRUE(fusion_algorithm_->GetFusedData(source_type, &fused_reading));
+
+    double x = fused_reading.orientation_quat.x;
+    double y = fused_reading.orientation_quat.y;
+    double z = fused_reading.orientation_quat.z;
+    double w = fused_reading.orientation_quat.w;
+
+    EXPECT_NEAR(quaternions_test_values[i][0], x, kEpsilon)
+        << "on test value " << i;
+    EXPECT_NEAR(quaternions_test_values[i][1], y, kEpsilon)
+        << "on test value " << i;
+    EXPECT_NEAR(quaternions_test_values[i][2], z, kEpsilon)
+        << "on test value " << i;
+    EXPECT_NEAR(quaternions_test_values[i][3], w, kEpsilon)
+        << "on test value " << i;
+
+    EXPECT_NEAR(1.0, x * x + y * y + z * z + w * w, kEpsilon)
+        << "on test value " << i;
+  }
 }
 
 }  // namespace device
diff --git a/services/network/proxy_resolving_client_socket.cc b/services/network/proxy_resolving_client_socket.cc
index 3442660..b6d30af 100644
--- a/services/network/proxy_resolving_client_socket.cc
+++ b/services/network/proxy_resolving_client_socket.cc
@@ -31,13 +31,17 @@
 ProxyResolvingClientSocket::ProxyResolvingClientSocket(
     net::HttpNetworkSession* network_session,
     const net::SSLConfig& ssl_config,
-    const GURL& url)
+    const GURL& url,
+    bool use_tls)
     : network_session_(network_session),
+      socket_handle_(std::make_unique<net::ClientSocketHandle>()),
       ssl_config_(ssl_config),
       proxy_resolve_request_(nullptr),
       url_(url),
+      use_tls_(use_tls),
       net_log_(net::NetLogWithSource::Make(network_session_->net_log(),
                                            net::NetLogSourceType::SOCKET)),
+      next_state_(STATE_NONE),
       weak_factory_(this) {
   // TODO(xunjieli): Handle invalid URLs more gracefully (at mojo API layer
   // or when the request is created).
@@ -51,8 +55,8 @@
 int ProxyResolvingClientSocket::Read(net::IOBuffer* buf,
                                      int buf_len,
                                      const net::CompletionCallback& callback) {
-  if (transport_.get() && transport_->socket())
-    return transport_->socket()->Read(buf, buf_len, callback);
+  if (socket_handle_->socket())
+    return socket_handle_->socket()->Read(buf, buf_len, callback);
   return net::ERR_SOCKET_NOT_CONNECTED;
 }
 
@@ -61,21 +65,21 @@
     int buf_len,
     const net::CompletionCallback& callback,
     const net::NetworkTrafficAnnotationTag& traffic_annotation) {
-  if (transport_.get() && transport_->socket())
-    return transport_->socket()->Write(buf, buf_len, callback,
-                                       traffic_annotation);
+  if (socket_handle_->socket())
+    return socket_handle_->socket()->Write(buf, buf_len, callback,
+                                           traffic_annotation);
   return net::ERR_SOCKET_NOT_CONNECTED;
 }
 
 int ProxyResolvingClientSocket::SetReceiveBufferSize(int32_t size) {
-  if (transport_.get() && transport_->socket())
-    return transport_->socket()->SetReceiveBufferSize(size);
+  if (socket_handle_->socket())
+    return socket_handle_->socket()->SetReceiveBufferSize(size);
   return net::ERR_SOCKET_NOT_CONNECTED;
 }
 
 int ProxyResolvingClientSocket::SetSendBufferSize(int32_t size) {
-  if (transport_.get() && transport_->socket())
-    return transport_->socket()->SetSendBufferSize(size);
+  if (socket_handle_->socket())
+    return socket_handle_->socket()->SetSendBufferSize(size);
   return net::ERR_SOCKET_NOT_CONNECTED;
 }
 
@@ -83,29 +87,16 @@
     const net::CompletionCallback& callback) {
   DCHECK(user_connect_callback_.is_null());
 
-  // First try to resolve the proxy.
-  // TODO(xunjieli): Having a null ProxyDelegate is bad. Figure out how to
-  // interact with the new interface for proxy delegate.
-  // https://crbug.com/793071.
-  int net_error = network_session_->proxy_resolution_service()->ResolveProxy(
-      url_, "POST", &proxy_info_,
-      base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxy,
-                          base::Unretained(this)),
-      &proxy_resolve_request_, nullptr /*proxy_delegate*/, net_log_);
-  if (net_error != net::ERR_IO_PENDING) {
-    // Defer execution of ConnectToProxy instead of calling it
-    // directly here for simplicity. From the caller's point of view,
-    // the connect always happens asynchronously.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&ProxyResolvingClientSocket::ConnectToProxy,
-                                  weak_factory_.GetWeakPtr(), net_error));
+  next_state_ = STATE_PROXY_RESOLVE;
+  int result = DoLoop(net::OK);
+  if (result == net::ERR_IO_PENDING) {
+    user_connect_callback_ = callback;
   }
-  user_connect_callback_ = callback;
-  return net::ERR_IO_PENDING;
+  return result;
 }
 
 void ProxyResolvingClientSocket::Disconnect() {
-  CloseTransportSocket();
+  CloseSocket(true /*close_connection*/);
   if (proxy_resolve_request_) {
     network_session_->proxy_resolution_service()->CancelRequest(
         proxy_resolve_request_);
@@ -115,24 +106,24 @@
 }
 
 bool ProxyResolvingClientSocket::IsConnected() const {
-  if (!transport_.get() || !transport_->socket())
+  if (!socket_handle_->socket())
     return false;
-  return transport_->socket()->IsConnected();
+  return socket_handle_->socket()->IsConnected();
 }
 
 bool ProxyResolvingClientSocket::IsConnectedAndIdle() const {
-  if (!transport_.get() || !transport_->socket())
+  if (!socket_handle_->socket())
     return false;
-  return transport_->socket()->IsConnectedAndIdle();
+  return socket_handle_->socket()->IsConnectedAndIdle();
 }
 
 int ProxyResolvingClientSocket::GetPeerAddress(net::IPEndPoint* address) const {
-  if (!transport_.get() || !transport_->socket()) {
+  if (!socket_handle_->socket()) {
     return net::ERR_SOCKET_NOT_CONNECTED;
   }
 
   if (proxy_info_.is_direct())
-    return transport_->socket()->GetPeerAddress(address);
+    return socket_handle_->socket()->GetPeerAddress(address);
 
   net::IPAddress ip_address;
   if (!ip_address.AssignFromIPLiteral(url_.HostNoBrackets())) {
@@ -146,44 +137,48 @@
 
 int ProxyResolvingClientSocket::GetLocalAddress(
     net::IPEndPoint* address) const {
-  if (transport_.get() && transport_->socket())
-    return transport_->socket()->GetLocalAddress(address);
+  if (socket_handle_->socket())
+    return socket_handle_->socket()->GetLocalAddress(address);
   return net::ERR_SOCKET_NOT_CONNECTED;
 }
 
 const net::NetLogWithSource& ProxyResolvingClientSocket::NetLog() const {
-  if (transport_.get() && transport_->socket())
-    return transport_->socket()->NetLog();
+  if (socket_handle_->socket())
+    return socket_handle_->socket()->NetLog();
   return net_log_;
 }
 
 void ProxyResolvingClientSocket::SetSubresourceSpeculation() {
-  if (transport_.get() && transport_->socket())
-    transport_->socket()->SetSubresourceSpeculation();
+  if (socket_handle_->socket())
+    socket_handle_->socket()->SetSubresourceSpeculation();
 }
 
 void ProxyResolvingClientSocket::SetOmniboxSpeculation() {
-  if (transport_.get() && transport_->socket())
-    transport_->socket()->SetOmniboxSpeculation();
+  if (socket_handle_->socket())
+    socket_handle_->socket()->SetOmniboxSpeculation();
 }
 
 bool ProxyResolvingClientSocket::WasEverUsed() const {
-  if (transport_.get() && transport_->socket())
-    return transport_->socket()->WasEverUsed();
+  if (socket_handle_->socket())
+    return socket_handle_->socket()->WasEverUsed();
   return false;
 }
 
 bool ProxyResolvingClientSocket::WasAlpnNegotiated() const {
+  if (socket_handle_->socket())
+    return socket_handle_->socket()->WasAlpnNegotiated();
   return false;
 }
 
 net::NextProto ProxyResolvingClientSocket::GetNegotiatedProtocol() const {
-  if (transport_.get() && transport_->socket())
-    return transport_->socket()->GetNegotiatedProtocol();
+  if (socket_handle_->socket())
+    return socket_handle_->socket()->GetNegotiatedProtocol();
   return net::kProtoUnknown;
 }
 
 bool ProxyResolvingClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) {
+  if (socket_handle_->socket())
+    return socket_handle_->socket()->GetSSLInfo(ssl_info);
   return false;
 }
 
@@ -201,11 +196,67 @@
   NOTIMPLEMENTED();
 }
 
-void ProxyResolvingClientSocket::ConnectToProxy(int net_error) {
-  proxy_resolve_request_ = nullptr;
+void ProxyResolvingClientSocket::OnIOComplete(int result) {
+  DCHECK_NE(net::ERR_IO_PENDING, result);
+  int net_error = DoLoop(result);
+  if (net_error != net::ERR_IO_PENDING)
+    base::ResetAndReturn(&user_connect_callback_).Run(net_error);
+}
 
-  DCHECK_NE(net_error, net::ERR_IO_PENDING);
-  if (net_error == net::OK) {
+int ProxyResolvingClientSocket::DoLoop(int result) {
+  DCHECK_NE(next_state_, STATE_NONE);
+  int rv = result;
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_PROXY_RESOLVE:
+        DCHECK_EQ(net::OK, rv);
+        rv = DoProxyResolve();
+        break;
+      case STATE_PROXY_RESOLVE_COMPLETE:
+        rv = DoProxyResolveComplete(rv);
+        break;
+      case STATE_INIT_CONNECTION:
+        DCHECK_EQ(net::OK, rv);
+        rv = DoInitConnection();
+        break;
+      case STATE_INIT_CONNECTION_COMPLETE:
+        rv = DoInitConnectionComplete(rv);
+        break;
+      case STATE_RESTART_TUNNEL_AUTH:
+        rv = DoRestartTunnelAuth(rv);
+        break;
+      case STATE_RESTART_TUNNEL_AUTH_COMPLETE:
+        rv = DoRestartTunnelAuthComplete(rv);
+        break;
+      default:
+        NOTREACHED() << "bad state";
+        rv = net::ERR_FAILED;
+        break;
+    }
+  } while (rv != net::ERR_IO_PENDING && next_state_ != STATE_NONE);
+  return rv;
+}
+
+int ProxyResolvingClientSocket::DoProxyResolve() {
+  next_state_ = STATE_PROXY_RESOLVE_COMPLETE;
+  // TODO(xunjieli): Having a null ProxyDelegate is bad. Figure out how to
+  // interact with the new interface for proxy delegate.
+  // https://crbug.com/793071.
+  // base::Unretained(this) is safe because resolution request is canceled when
+  // |proxy_resolve_request_| is destroyed.
+  return network_session_->proxy_resolution_service()->ResolveProxy(
+      url_, "POST", &proxy_info_,
+      base::BindRepeating(&ProxyResolvingClientSocket::OnIOComplete,
+                          base::Unretained(this)),
+      &proxy_resolve_request_, nullptr /*proxy_delegate*/, net_log_);
+}
+
+int ProxyResolvingClientSocket::DoProxyResolveComplete(int result) {
+  proxy_resolve_request_ = nullptr;
+  if (result == net::OK) {
+    next_state_ = STATE_INIT_CONNECTION;
     // Removes unsupported proxies from the list. Currently, this removes
     // just the SCHEME_QUIC proxy, which doesn't yet support tunneling.
     // TODO(xunjieli): Allow QUIC proxy once it supports tunneling.
@@ -217,82 +268,119 @@
     if (proxy_info_.is_empty()) {
       // No proxies/direct to choose from. This happens when we don't support
       // any of the proxies in the returned list.
-      net_error = net::ERR_NO_SUPPORTED_PROXIES;
+      result = net::ERR_NO_SUPPORTED_PROXIES;
     }
   }
+  return result;
+}
 
-  if (net_error != net::OK) {
-    CloseTransportSocket();
-    base::ResetAndReturn(&user_connect_callback_).Run(net_error);
-    return;
-  }
+int ProxyResolvingClientSocket::DoInitConnection() {
+  DCHECK(!socket_handle_->socket());
 
-  transport_.reset(new net::ClientSocketHandle);
+  next_state_ = STATE_INIT_CONNECTION_COMPLETE;
+
   // Now that the proxy is resolved, issue a socket connect.
   net::HostPortPair host_port_pair = net::HostPortPair::FromURL(url_);
   // Ignore socket limit set by socket pool for this type of socket.
   int request_load_flags = net::LOAD_IGNORE_LIMITS;
   net::RequestPriority request_priority = net::MAXIMUM_PRIORITY;
 
-  net_error = net::InitSocketHandleForRawConnect(
+  // base::Unretained(this) is safe because request is canceled when
+  // |socket_handle_| is destroyed.
+  if (use_tls_) {
+    return net::InitSocketHandleForTlsConnect(
+        host_port_pair, network_session_, request_load_flags, request_priority,
+        proxy_info_, ssl_config_, ssl_config_, net::PRIVACY_MODE_DISABLED,
+        net_log_, socket_handle_.get(),
+        base::BindRepeating(&ProxyResolvingClientSocket::OnIOComplete,
+                            base::Unretained(this)));
+  }
+  return net::InitSocketHandleForRawConnect(
       host_port_pair, network_session_, request_load_flags, request_priority,
       proxy_info_, ssl_config_, ssl_config_, net::PRIVACY_MODE_DISABLED,
-      net_log_, transport_.get(),
-      base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxyDone,
+      net_log_, socket_handle_.get(),
+      base::BindRepeating(&ProxyResolvingClientSocket::OnIOComplete,
                           base::Unretained(this)));
-  if (net_error != net::ERR_IO_PENDING) {
-    // Since this method is always called asynchronously. it is OK to call
-    // ConnectToProxyDone synchronously.
-    ConnectToProxyDone(net_error);
-  }
 }
 
-void ProxyResolvingClientSocket::ConnectToProxyDone(int net_error) {
-  if (net_error != net::OK) {
-    // If the connection fails, try another proxy.
-    net_error = ReconsiderProxyAfterError(net_error);
+int ProxyResolvingClientSocket::DoInitConnectionComplete(int result) {
+  if (result == net::ERR_PROXY_AUTH_REQUESTED) {
+    if (use_tls_) {
+      // Put the in-progress HttpProxyClientSocket into |socket_handle_| to
+      // do tunnel auth. After auth completes, it's important to reset
+      // |socket_handle_|, so it doesn't have a HttpProxyClientSocket when the
+      // code expects an SSLClientSocket. The tunnel restart code is careful to
+      // put it back to the socket pool before returning control to the rest of
+      // this class.
+      socket_handle_.reset(
+          socket_handle_->release_pending_http_proxy_connection());
+    }
+    next_state_ = STATE_RESTART_TUNNEL_AUTH;
+    return result;
+  }
+
+  if (result != net::OK) {
     // ReconsiderProxyAfterError either returns an error (in which case it is
     // not reconsidering a proxy) or returns ERR_IO_PENDING if it is considering
     // another proxy.
-    DCHECK_NE(net_error, net::OK);
-    if (net_error == net::ERR_IO_PENDING) {
-      // Proxy reconsideration pending. Return.
-      return;
-    }
-    CloseTransportSocket();
-  } else {
-    network_session_->proxy_resolution_service()->ReportSuccess(proxy_info_,
-                                                                nullptr);
+    return ReconsiderProxyAfterError(result);
   }
-  base::ResetAndReturn(&user_connect_callback_).Run(net_error);
+
+  network_session_->proxy_resolution_service()->ReportSuccess(proxy_info_,
+                                                              nullptr);
+  return net::OK;
 }
 
-void ProxyResolvingClientSocket::CloseTransportSocket() {
-  if (transport_.get() && transport_->socket())
-    transport_->socket()->Disconnect();
-  transport_.reset();
+int ProxyResolvingClientSocket::DoRestartTunnelAuth(int result) {
+  DCHECK_EQ(net::ERR_PROXY_AUTH_REQUESTED, result);
+
+  net::ProxyClientSocket* proxy_socket =
+      static_cast<net::ProxyClientSocket*>(socket_handle_->socket());
+
+  if (proxy_socket->GetAuthController() &&
+      proxy_socket->GetAuthController()->HaveAuth()) {
+    next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE;
+    // base::Unretained(this) is safe because |proxy_socket| is owned by this.
+    return proxy_socket->RestartWithAuth(base::BindRepeating(
+        &ProxyResolvingClientSocket::OnIOComplete, base::Unretained(this)));
+  }
+  // This socket is unusable if the underlying authentication handler doesn't
+  // already have credentials.  It is possible to overcome this hurdle and
+  // finish the handshake if this class exposes an interface for an embedder to
+  // supply credentials.
+  CloseSocket(true /*close_connection*/);
+  return result;
 }
 
-// This method reconsiders the proxy on certain errors. If it does
-// reconsider a proxy it always returns ERR_IO_PENDING and posts a call to
-// ConnectToProxy with the result of the reconsideration.
+int ProxyResolvingClientSocket::DoRestartTunnelAuthComplete(int result) {
+  if (result == net::ERR_PROXY_AUTH_REQUESTED) {
+    // Handle multi-round auth challenge.
+    next_state_ = STATE_RESTART_TUNNEL_AUTH;
+    return result;
+  }
+  if (result == net::OK) {
+    CloseSocket(false /*close_connection*/);
+    // Now that the HttpProxyClientSocket is connected, release it as an idle
+    // socket into the pool and start the connection process from the beginning.
+    next_state_ = STATE_INIT_CONNECTION;
+    return net::OK;
+  }
+  CloseSocket(true /*close_connection*/);
+  return ReconsiderProxyAfterError(result);
+}
+
+void ProxyResolvingClientSocket::CloseSocket(bool close_connection) {
+  if (close_connection && socket_handle_->socket())
+    socket_handle_->socket()->Disconnect();
+  socket_handle_->Reset();
+}
+
 int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) {
+  DCHECK(!socket_handle_->socket());
   DCHECK(!proxy_resolve_request_);
   DCHECK_NE(error, net::OK);
   DCHECK_NE(error, net::ERR_IO_PENDING);
 
-  if (error == net::ERR_PROXY_AUTH_REQUESTED) {
-    net::ProxyClientSocket* proxy_socket =
-        static_cast<net::ProxyClientSocket*>(transport_->socket());
-
-    if (proxy_socket->GetAuthController()->HaveAuth()) {
-      return proxy_socket->RestartWithAuth(
-          base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxyDone,
-                              base::Unretained(this)));
-    }
-    return error;
-  }
-
   // Check if the error was a proxy failure.
   if (!net::CanFalloverToNextProxy(&error))
     return error;
@@ -307,14 +395,8 @@
   if (!proxy_info_.Fallback(error, net_log_))
     return error;
 
-  CloseTransportSocket();
-
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&ProxyResolvingClientSocket::ConnectToProxy,
-                                weak_factory_.GetWeakPtr(), net::OK));
-  // Since we potentially have another try to go, set the return code code to
-  // ERR_IO_PENDING.
-  return net::ERR_IO_PENDING;
+  next_state_ = STATE_INIT_CONNECTION;
+  return net::OK;
 }
 
 }  // namespace network
diff --git a/services/network/proxy_resolving_client_socket.h b/services/network/proxy_resolving_client_socket.h
index 7aa6185..6616250 100644
--- a/services/network/proxy_resolving_client_socket.h
+++ b/services/network/proxy_resolving_client_socket.h
@@ -50,10 +50,12 @@
   // any sensitive data (like embedded usernames and passwords), and local data
   // (i.e. reference fragment) will be sanitized by
   // net::ProxyResolutionService::ResolveProxyHelper() before the url is
-  // disclosed to the proxy. |network_session| must outlive |this|.
+  // disclosed to the proxy. If |use_tls|, this will try to do a tls connect
+  // instead of a regular tcp connect. |network_session| must outlive |this|.
   ProxyResolvingClientSocket(net::HttpNetworkSession* network_session,
                              const net::SSLConfig& ssl_config,
-                             const GURL& url);
+                             const GURL& url,
+                             bool use_tls);
   ~ProxyResolvingClientSocket() override;
 
   // net::StreamSocket implementation.
@@ -88,29 +90,53 @@
   void ApplySocketTag(const net::SocketTag& tag) override;
 
  private:
+  enum State {
+    STATE_PROXY_RESOLVE,
+    STATE_PROXY_RESOLVE_COMPLETE,
+    STATE_INIT_CONNECTION,
+    STATE_INIT_CONNECTION_COMPLETE,
+    STATE_RESTART_TUNNEL_AUTH,
+    STATE_RESTART_TUNNEL_AUTH_COMPLETE,
+    STATE_DONE,
+    STATE_NONE,
+  };
+
   FRIEND_TEST_ALL_PREFIXES(ProxyResolvingClientSocketTest, ConnectToProxy);
   FRIEND_TEST_ALL_PREFIXES(ProxyResolvingClientSocketTest, ReadWriteErrors);
+  FRIEND_TEST_ALL_PREFIXES(ProxyResolvingClientSocketTest,
+                           ResetSocketAfterTunnelAuth);
 
-  void ConnectToProxy(int net_error);
-  void ConnectToProxyDone(int net_error);
+  void OnIOComplete(int result);
 
-  void CloseTransportSocket();
+  int DoLoop(int result);
+  int DoProxyResolve();
+  int DoProxyResolveComplete(int result);
+  int DoInitConnection();
+  int DoInitConnectionComplete(int result);
+  int DoRestartTunnelAuth(int result);
+  int DoRestartTunnelAuthComplete(int result);
+
+  void CloseSocket(bool close_connection);
+
   int ReconsiderProxyAfterError(int error);
 
   net::HttpNetworkSession* network_session_;
 
-  // The transport socket.
-  std::unique_ptr<net::ClientSocketHandle> transport_;
+  std::unique_ptr<net::ClientSocketHandle> socket_handle_;
 
   const net::SSLConfig ssl_config_;
   net::ProxyResolutionService::Request* proxy_resolve_request_;
   net::ProxyInfo proxy_info_;
   const GURL url_;
+  const bool use_tls_;
+
   net::NetLogWithSource net_log_;
 
   // The callback passed to Connect().
   net::CompletionCallback user_connect_callback_;
 
+  State next_state_;
+
   base::WeakPtrFactory<ProxyResolvingClientSocket> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ProxyResolvingClientSocket);
diff --git a/services/network/proxy_resolving_client_socket_factory.cc b/services/network/proxy_resolving_client_socket_factory.cc
index 1652aaa..8eaa859 100644
--- a/services/network/proxy_resolving_client_socket_factory.cc
+++ b/services/network/proxy_resolving_client_socket_factory.cc
@@ -70,7 +70,8 @@
 std::unique_ptr<ProxyResolvingClientSocket>
 ProxyResolvingClientSocketFactory::CreateSocket(
     const net::SSLConfig& ssl_config,
-    const GURL& url) {
+    const GURL& url,
+    bool use_tls) {
   // |request_context|'s HttpAuthCache might have updates. For example, a user
   // might have since entered proxy credentials. Clear the http auth of
   // |network_session_| and copy over the data from |request_context|'s auth
@@ -83,7 +84,7 @@
           ->http_auth_cache();
   network_session_->http_auth_cache()->UpdateAllFrom(*other_auth_cache);
   return std::make_unique<ProxyResolvingClientSocket>(network_session_.get(),
-                                                      ssl_config, url);
+                                                      ssl_config, url, use_tls);
 }
 
 }  // namespace network
diff --git a/services/network/proxy_resolving_client_socket_factory.h b/services/network/proxy_resolving_client_socket_factory.h
index f722d06..cf0c726 100644
--- a/services/network/proxy_resolving_client_socket_factory.h
+++ b/services/network/proxy_resolving_client_socket_factory.h
@@ -39,10 +39,10 @@
   // doesn't need to explicitly sanitize the url, any sensitive data (like
   // embedded usernames and passwords), and local data (i.e. reference fragment)
   // will be sanitized by net::ProxyService::ResolveProxyHelper() before the url
-  // is disclosed to the proxy.
-  std::unique_ptr<ProxyResolvingClientSocket> CreateSocket(
-      const net::SSLConfig& ssl_config,
-      const GURL& url);
+  // is disclosed to the proxy. If |use_tls|, tls connect will be used in
+  // addition to tcp connect.
+  std::unique_ptr<ProxyResolvingClientSocket>
+  CreateSocket(const net::SSLConfig& ssl_config, const GURL& url, bool use_tls);
 
  private:
   std::unique_ptr<net::HttpNetworkSession> network_session_;
diff --git a/services/network/proxy_resolving_client_socket_unittest.cc b/services/network/proxy_resolving_client_socket_unittest.cc
index 0ebd37a..ad24ac2 100644
--- a/services/network/proxy_resolving_client_socket_unittest.cc
+++ b/services/network/proxy_resolving_client_socket_unittest.cc
@@ -20,6 +20,7 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/proxy_resolution/mock_proxy_resolver.h"
 #include "net/proxy_resolution/proxy_config_service_fixed.h"
+#include "net/proxy_resolution/proxy_config_with_annotation.h"
 #include "net/proxy_resolution/proxy_resolution_service.h"
 #include "net/socket/client_socket_pool_manager.h"
 #include "net/socket/socket_test_util.h"
@@ -50,10 +51,13 @@
 
 }  // namespace
 
-class ProxyResolvingClientSocketTest : public testing::Test {
+class ProxyResolvingClientSocketTest
+    : public testing::Test,
+      public testing::WithParamInterface<bool> {
  protected:
   ProxyResolvingClientSocketTest()
-      : context_with_proxy_("PROXY bad:99; PROXY maybe:80; DIRECT") {}
+      : context_with_proxy_("PROXY bad:99; PROXY maybe:80; DIRECT"),
+        use_tls_(GetParam()) {}
 
   ~ProxyResolvingClientSocketTest() override {}
 
@@ -65,12 +69,17 @@
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   TestURLRequestContextWithProxy context_with_proxy_;
+  const bool use_tls_;
 };
 
+INSTANTIATE_TEST_CASE_P(/* no prefix */,
+                        ProxyResolvingClientSocketTest,
+                        ::testing::Bool());
+
 // Tests that the global socket pool limit
 // (ClientSocketPoolManager::max_sockets_per_group) doesn't apply to this
 // type of sockets.
-TEST_F(ProxyResolvingClientSocketTest, SocketLimitNotApply) {
+TEST_P(ProxyResolvingClientSocketTest, SocketLimitNotApply) {
   const int kNumSockets = net::ClientSocketPoolManager::max_sockets_per_group(
                               net::HttpNetworkSession::NORMAL_SOCKET_POOL) +
                           10;
@@ -82,11 +91,15 @@
                      "Proxy-Connection: keep-alive\r\n\r\n")};
   net::MockRead reads[] = {net::MockRead("HTTP/1.1 200 Success\r\n\r\n")};
   std::vector<std::unique_ptr<net::StaticSocketDataProvider>> socket_data;
+  std::vector<std::unique_ptr<net::SSLSocketDataProvider>> ssl_data;
   for (int i = 0; i < kNumSockets; ++i) {
     socket_data.push_back(std::make_unique<net::StaticSocketDataProvider>(
         reads, arraysize(reads), writes, arraysize(writes)));
     socket_data[i]->set_connect_data(net::MockConnect(net::ASYNC, net::OK));
     socket_factory.AddSocketDataProvider(socket_data[i].get());
+    ssl_data.push_back(
+        std::make_unique<net::SSLSocketDataProvider>(net::ASYNC, net::OK));
+    socket_factory.AddSSLSocketDataProvider(ssl_data[i].get());
   }
 
   ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
@@ -95,7 +108,7 @@
   for (int i = 0; i < kNumSockets; ++i) {
     std::unique_ptr<ProxyResolvingClientSocket> socket =
         proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
-                                                    kDestination);
+                                                    kDestination, use_tls_);
     net::TestCompletionCallback callback;
     int status = socket->Connect(callback.callback());
     EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
@@ -104,10 +117,11 @@
   for (int i = 0; i < kNumSockets; ++i) {
     EXPECT_TRUE(socket_data[i]->AllReadDataConsumed());
     EXPECT_TRUE(socket_data[i]->AllWriteDataConsumed());
+    EXPECT_EQ(use_tls_, ssl_data[i]->ConnectDataConsumed());
   }
 }
 
-TEST_F(ProxyResolvingClientSocketTest, ConnectError) {
+TEST_P(ProxyResolvingClientSocketTest, ConnectError) {
   const struct TestData {
     // Whether the error is encountered synchronously as opposed to
     // asynchronously.
@@ -136,7 +150,7 @@
         &socket_factory, context.get());
     std::unique_ptr<ProxyResolvingClientSocket> socket =
         proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
-                                                    kDestination);
+                                                    kDestination, use_tls_);
     net::TestCompletionCallback callback;
     int status = socket->Connect(callback.callback());
     EXPECT_EQ(net::ERR_IO_PENDING, status);
@@ -152,7 +166,7 @@
 }
 
 // Tests that the connection is established to the proxy.
-TEST_F(ProxyResolvingClientSocketTest, ConnectToProxy) {
+TEST_P(ProxyResolvingClientSocketTest, ConnectToProxy) {
   const GURL kDestination("https://example.com:443");
   // Use a different port than that of |kDestination|.
   const int kProxyPort = 8009;
@@ -171,6 +185,9 @@
         net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
                        "Host: example.com:443\r\n"
                        "Proxy-Connection: keep-alive\r\n\r\n")};
+    net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+    socket_factory.AddSSLSocketDataProvider(&ssl_socket);
+
     net::StaticSocketDataProvider socket_data(reads, arraysize(reads), writes,
                                               arraysize(writes));
     net::IPEndPoint remote_addr(net::IPAddress(127, 0, 0, 1),
@@ -183,7 +200,7 @@
         &socket_factory, context.get());
     std::unique_ptr<ProxyResolvingClientSocket> socket =
         proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
-                                                    kDestination);
+                                                    kDestination, use_tls_);
     net::TestCompletionCallback callback;
     int status = socket->Connect(callback.callback());
     EXPECT_EQ(net::ERR_IO_PENDING, status);
@@ -196,16 +213,42 @@
       // proxy, so call private member to make sure address is correct.
       EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, status);
       status =
-          socket->transport_->socket()->GetPeerAddress(&actual_remote_addr);
+          socket->socket_handle_->socket()->GetPeerAddress(&actual_remote_addr);
     }
     EXPECT_EQ(net::OK, status);
     EXPECT_EQ(remote_addr.ToString(), actual_remote_addr.ToString());
+    EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
   }
 }
 
+TEST_P(ProxyResolvingClientSocketTest, SocketDestroyedBeforeConnectComplete) {
+  const GURL kDestination("https://example.com:443");
+  net::StaticSocketDataProvider socket_data;
+  socket_data.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+
+  net::MockClientSocketFactory socket_factory;
+  socket_factory.AddSocketDataProvider(&socket_data);
+  socket_factory.AddSSLSocketDataProvider(&ssl_socket);
+
+  auto context = std::make_unique<TestURLRequestContextWithProxy>("DIRECT");
+
+  ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
+      &socket_factory, context.get());
+  std::unique_ptr<ProxyResolvingClientSocket> socket =
+      proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
+                                                  kDestination, use_tls_);
+  net::TestCompletionCallback callback;
+  int status = socket->Connect(callback.callback());
+  EXPECT_EQ(net::ERR_IO_PENDING, status);
+  socket.reset();
+  // Makes sure there is no UAF and socket request is canceled properly.
+  base::RunLoop().RunUntilIdle();
+}
+
 // Tests that connection itself is successful but an error occurred during
 // Read()/Write().
-TEST_F(ProxyResolvingClientSocketTest, ReadWriteErrors) {
+TEST_P(ProxyResolvingClientSocketTest, ReadWriteErrors) {
   const GURL kDestination("http://example.com:80");
   const struct TestData {
     // Whether there is a read error as opposed to a write error.
@@ -254,12 +297,14 @@
     socket_data.set_connect_data(
         net::MockConnect(net::ASYNC, net::OK, remote_addr));
     net::MockClientSocketFactory socket_factory;
+    net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+    socket_factory.AddSSLSocketDataProvider(&ssl_socket);
     socket_factory.AddSocketDataProvider(&socket_data);
     ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
         &socket_factory, context.get());
     std::unique_ptr<ProxyResolvingClientSocket> socket =
         proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
-                                                    kDestination);
+                                                    kDestination, use_tls_);
     net::TestCompletionCallback callback;
     int status = socket->Connect(callback.callback());
     EXPECT_EQ(net::ERR_IO_PENDING, status);
@@ -272,7 +317,7 @@
       // proxy, so call private member to make sure address is correct.
       EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, status);
       status =
-          socket->transport_->socket()->GetPeerAddress(&actual_remote_addr);
+          socket->socket_handle_->socket()->GetPeerAddress(&actual_remote_addr);
     }
     EXPECT_EQ(net::OK, status);
     EXPECT_EQ(remote_addr.ToString(), actual_remote_addr.ToString());
@@ -298,10 +343,11 @@
     EXPECT_EQ(net::ERR_FAILED, read_write_result);
     EXPECT_TRUE(socket_data.AllReadDataConsumed());
     EXPECT_TRUE(socket_data.AllWriteDataConsumed());
+    EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
   }
 }
 
-TEST_F(ProxyResolvingClientSocketTest, ReportsBadProxies) {
+TEST_P(ProxyResolvingClientSocketTest, ReportsBadProxies) {
   const GURL kDestination("https://example.com:443");
   net::MockClientSocketFactory socket_factory;
 
@@ -319,12 +365,14 @@
                                              arraysize(writes));
   socket_data2.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
   socket_factory.AddSocketDataProvider(&socket_data2);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  socket_factory.AddSSLSocketDataProvider(&ssl_socket);
 
   ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
       &socket_factory, &context_with_proxy_);
   std::unique_ptr<ProxyResolvingClientSocket> socket =
       proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
-                                                  kDestination);
+                                                  kDestination, use_tls_);
   net::TestCompletionCallback callback;
   int status = socket->Connect(callback.callback());
   EXPECT_EQ(net::ERR_IO_PENDING, status);
@@ -337,9 +385,127 @@
   EXPECT_EQ(1u, retry_info.size());
   net::ProxyRetryInfoMap::const_iterator iter = retry_info.find("bad:99");
   EXPECT_TRUE(iter != retry_info.end());
+  EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
 }
 
-TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Lookup) {
+TEST_P(ProxyResolvingClientSocketTest, ResetSocketAfterTunnelAuth) {
+  net::MockClientSocketFactory socket_factory;
+  const GURL kDestination("https://example.com:443");
+
+  // Initial connect without credentials. The server responds with a 407.
+  net::MockWrite kConnectWrites1[] = {
+      net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
+                     "Host: example.com:443\r\n"
+                     "Proxy-Connection: keep-alive\r\n"
+                     "\r\n")};
+  net::MockRead kConnectReads1[] = {
+      net::MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"
+                    "Proxy-Authenticate: Basic realm=\"test_realm\"\r\n"
+                    "\r\n")};
+
+  net::StaticSocketDataProvider kSocketData1(
+      kConnectReads1, arraysize(kConnectReads1), kConnectWrites1,
+      arraysize(kConnectWrites1));
+  socket_factory.AddSocketDataProvider(&kSocketData1);
+
+  ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
+      &socket_factory, &context_with_proxy_);
+  std::unique_ptr<ProxyResolvingClientSocket> socket =
+      proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
+                                                  kDestination, use_tls_);
+  net::TestCompletionCallback callback;
+  int status = socket->Connect(callback.callback());
+  EXPECT_THAT(callback.GetResult(status),
+              net::test::IsError(net::ERR_PROXY_AUTH_REQUESTED));
+  // Make sure |socket_handle_| is closed appropriately.
+  EXPECT_FALSE(socket->socket_handle_->socket());
+}
+
+TEST_P(ProxyResolvingClientSocketTest, MultiroundAuth) {
+  net::MockClientSocketFactory socket_factory;
+  const GURL kDestination("https://example.com:443");
+
+  // Initial connect without credentials. The server responds with a 407.
+  net::MockWrite kConnectWrites1[] = {
+      net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
+                     "Host: example.com:443\r\n"
+                     "Proxy-Connection: keep-alive\r\n"
+                     "\r\n")};
+  net::MockRead kConnectReads1[] = {
+      net::MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"
+                    "Proxy-Authenticate: Basic realm=\"test_realm\"\r\n"
+                    "\r\n")};
+
+  // Second connect attempt includes credentials for test_realm.
+  net::MockWrite kConnectWrites2[] = {
+      net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
+                     "Host: example.com:443\r\n"
+                     "Proxy-Connection: keep-alive\r\n"
+                     "Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==\r\n"
+                     "\r\n")};
+  net::MockRead kConnectReads2[] = {
+      net::MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"
+                    "Proxy-Authenticate: Basic realm=\"test_realm2\"\r\n"
+                    "\r\n")};
+
+  // Third connect attempt include credentials for test_realm2.
+  net::MockWrite kConnectWrites3[] = {
+      net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
+                     "Host: example.com:443\r\n"
+                     "Proxy-Connection: keep-alive\r\n"
+                     "Proxy-Authorization: Basic dXNlcjI6cGFzc3dvcmQy\r\n"
+                     "\r\n")};
+  net::MockRead kConnectReads3[] = {
+      net::MockRead("HTTP/1.1 200 Success\r\n\r\n")};
+
+  net::StaticSocketDataProvider kSocketData1(
+      kConnectReads1, arraysize(kConnectReads1), kConnectWrites1,
+      arraysize(kConnectWrites1));
+  socket_factory.AddSocketDataProvider(&kSocketData1);
+
+  net::StaticSocketDataProvider kSocketData2(
+      kConnectReads2, arraysize(kConnectReads2), kConnectWrites2,
+      arraysize(kConnectWrites2));
+  socket_factory.AddSocketDataProvider(&kSocketData2);
+  net::StaticSocketDataProvider kSocketData3(
+      kConnectReads3, arraysize(kConnectReads3), kConnectWrites3,
+      arraysize(kConnectWrites3));
+  socket_factory.AddSocketDataProvider(&kSocketData3);
+
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  socket_factory.AddSSLSocketDataProvider(&ssl_socket);
+
+  net::HttpAuthCache* auth_cache =
+      context_with_proxy_.http_transaction_factory()
+          ->GetSession()
+          ->http_auth_cache();
+
+  auth_cache->Add(GURL("http://bad:99"), "test_realm",
+                  net::HttpAuth::AUTH_SCHEME_BASIC,
+                  "Basic realm=\"test_realm\"",
+                  net::AuthCredentials(base::ASCIIToUTF16("user"),
+                                       base::ASCIIToUTF16("password")),
+                  std::string());
+
+  auth_cache->Add(GURL("http://bad:99"), "test_realm2",
+                  net::HttpAuth::AUTH_SCHEME_BASIC,
+                  "Basic realm=\"test_realm2\"",
+                  net::AuthCredentials(base::ASCIIToUTF16("user2"),
+                                       base::ASCIIToUTF16("password2")),
+                  std::string());
+
+  ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
+      &socket_factory, &context_with_proxy_);
+  std::unique_ptr<ProxyResolvingClientSocket> socket =
+      proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
+                                                  kDestination, use_tls_);
+  net::TestCompletionCallback callback;
+  int status = socket->Connect(callback.callback());
+  EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
+  EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
+}
+
+TEST_P(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Lookup) {
   net::MockClientSocketFactory socket_factory;
   const GURL kDestination("https://example.com:443");
 
@@ -373,6 +539,8 @@
       kConnectReads2, arraysize(kConnectReads2), kConnectWrites2,
       arraysize(kConnectWrites2));
   socket_factory.AddSocketDataProvider(&kSocketData2);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  socket_factory.AddSSLSocketDataProvider(&ssl_socket);
 
   net::HttpAuthCache* auth_cache =
       context_with_proxy_.http_transaction_factory()
@@ -393,16 +561,17 @@
       &socket_factory, &context_with_proxy_);
   std::unique_ptr<ProxyResolvingClientSocket> socket =
       proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
-                                                  kDestination);
+                                                  kDestination, use_tls_);
   net::TestCompletionCallback callback;
   int status = socket->Connect(callback.callback());
   EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
+  EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
 }
 
 // Make sure that if HttpAuthCache is updated e.g through normal URLRequests,
 // ProxyResolvingClientSocketFactory uses the latest cache for creating new
 // sockets.
-TEST_F(ProxyResolvingClientSocketTest, FactoryUsesLatestHTTPAuthCache) {
+TEST_P(ProxyResolvingClientSocketTest, FactoryUsesLatestHTTPAuthCache) {
   net::MockClientSocketFactory socket_factory;
   ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
       &socket_factory, &context_with_proxy_);
@@ -449,16 +618,19 @@
       kConnectReads, arraysize(kConnectReads), kConnectWrites,
       arraysize(kConnectWrites));
   socket_factory.AddSocketDataProvider(&kSocketData);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  socket_factory.AddSSLSocketDataProvider(&ssl_socket);
 
   std::unique_ptr<ProxyResolvingClientSocket> socket =
       proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
-                                                  kDestination);
+                                                  kDestination, use_tls_);
   net::TestCompletionCallback callback;
   int status = socket->Connect(callback.callback());
   EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
+  EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
 }
 
-TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Preemptive) {
+TEST_P(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Preemptive) {
   net::MockClientSocketFactory socket_factory;
   const GURL kDestination("https://example.com:443");
 
@@ -476,6 +648,8 @@
       kConnectReads, arraysize(kConnectReads), kConnectWrites,
       arraysize(kConnectWrites));
   socket_factory.AddSocketDataProvider(&kSocketData);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  socket_factory.AddSSLSocketDataProvider(&ssl_socket);
 
   net::HttpAuthCache* auth_cache =
       context_with_proxy_.http_transaction_factory()
@@ -493,14 +667,15 @@
       &socket_factory, &context_with_proxy_);
   std::unique_ptr<ProxyResolvingClientSocket> socket =
       proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
-                                                  kDestination);
+                                                  kDestination, use_tls_);
 
   net::TestCompletionCallback callback;
   int status = socket->Connect(callback.callback());
   EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
+  EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
 }
 
-TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_NoCredentials) {
+TEST_P(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_NoCredentials) {
   net::MockClientSocketFactory socket_factory;
   const GURL kDestination("https://example.com:443");
 
@@ -524,7 +699,7 @@
       &socket_factory, &context_with_proxy_);
   std::unique_ptr<ProxyResolvingClientSocket> socket =
       proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
-                                                  kDestination);
+                                                  kDestination, use_tls_);
 
   net::TestCompletionCallback callback;
   int status = socket->Connect(callback.callback());
@@ -532,7 +707,7 @@
 }
 
 // Make sure that url is sanitized before it is disclosed to the proxy.
-TEST_F(ProxyResolvingClientSocketTest, URLSanitized) {
+TEST_P(ProxyResolvingClientSocketTest, URLSanitized) {
   GURL url("http://username:password@www.example.com:79/?ref#hash#hash");
 
   auto context = std::make_unique<net::TestURLRequestContext>(true);
@@ -555,7 +730,8 @@
   ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
       nullptr, context.get());
   std::unique_ptr<ProxyResolvingClientSocket> socket =
-      proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(), url);
+      proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(), url,
+                                                  use_tls_);
   net::TestCompletionCallback callback;
   int status = socket->Connect(callback.callback());
   EXPECT_EQ(net::ERR_IO_PENDING, status);
@@ -572,18 +748,57 @@
             resolver.pending_jobs()[0]->url());
 }
 
+// Tests that socket is destroyed before proxy resolution can complete
+// asynchronously.
+TEST_P(ProxyResolvingClientSocketTest,
+       SocketDestroyedBeforeProxyResolutionCompletes) {
+  GURL url("http://www.example.com:79");
+
+  auto context = std::make_unique<net::TestURLRequestContext>(true);
+  net::ProxyConfig proxy_config;
+  proxy_config.set_pac_url(GURL("http://foopy/proxy.pac"));
+  proxy_config.set_pac_mandatory(true);
+  net::MockAsyncProxyResolver resolver;
+  auto proxy_resolver_factory =
+      std::make_unique<net::MockAsyncProxyResolverFactory>(false);
+  net::MockAsyncProxyResolverFactory* proxy_resolver_factory_raw =
+      proxy_resolver_factory.get();
+  net::ProxyResolutionService service(
+      std::make_unique<net::ProxyConfigServiceFixed>(
+          net::ProxyConfigWithAnnotation(proxy_config,
+                                         TRAFFIC_ANNOTATION_FOR_TESTS)),
+      std::move(proxy_resolver_factory), nullptr);
+  context->set_proxy_resolution_service(&service);
+  context->Init();
+
+  ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
+      nullptr, context.get());
+  std::unique_ptr<ProxyResolvingClientSocket> socket =
+      proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(), url,
+                                                  use_tls_);
+  net::TestCompletionCallback callback;
+  EXPECT_EQ(net::ERR_IO_PENDING, socket->Connect(callback.callback()));
+  socket.reset();
+  ASSERT_EQ(1u, proxy_resolver_factory_raw->pending_requests().size());
+  proxy_resolver_factory_raw->pending_requests()[0]->CompleteNowWithForwarder(
+      net::OK, &resolver);
+  base::RunLoop().RunUntilIdle();
+}
+
 class ReconsiderProxyAfterErrorTest
     : public testing::Test,
-      public testing::WithParamInterface<::testing::tuple<bool, int>> {
+      public testing::WithParamInterface<::testing::tuple<bool, bool, int>> {
  public:
   ReconsiderProxyAfterErrorTest()
       : context_with_proxy_(
-            "HTTPS badproxy:99; HTTPS badfallbackproxy:98; DIRECT") {}
+            "HTTPS badproxy:99; HTTPS badfallbackproxy:98; DIRECT"),
+        use_tls_(::testing::get<0>(GetParam())) {}
 
   ~ReconsiderProxyAfterErrorTest() override {}
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   TestURLRequestContextWithProxy context_with_proxy_;
+  const bool use_tls_;
 };
 
 // List of errors that are used in the proxy resolution tests.
@@ -600,12 +815,14 @@
 INSTANTIATE_TEST_CASE_P(
     /* no prefix */,
     ReconsiderProxyAfterErrorTest,
-    testing::Combine(testing::Bool(), testing::ValuesIn(kProxyTestMockErrors)));
+    testing::Combine(testing::Bool(),
+                     testing::Bool(),
+                     testing::ValuesIn(kProxyTestMockErrors)));
 
 TEST_P(ReconsiderProxyAfterErrorTest, ReconsiderProxyAfterError) {
   net::IoMode io_mode =
-      ::testing::get<0>(GetParam()) ? net::SYNCHRONOUS : net::ASYNC;
-  const int mock_error = ::testing::get<1>(GetParam());
+      ::testing::get<1>(GetParam()) ? net::SYNCHRONOUS : net::ASYNC;
+  const int mock_error = ::testing::get<2>(GetParam());
 
   // Before starting the test, verify that there are no proxies marked as bad.
   ASSERT_TRUE(context_with_proxy_.proxy_resolution_service()
@@ -628,13 +845,15 @@
   net::StaticSocketDataProvider data3;
   data3.set_connect_data(net::MockConnect(io_mode, net::OK));
   socket_factory.AddSocketDataProvider(&data3);
+  net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
+  socket_factory.AddSSLSocketDataProvider(&ssl_socket);
 
   const GURL kDestination("https://example.com:443");
   ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
       &socket_factory, &context_with_proxy_);
   std::unique_ptr<ProxyResolvingClientSocket> socket =
       proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
-                                                  kDestination);
+                                                  kDestination, use_tls_);
   net::TestCompletionCallback callback;
   int status = socket->Connect(callback.callback());
   EXPECT_EQ(net::ERR_IO_PENDING, status);
@@ -646,6 +865,7 @@
   EXPECT_EQ(2u, retry_info.size()) << mock_error;
   EXPECT_NE(retry_info.end(), retry_info.find("https://badproxy:99"));
   EXPECT_NE(retry_info.end(), retry_info.find("https://badfallbackproxy:98"));
+  EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
 }
 
 }  // namespace network
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc
index 26ed309f..4974dc19 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_win.cc
@@ -12,7 +12,6 @@
 #include <base/strings/sys_string_conversions.h>
 #include <base/win/pe_image.h>
 #include <base/win/win_util.h>
-#include "base/process/process_metrics.h"
 
 namespace memory_instrumentation {
 
@@ -22,14 +21,12 @@
   // Creating process metrics for child processes in mac or windows requires
   // additional information like ProcessHandle or port provider.
   DCHECK_EQ(base::kNullProcessId, pid);
-  auto process_metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
 
-  size_t private_bytes = 0;
-  process_metrics->GetMemoryBytes(&private_bytes, nullptr);
-  dump->platform_private_footprint->private_bytes = private_bytes;
-
-  PROCESS_MEMORY_COUNTERS pmc;
-  if (::GetProcessMemoryInfo(::GetCurrentProcess(), &pmc, sizeof(pmc))) {
+  PROCESS_MEMORY_COUNTERS_EX pmc;
+  if (::GetProcessMemoryInfo(::GetCurrentProcess(),
+                             reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
+                             sizeof(pmc))) {
+    dump->platform_private_footprint->private_bytes = pmc.PrivateUsage;
     dump->resident_set_kb = pmc.WorkingSetSize / 1024;
   }
   return true;
diff --git a/services/shape_detection/PRESUBMIT.py b/services/shape_detection/PRESUBMIT.py
index 6358b80..2b10dc13 100644
--- a/services/shape_detection/PRESUBMIT.py
+++ b/services/shape_detection/PRESUBMIT.py
@@ -19,7 +19,7 @@
   return output_api.EnsureCQIncludeTrybotsAreAdded(
     cl,
     [
-      'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel',
+      'luci.chromium.try:mac_optional_gpu_tests_rel',
       'master.tryserver.chromium.win:win10_chromium_x64_rel_ng'
     ],
     'Automatically added optional Mac GPU and Windows 10 tests to run on CQ.')
diff --git a/services/video_capture/test/mock_device_factory.cc b/services/video_capture/test/mock_device_factory.cc
index c63ab2ab..206e2d0 100644
--- a/services/video_capture/test/mock_device_factory.cc
+++ b/services/video_capture/test/mock_device_factory.cc
@@ -79,4 +79,10 @@
   supported_formats->push_back(kSupportedFormat);
 }
 
+void MockDeviceFactory::GetCameraLocationsAsync(
+    std::unique_ptr<media::VideoCaptureDeviceDescriptors> device_descriptors,
+    DeviceDescriptorsCallback result_callback) {
+  base::ResetAndReturn(&result_callback).Run(std::move(device_descriptors));
+}
+
 }  // namespace video_capture
diff --git a/services/video_capture/test/mock_device_factory.h b/services/video_capture/test/mock_device_factory.h
index 114853f..10eb472 100644
--- a/services/video_capture/test/mock_device_factory.h
+++ b/services/video_capture/test/mock_device_factory.h
@@ -31,6 +31,9 @@
   void GetSupportedFormats(
       const media::VideoCaptureDeviceDescriptor& device_descriptor,
       media::VideoCaptureFormats* supported_formats) override;
+  void GetCameraLocationsAsync(
+      std::unique_ptr<media::VideoCaptureDeviceDescriptors> device_descriptors,
+      DeviceDescriptorsCallback result_callback) override;
 
  private:
   std::map<media::VideoCaptureDeviceDescriptor, media::VideoCaptureDevice*>
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 918dac4..60c62dd 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -5415,7 +5415,7 @@
               "device_type": "gce_x86"
             }
           ],
-          "hard_timeout": 60
+          "hard_timeout": 120
         },
         "test": "gl_unittests"
       },
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 20644591..a454c8fe 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -1349,7 +1349,7 @@
               "device_type": "hammerhead"
             }
           ],
-          "hard_timeout": 60,
+          "hard_timeout": 120,
           "output_links": [
             {
               "link": [
@@ -3032,7 +3032,7 @@
               "device_type": "hammerhead"
             }
           ],
-          "hard_timeout": 60,
+          "hard_timeout": 120,
           "output_links": [
             {
               "link": [
@@ -4624,7 +4624,7 @@
             }
           ],
           "expiration": 10800,
-          "hard_timeout": 60,
+          "hard_timeout": 120,
           "output_links": [
             {
               "link": [
@@ -7771,7 +7771,7 @@
             }
           ],
           "expiration": 10800,
-          "hard_timeout": 60,
+          "hard_timeout": 120,
           "output_links": [
             {
               "link": [
@@ -12679,7 +12679,7 @@
             }
           ],
           "expiration": 10800,
-          "hard_timeout": 60,
+          "hard_timeout": 120,
           "output_links": [
             {
               "link": [
@@ -13076,7 +13076,7 @@
             }
           ],
           "expiration": 10800,
-          "hard_timeout": 60,
+          "hard_timeout": 120,
           "output_links": [
             {
               "link": [
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 9c9ac6b..831b3f3 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -5746,7 +5746,7 @@
               "device_type": "hammerhead"
             }
           ],
-          "hard_timeout": 60,
+          "hard_timeout": 120,
           "output_links": [
             {
               "link": [
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index f005267a..e15cde8 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -55,10 +55,6 @@
 -WebViewTests/WebViewTest.ClearData/1
 -WebViewTests/WebViewTest.ClearDataCache/0
 -WebViewTests/WebViewTest.ClearDataCache/1
--WebViewTests/WebViewTest.DownloadCookieIsolation_CrossSession/0
--WebViewTests/WebViewTest.DownloadCookieIsolation_CrossSession/1
--WebViewTests/WebViewTest.StoragePersistence/0
--WebViewTests/WebViewTest.StoragePersistence/1
 -WebViewTests/WebViewTest.WebViewInBackgroundPage/0
 -WebViewTests/WebViewTest.WebViewInBackgroundPage/1
 # Need support for blocking cookies via content settings:
@@ -310,12 +306,6 @@
 -PPAPINaClPNaClNonSfiTest.FileRef2
 -PPAPINaClPNaClNonSfiTest.URLLoader1
 
-# Session cookies tests are flaky.
-# https://crbug.com/795417
--ContinueWhereILeftOffTest.SessionCookies
--RestartTest.SessionCookies
-
-
 # Requires a replacement for ChromeNetworkDelegate.
 -VariationsHttpHeadersBrowserTest.TestStrippingHeadersFromResourceRequest
 
@@ -327,12 +317,6 @@
 # Fails because data: URL is not downloaded but navigated to.
 -BrowserActionApiTest.BrowserActionPopupDownload
 
-# Flakes https://logs.chromium.org/v/?s=chromium%2Fbb%2Fchromium.linux%2FLinux_Tests%2F65822%2F%2B%2Frecipes%2Fsteps%2Fnetwork_service_browser_tests%2F0%2Flogs%2FNoSessionRestoreTest.PRE_CookiesClearedOnExit%2F0
--NoSessionRestoreTest.CookiesClearedOnExit
-
-# Flakes https://logs.chromium.org/v/?s=chromium%2Fbb%2Fchromium.linux%2FLinux_Tests%2F65817%2F%2B%2Frecipes%2Fsteps%2Fnetwork_service_browser_tests%2F0%2Flogs%2FRestartTest.CookiesClearedOnExit%2F0
--RestartTest.CookiesClearedOnExit
-
 # Flaky in linux tests.
 # https://crbug.com/820995
 -PlatformAppBrowserTest.Restrictions
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index b4210a1..253dd155 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -3086,7 +3086,7 @@
       },
       'Marshmallow Tablet Tester': {
         'swarming': {
-          'hard_timeout': 60,
+          'hard_timeout': 120,
         },
       },
 
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 1ad614be..f661656 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -274,7 +274,7 @@
       # generation script is merged with this one, this should be promoted from
       # the Android-specific section.
       'android_swarming': {
-        'hard_timeout': 60,
+        'hard_timeout': 120,
       },
     },
     'mojo_test_apk': {
diff --git a/testing/libfuzzer/README.md b/testing/libfuzzer/README.md
index 99a7aa9..df023d7 100644
--- a/testing/libfuzzer/README.md
+++ b/testing/libfuzzer/README.md
@@ -10,7 +10,7 @@
 ***
 
 This directory contains integration between [libFuzzer] and Chromium.
-libFuzzer is an in-process coverage-driven evolutionary fuzzer. It helps
+LibFuzzer is an in-process coverage-driven evolutionary fuzzing engine. It helps
 engineers to uncover potential security & stability problems earlier.
 
 *** note
@@ -25,11 +25,11 @@
 ## Documentation
 
 * [Getting Started Guide] walks you through all the steps necessary to create
-your fuzzer and submit it to ClusterFuzz.
-* [Efficient Fuzzer Guide] explains how to measure fuzzer effectiveness and
+your fuzz target and submit it to ClusterFuzz.
+* [Efficient Fuzzer Guide] explains how to measure fuzz target effectiveness and
 ways to improve it.
 * [Guide to libprotobuf-mutator] walks through the steps necessary to create a
-fuzzer that libFuzzer gives mutated protobufs to as input (for developers
+fuzz target that libFuzzer gives mutated protobufs to as input (for developers
 already familiar with libFuzzer).
 * [ClusterFuzz Integration] describes integration between ClusterFuzz and
 libFuzzer.
@@ -39,7 +39,7 @@
 
 ## Trophies
 * [ClusterFuzz Bugs] - issues found and automatically filed by ClusterFuzz.
-* [Manual Bugs] - issues that were filed manually after running fuzzers.
+* [Manual Bugs] - issues that were filed manually after running fuzz targets.
 * [Pdfium Bugs] - bugs found in pdfium by manual fuzzing.
 * [OSS Trophies] - bugs found with libFuzzer in open-source projects.
 
diff --git a/testing/libfuzzer/clusterfuzz.md b/testing/libfuzzer/clusterfuzz.md
index 56b72053..c46c4df 100644
--- a/testing/libfuzzer/clusterfuzz.md
+++ b/testing/libfuzzer/clusterfuzz.md
@@ -1,34 +1,34 @@
 # libFuzzer and ClusterFuzz Integration
 
 ClusterFuzz is a distributed fuzzing infrastructure that automatically
-executes libFuzzer tests on scale.
+executes libFuzzer powered fuzzer tests on scale.
 
 Googlers can read more [here](https://goto.google.com/clusterfuzz).
 
 ## Status Links
 
 * [Buildbot] - status of all libFuzzer builds.
-* [ClusterFuzz Fuzzer Status] - fuzzing metrics, links to crashes and coverage 
+* [ClusterFuzz Fuzzer Status] - fuzzing metrics, links to crashes and coverage
 reports.
-* [ClusterFuzz libFuzzer Logs] - individual fuzzer run logs.
-* [Corpus GCS Bucket] - current corpus for each fuzzer. Can be used to upload
-bootstrapped corpus.
+* [ClusterFuzz libFuzzer Logs] - individual fuzz target run logs.
+* [Corpus GCS Bucket] - current corpus for each fuzz target. Can be used to
+upload bootstrapped corpus.
 
 ## Integration Details
 
 The integration between libFuzzer and ClusterFuzz consists of:
 
 * Build rules definition in [fuzzer_test.gni].
-* [Buildbot] that automatically discovers fuzzers using `gn refs` facility, 
-builds fuzzers with multiple sanitizers and uploads binaries to a special
-GCS bucket. Build bot recipe is defined in [chromium_libfuzzer.py].
-* ClusterFuzz downloads new binaries once a day and runs fuzzers continuously.
-* Fuzzer run logs are uploaded to [ClusterFuzz libFuzzer Logs] GCS bucket.
-* Fuzzing corpus is maintained for each fuzzer in [Corpus GCS Bucket]. Once a day,
-corpus is minimized to reduce number of duplicates and/or reduce effect of 
-parasitic coverage. 
-* [ClusterFuzz Fuzzer Status] displays fuzzer runtime 
-metrics as well as provides links to crashes and coverage reports.
+* [Buildbot] that automatically discovers fuzz targets using `gn refs`, builds
+fuzz targets with multiple sanitizers and uploads binaries to a GCS bucket.
+Recipe is defined in [chromium_libfuzzer.py].
+* ClusterFuzz downloads builds and runs fuzz targets continuously.
+* Fuzz target run logs are uploaded to [ClusterFuzz libFuzzer Logs] GCS bucket.
+* Fuzzing corpus is maintained for each fuzz target in [Corpus GCS Bucket]. Once
+a day, the corpus is minimized to reduce number of duplicates and/or reduce
+effect of parasitic coverage.
+* [ClusterFuzz Fuzzer Status] displays fuzzer runtime metrics as well as
+provides links to crashes and coverage reports.
 
 
 ## Corpus
@@ -41,7 +41,7 @@
 These corpus files are frequently modified during fuzzing sessions and can be
 deleted during corpus minimization.
 
-A fuzzer has two input corpus directories, seed and general, but its output
+A fuzz target has two input corpus directories, seed and general, but its output
 goes into general corpus directory. Seed corpus is read-only.
 
 
diff --git a/testing/libfuzzer/efficient_fuzzer.md b/testing/libfuzzer/efficient_fuzzer.md
index bbde9ab..04b5dc7 100644
--- a/testing/libfuzzer/efficient_fuzzer.md
+++ b/testing/libfuzzer/efficient_fuzzer.md
@@ -1,39 +1,124 @@
-# Efficient Fuzzer
+# Efficient Fuzzer Guide
 
-This document describes ways to determine your fuzzer efficiency and ways
+This document describes ways to determine efficiency of a fuzz target and ways
 to improve it.
 
 ## Overview
 
-Being a coverage-driven fuzzer, libFuzzer considers a certain input *interesting*
-if it results in new code coverage. The set of all interesting inputs is called
+Being a coverage-driven fuzzing engine, libFuzzer considers a certain input
+*interesting* if it results in new code coverage, i.e. it reaches a code that
+has not been reached before. The set of all interesting inputs is called
 *corpus*.
 
 Items in corpus are constantly mutated in search of new interesting inputs.
 Corpus can be shared across fuzzer runs and grows over time as new code is
 reached.
 
-The following things are extremely effective for improving fuzzer efficiency, so we
-*strongly recommend* them for any fuzzer:
+There are several metrics you should look at to determine effectiveness of your
+fuzz target:
+
+* [Execution Speed](#Execution-Speed)
+* [Code Coverage](#Code-Coverage)
+* [Corpus Size](#Corpus-Size)
+
+You can collect these metrics manually or take them from [ClusterFuzz status]
+pages after a fuzz target is checked in Chromium repository.
+
+The following things are extremely useful for improving fuzzing efficiency, so
+we *strongly recommend* them for any fuzz target:
 
 * [Seed Corpus](#Seed-Corpus)
 * [Fuzzer Dictionary](#Fuzzer-Dictionary)
 
-There are several metrics you should look at to determine your fuzzer effectiveness:
+There are other ways that are useful in some cases, but not always applicable:
+* [Custom Options](#Custom-Options)
+* [Custom Build](#Custom-Build)
 
-* [Fuzzer Speed](#Fuzzer-Speed)
-* [Corpus Size](#Corpus-Size)
-* [Code Coverage](#Code-Coverage)
 
-You can collect these metrics manually or take them from [ClusterFuzz status]
-pages after a fuzzer is checked in Chromium repository.
+## Execution Speed
+
+Fuzz target speed is calculated in executions per second. It is printed while a
+fuzz target is running:
+
+```
+#19346  NEW    cov: 2815 bits: 1082 indir: 43 units: 150 exec/s: 19346 L: 62
+```
+
+Because libFuzzer performs randomized mutations, it is critical to have it run
+as fast as possible to navigate through the large search space efficiently and
+find interesting code paths. You should try to get to at least 1,000 exec/s from
+your fuzz target locally before submitting it to the Chromium repository.
+
+
+### Initialization/Cleanup
+
+Try to keep `LLVMFuzzerTestOneInput` function as simple as possible. If your
+fuzzing function is too complex, it can bring down fuzzer execution speed OR it
+can target very specific usecases and fail to account for unexpected scenarios.
+
+Prefer to use static initialization and shared resources rather than performing
+setup and teardown on every single input. Checkout example on
+[startup initialization] in libFuzzer documentation.
+
+You can skip freeing static resources. However, all resources allocated within
+`LLVMFuzzerTestOneInput` function should be de-allocated since this function is
+called millions of times during a fuzzing session. Otherwise, we will hit OOMs
+frequently and reduce overall fuzzing efficiency.
+
+
+### Memory Usage
+
+Avoid allocation of dynamic memory wherever possible. Memory instrumentation
+works faster for stack-based and static objects, than for heap allocated ones.
+
+It is always a good idea to try different variants for your fuzz target locally,
+and then submit the fastest implementation.
+
+
+## Code Coverage
+
+[ClusterFuzz status] page provides source-level coverage report for fuzz targets
+from recent runs. Looking at the report might provide an insight on how to
+improve code coverage of a fuzz target.
+
+You can also generate source-level coverage report locally by running the
+[coverage script] stored in Chromium repository. The script provides detailed
+instructions as well as an usage example.
+
+Note that code coverage of a fuzz target **depends heavily** on the corpus
+provided when running the target, i.e. code coverage report generated by a fuzz
+target launched without any corpus would not make much sense.
+
+We encourage you to try out the [coverage script], as it usually generates a better
+code coverage visualization compared to the coverage report hosted on
+ClusterFuzz.
+*NOTE: This is an experimental feature and an active area of work. We are
+working on improving this process.*
+
+
+## Corpus Size
+
+After running for a while, a fuzz target would reach a plateau and may stop
+discovering new interesting inputs. Corpus for a reasonably complex target
+should contain hundreds (if not thousands) of items.
+
+Too small of a corpus size indicates that fuzz target is hitting a code barrier
+and is unable to get past it. Common cases of such issues include: checksums,
+magic numbers, etc. The easiest way to diagnose this problem is to generate and
+analyze a [coverage report](#Coverage). To fix the issue, you can:
+
+* Change the code (e.g. disable crc checks while fuzzing), see [Custom Build](#Custom-Build).
+* Prepare or improve [seed corpus](#Seed-Corpus).
+* Prepare or improve [fuzzer dictionary](#Fuzzer-Dictionary).
+* Add [custom options](#Custom-Options).
+
 
 ## Seed Corpus
 
-Seed corpus is a set of *valid* and *interesting* inputs that serve as starting points
-for a fuzzer. If one is not provided, a fuzzer would have to guess these inputs
-from scratch, which can take an indefinite amount of time depending of the size
-of inputs.
+Seed corpus is a set of *valid* and *interesting* inputs that serve as starting
+points for a fuzz target. If one is not provided, a fuzzing engine would have to
+guess these inputs from scratch, which can take an indefinite amount of time
+depending on size of the inputs and complexity of the target format.
 
 Seed corpus works especially well for strictly defined file formats and data
 transmission protocols.
@@ -41,20 +126,23 @@
 * For file format parsers, add valid files from your test suite.
 * For protocol parsers, add valid raw streams from test suite into separate files.
 
-Other examples include a graphics library seed corpus, which would be a variety of
-small PNG/JPG/GIF files.
+Other examples include a graphics library seed corpus, which would be a variety
+of small PNG/JPG/GIF files.
 
-If you are running the fuzzer locally, you can pass a corpus directory as an argument:
+If you are running a fuzz target locally, you can pass a corpus directory as an
+argument:
 
 ```
 ./out/libfuzzer/my_fuzzer ~/tmp/my_fuzzer_corpus
 ```
 
-While libFuzzer can start with an empty corpus, most fuzzers require a seed corpus
-to be useful. The fuzzer would store all the interesting items it finds in that directory.
+The fuzzer would store all the interesting inputs it finds in that directory.
+
+While libFuzzer can start with an empty corpus, seed corpus is always useful and
+in many cases is able to increase code coverage by an order of magnitude.
 
 ClusterFuzz uses seed corpus defined in Chromium source repository. You need to
-add a `seed_corpus` attribute to your fuzzer definition in BUILD.gn file:
+add a `seed_corpus` attribute to your `fuzzer_test` definition in BUILD.gn file:
 
 ```
 fuzzer_test("my_protocol_fuzzer") {
@@ -77,9 +165,9 @@
 All files found in these directories and their subdirectories will be archived
 into a `<my_fuzzer_name>_seed_corpus.zip` output archive.
 
-If you can't store seed corpus in Chromium repository (e.g. it is too large, has
-licensing issues, etc), you can upload the corpus to Google Cloud Storage bucket
-used by ClusterFuzz:
+If you can't store seed corpus in Chromium repository (e.g. it is too large,
+cannot be open sourced, etc), you can upload the corpus to Google Cloud Storage
+bucket used by ClusterFuzz:
 
 1) Go to [Corpus GCS Bucket].
 2) Open directory named `<my_fuzzer_name>_static`. If the directory does not
@@ -91,66 +179,12 @@
 gsutil -m rsync <path_to_corpus> gs://clusterfuzz-corpus/libfuzzer/<my_fuzzer_name>_static
 ```
 
-## Fuzzer Dictionary
-
-It is very useful to provide fuzzer a set of *common words or values* that you
-expect to find in the input. Adding a dictionary highly improves the efficiency of
-finding new units and works especially well in certain usecases (e.g. fuzzing file
-format decoders).
-
-To add a dictionary, first create a dictionary file. Dictionary file is a flat text file
-where tokens are listed one per line in the format of name="value". The
-alphanumeric name is ignored and can be omitted, although it is a convenient
-way to document the meaning of a particular token. The value must appear in
-quotes, with hex escaping (\xNN) applied to all non-printable, high-bit, or
-otherwise problematic characters (\\ and \" shorthands are recognized too).
-This syntax is similar to the one used by [AFL] fuzzing engine (-x option).
-
-An examples dictionary looks like:
-
-```
-# Lines starting with '#' and empty lines are ignored.
-
-# Adds "blah" word (w/o quotes) to the dictionary.
-kw1="blah"
-# Use \\ for backslash and \" for quotes.
-kw2="\"ac\\dc\""
-# Use \xAB for hex values.
-kw3="\xF7\xF8"
-# Key name before '=' can be omitted:
-"foo\x0Abar"
-```
-
-Make sure to test your dictionary by running your fuzzer locally:
-
-```bash
-./out/libfuzzer/my_protocol_fuzzer -dict=<path_to_dict> <path_to_corpus>
-```
-
-If the dictionary is effective, you should see new units discovered in fuzzer output.
-
-To submit a dictionary to Chromium repository:
-
-1) Add the dictionary file in the same directory as your fuzz target, with name
-`<my_fuzzer>.dict`.
-2) Add `dict` attribute to fuzzer definition in BUILD.gn file:
-
-```
-fuzzer_test("my_protocol_fuzzer") {
-  ...
-  dict = "my_protocol_fuzzer.dict"
-}
-```
-
-The dictionary will be used automatically by ClusterFuzz once it picks up a new
-revision build.
-
 ### Corpus Minimization
 
-It's important to minimize seed corpus to a *small set of interesting inputs* before
-uploading. The reason being that seed corpus is synced to all fuzzing bots for every
-iteration, so it is important to keep it small both for fuzzing efficiency and to prevent
-our bots from running out of disk space (should not exceed 1 Gb).
+It's important to minimize seed corpus to a *small set of interesting inputs*
+before uploading. The reason being that seed corpus is synced to all fuzzing
+bots for every iteration, so it is important to keep it small both for fuzzing
+efficiency and to prevent our bots from running out of disk space.
 
 The minimization can be done using `-merge=1` option of libFuzzer:
 
@@ -166,125 +200,72 @@
 a minimized corpus that gives the same code coverage as the initial
 `seed_corpus` directory.
 
-## Fuzzer Speed
 
-Fuzzer speed is calculated in executions per second. It is printed while the fuzzer
-is running:
+
+
+## Fuzzer Dictionary
+
+It is very useful to provide fuzz target with a set of *common words or values*
+that you expect to find in the input. Adding a dictionary highly improves the
+efficiency of finding new units and works especially well in certain usecases
+(e.g. fuzzing file format decoders or text based protocols like XML).
+
+To add a dictionary, first create a dictionary file. This is a flat text file
+where tokens are listed one per line in the format of `name="value"`, where
+`name` is optional and can be omitted, although it is a convenient way to
+document the meaning of a particular token. The value must appear in quotes,
+with hex escaping (\xNN) applied to all non-printable, high-bit, or otherwise
+problematic characters (\\ and \" shorthands are recognized too). This syntax is
+similar to the one used by [AFL] fuzzing engine (-x option).
+
+An example dictionary looks like:
 
 ```
-#19346  NEW    cov: 2815 bits: 1082 indir: 43 units: 150 exec/s: 19346 L: 62
+# Lines starting with '#' and empty lines are ignored.
+
+# Adds "blah" word (w/o quotes) to the dictionary.
+kw1="blah"
+# Use \\ for backslash and \" for quotes.
+kw2="\"ac\\dc\""
+# Use \xAB for hex values.
+kw3="\xF7\xF8"
+# Key name before '=' can be omitted:
+"foo\x0Abar"
 ```
 
-Because libFuzzer performs randomized mutations, it is critical to have it run as
-fast as possible to navigate the large search space efficiently and find interesting
-code paths. You should try to get to at least 1,000 exec/s from your fuzzer runs
-locally before submitting the fuzzer to Chromium repository. Profile the fuzzer
-using any standard tool to see where it spends its time.
+Make sure to test your dictionary by running your fuzz target locally:
 
-
-### Initialization/Cleanup
-
-Try to keep `LLVMFuzzerTestOneInput` function as simple as possible. If your fuzzing
-function is too complex, it can bring down fuzzer execution speed OR it might target
-very specific usecases and fail to account for unexpected scenarios.
-
-Prefer to use static initialization and shared resources rather than bringing the
-environment up and down on every single run. Otherwise, it will slow down
-fuzzer speed on every run and its ability to find new interesting inputs.
-Checkout example on [startup initialization] in libFuzzer documentation.
-
-Fuzzers don't have to shutdown gracefully. We either kill them or they crash
-because memory sanitizer tool found a problem. You can skip freeing static
-resources.
-
-All resources allocated within `LLVMFuzzerTestOneInput` function should be
-de-allocated since this function is called millions of times during a fuzzing session.
-Otherwise, we will hit OOMs frequently and reduce overall fuzzing efficiency.
-
-
-### Memory Usage
-
-Avoid allocation of dynamic memory wherever possible. Memory instrumentation
-works faster for stack-based and static objects, than for heap allocated ones.
-
-It is always a good idea to try different variants for your fuzzer locally, and then
-submit the fastest implementation.
-
-
-### Maximum Testcase Length
-
-You can control the maximum length of a test input using `-max_len` parameter
-(see [custom options](#Custom-Options)). This parameter can often significantly
-improve execution speed. Beware that you might miss coverage and unexpected
-scenarios happening from longer size inputs.
-
-1) Define which `-max_len` value is reasonable for your target. For example, it
-may be useless to fuzz an image decoder with too small value of testcase length.
-
-2) Increase the value defined on previous step. Check its influence on execution
-speed of fuzzer. If speed doesn't drop significantly for long inputs, it is fine
-to have some bigger value for `-max_len` or even skip it completely.
-
-In general, bigger `-max_len` value gives better coverage which is the main
-priority for fuzzing. However, low execution speed may result in waste of
-fuzzing resources and being unable to find interesting inputs in reasonable time.
-If large inputs make the fuzzer too slow, you should adjust the value of `-max_len`
-and find a trade-off between coverage and execution speed.
-
-*Note:* ClusterFuzz runs two different fuzzing engines (**LibFuzzer** and
-**AFL**) using the same target functions. AFL doesn't support `-max_len`
-parameter and may provide input of any length to the target. If your target has
-an input length limit that you would like to *strictly enforce*, it's
-recommended to add a sanity check to the beginning of your target function:
-
-```
-if (size > kSizeLimit)
-  return 0;
+```bash
+./out/libfuzzer/my_protocol_fuzzer -dict=<path_to_dict> <path_to_corpus>
 ```
 
-For more information check out the discussion in [issue 638836].
+If the dictionary is effective, you should see new units discovered in the
+output.
+
+To submit a dictionary to Chromium repository:
+
+1) Add the dictionary file in the same directory as your fuzz target
+2) Add `dict` attribute to `fuzzer_test` definition in BUILD.gn file:
+
+```
+fuzzer_test("my_protocol_fuzzer") {
+  ...
+  dict = "my_protocol_fuzzer.dict"
+}
+```
+
+The dictionary will be used automatically by ClusterFuzz once it picks up a new
+revision build.
 
 
-## Code Coverage
-
-[ClusterFuzz status] page provides fuzzer source-level coverage report from
-recent runs. Looking at the report might provide an insight to improve fuzzer
-coverage.
-
-You can also generate source-level coverage report locally by running the
-[coverage script] stored in Chromium repository. The script provides detailed
-instructions as well as an usage example.
-
-We encourage you to try out the script, as it usually generates a better code
-coverage visualization compared to the coverage report hosted on ClusterFuzz.
-*NOTE: This is an experimental feature and an active area of work. We are
-working on improving this process.*
-
-
-## Corpus Size
-
-After running for a while, the fuzzer would reach a plateau and won't discover
-new interesting inputs. Corpus for a reasonably complex functionality should
-contain hundreds (if not thousands) of items.
-
-Too small of a corpus size indicates fuzzer is hitting a code barrier and is unable
-to get past it. Common cases of such issues include: checksums, magic numbers,
-etc. The easiest way to diagnose this problem is to generate and analyze a
-[coverage report](#Coverage). To fix the issue, you can:
-
-* Change the code (e.g. disable crc checks while fuzzing).
-* Prepare or improve [seed corpus](#Seed-Corpus).
-* Prepare or improve [fuzzer dictionary](#Fuzzer-Dictionary).
-* Add [custom options](#Custom-Options).
-
-### Custom Options
+## Custom Options
 
 Custom options help to fine tune libFuzzer execution parameters and will also
 override the default values used by ClusterFuzz. Please read [libFuzzer options]
 page for detailed documentation on how these work.
 
-Add the options needed in `libfuzzer_options` attribute to your fuzzer definition in
-BUILD.gn file:
+Add the options needed in `libfuzzer_options` attribute to your `fuzzer_test`
+definition in BUILD.gn file:
 
 ```
 fuzzer_test("my_protocol_fuzzer") {
@@ -299,6 +280,18 @@
 Please note that `dict` parameter should be provided [separately](#Fuzzer-Dictionary).
 All other options can be passed using `libfuzzer_options` property.
 
+
+## Custom Build
+
+If you need to change the code being tested by your fuzz target, you may use an
+`#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` macro in your target code.
+
+Note that patching target code is not a preferred way of improving corresponding
+fuzz target, but in some cases that might be the only way possible, e.g. when
+there is no intended API to disable checksum verification, or when target code
+uses random generator that affects reproducibility of crashes.
+
+
 [AFL]: http://lcamtuf.coredump.cx/afl/
 [ClusterFuzz status]: clusterfuzz.md#Status-Links
 [Corpus GCS Bucket]: https://console.cloud.google.com/storage/clusterfuzz-corpus/libfuzzer
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn
index 1038fd0d..a3006cf 100644
--- a/testing/libfuzzer/fuzzers/BUILD.gn
+++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -130,15 +130,6 @@
   libfuzzer_options = [ "max_len=1500" ]
 }
 
-fuzzer_test("base_json_reader_fuzzer") {
-  sources = [
-    "base_json_reader_fuzzer.cc",
-  ]
-  deps = [
-    "//base",
-  ]
-}
-
 libpng_seed_corpuses = [
   "//components/viz/test/data",
   "//third_party/WebKit/LayoutTests/images/png-suite/samples",
diff --git a/testing/libfuzzer/getting_started.md b/testing/libfuzzer/getting_started.md
index 8a66569..44875fab 100644
--- a/testing/libfuzzer/getting_started.md
+++ b/testing/libfuzzer/getting_started.md
@@ -7,8 +7,8 @@
 This document will walk you through:
 
 * setting up your build enviroment.
-* creating your first fuzzer.
-* running the fuzzer and verifying its vitals.
+* creating your first fuzz target.
+* running the fuzz target and verifying its vitals.
 
 ## Configure Build
 
@@ -33,14 +33,14 @@
 | `is_ubsan_security=true` | enables [Undefined Behavior Sanitizer] to catch<sup>\[[*](reference.md#UBSan)\]</sup> undefined behavior like integer overflow. |
 | | it is possible to run libfuzzer without any sanitizers; *probably not what you want*.|
 
- Fuzzers are built with minimal symbols by default, regardless of the value of
-`is_debug` and `symbol_level`. However if you want to run the fuzzer under a
+Fuzz targets are built with minimal symbols by default, regardless of the value
+of `is_debug` and `symbol_level`. However if you want to run fuzz target under a
 debugger you can re-enable them by setting `sanitizer_keep_symbols=true`.
 
 To get the exact GN configuration that are used on our builders, see
 [Build Config].
 
-## Write Fuzzer Function
+## Write Fuzz Target
 
 Create a new `<my_fuzzer>.cc` file and define a `LLVMFuzzerTestOneInput` function:
 
@@ -54,10 +54,11 @@
 }
 ```
 
-*Note*: You should create the fuzzer file `<my_fuzzer>.cc` next to the code that is being
-tested and in the same directory as your other unit tests. Please do not use
-`testing/libfuzzer/fuzzers` directory, this was a directory used for initial sample fuzzers and
-is no longer recommended for any new fuzzers.
+*Note*: You should create the fuzz target file `<my_fuzzer>.cc next to the code
+that is being tested and in the same directory as your other unit tests. Please
+do not use `testing/libfuzzer/fuzzers` directory, this was a directory used for
+initial sample fuzz targets and is no longer recommended for landing new fuzz
+targets.
 
 [quic_stream_factory_fuzzer.cc] is a good example of real-world fuzz target.
 
@@ -73,7 +74,7 @@
 }
 ```
 
-## Build and Run Fuzzer Locally
+## Build and Run Fuzz Target Locally
 
 Build with ninja as usual and run:
 
@@ -82,24 +83,29 @@
 ./out/libfuzzer/url_parse_fuzzer
 ```
 
-Your fuzzer should produce output like this:
+Your fuzz target should produce output like this:
 
 ```
-INFO: Seed: 1787335005
-INFO: -max_len is not provided, using 64
-INFO: PreferSmall: 1
-#0      READ   units: 1 exec/s: 0
-#1      INITED cov: 2361 bits: 95 indir: 29 units: 1 exec/s: 0
-#2      NEW    cov: 2710 bits: 359 indir: 36 units: 2 exec/s: 0 L: 64 MS: 0
+INFO: Seed: 1511722356
+INFO: Loaded 2 modules   (115485 guards): 22572 [0x7fe8acddf560, 0x7fe8acdf5610), 92913 [0xaa05d0, 0xafb194), 
+INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
+INFO: A corpus is not provided, starting from an empty corpus
+#2  INITED cov: 961 ft: 48 corp: 1/1b exec/s: 0 rss: 48Mb
+#3  NEW    cov: 986 ft: 70 corp: 2/104b exec/s: 0 rss: 48Mb L: 103/103 MS: 1 InsertRepeatedBytes-
+#4  NEW    cov: 989 ft: 74 corp: 3/106b exec/s: 0 rss: 48Mb L: 2/103 MS: 1 InsertByte-
+#6  NEW    cov: 991 ft: 76 corp: 4/184b exec/s: 0 rss: 48Mb L: 78/103 MS: 2 CopyPart-InsertRepeatedBytes-
 ```
 
-The `... NEW ...` line appears when libFuzzer finds new and interesting inputs.
-The efficient fuzzer should be able to finds lots of them rather quickly.
-The `... pulse ...` line will appear periodically to show the current status.
+* `... NEW ...` line appears when libFuzzer finds new and interesting inputs.
+* an efficient fuzz target should be able to finds lots of them rather quickly.
+* `... pulse ...` line will appear periodically to show the current status.
+
+For more information about libFuzzer's output, please refer to [its own
+documentation].
 
 ### Symbolize Stacktrace
 
-If your fuzzer crashes when running locally and you see non-symbolized
+If your fuzz target crashes when running locally and you see non-symbolized
 stacktrace, make sure that you have directory containing `llvm-symbolizer`
 binary added in `$PATH`. The symbolizer binary is included in Chromium's Clang
 package located at `third_party/llvm-build/Release+Asserts/bin/` directory.
@@ -112,35 +118,42 @@
     ./fuzzer ./crash-input
 ```
 
-The same approach works with other sanitizers (e.g. `MSAN_OPTIONS`, `UBSAN_OPTIONS`, etc).
+The same approach works with other sanitizers (e.g. `MSAN_OPTIONS`,
+`UBSAN_OPTIONS`, etc).
 
-## Improving Your Fuzzer
+## Improving Your Fuzz Target
 
-Your fuzzer may immediately discover interesting (i.e. crashing) inputs.
+Your fuzz target may immediately discover interesting (i.e. crashing) inputs.
 To make it more efficient, several small steps can take you really far:
 
 * Create seed corpus. Add `seed_corpus = "src/fuzz-testcases/"` attribute
-to your fuzzer targets and add example files in appropriate folder. Read more
-in [Seed Corpus] section of efficient fuzzer guide.
+to your fuzzer target and add example files in appropriate folder. Read more
+in [Seed Corpus] section of the [Efficient Fuzzer Guide].
 *Make sure corpus files are appropriately licensed.*
 * Create mutation dictionary. With a `dict = "protocol.dict"` attribute and
 `key=value` dicitionary file format, mutations can be more effective.
-See [Fuzzer Dictionary] section of efficient fuzzer guide.
-* Specify maximum testcase length. By default libFuzzer uses `-max_len=64`
- (or takes the longest testcase in a corpus). ClusterFuzz takes
-random value in range from `1` to `10000` for each fuzzing session and passes
-that value to libFuzzers. If corpus contains testcases of size greater than
-`max_len`, libFuzzer will use only first `max_len` bytes of such testcases.
-See [Maximum Testcase Length] section of the efficient fuzzer guide.
+See [Fuzzer Dictionary] section of the [Efficient Fuzzer Guide].
+* Specify testcase length limits. By default, libFuzzer uses `-max_len=4096`
+or takes the longest testcase in the corpus if `-max_len` is not specified.
+ClusterFuzz uses different strategies for different fuzzing sessions, including
+different random values. Also, ClusterFuzz uses different fuzzing engines (e.g.
+AFL that doesn't have `-max_len` option). If your target has an input length
+limit that you would like to *strictly enforce*, add a sanity check to the
+beginning of your target function:
 
-## Disable noisy error message logging
+```cpp
+if (size < kMinInputLength || size > kMaxInputLength)
+  return 0;
+```
 
-If the code that you are a fuzzing generates lot of error messages when
+### Disable noisy error message logging
+
+If the code that you are fuzzing generates lot of error messages when
 encountering incorrect or invalid data, then you need to silence those errors
-in the fuzzer. Otherwise, fuzzer will be slow and inefficient.
+in the fuzz target. Otherwise, fuzz target will be slow and inefficient.
 
-If the target uses the Chromium logging APIs, the best way to do that is to
-override the environment used for logging in your fuzzer:
+If the target uses Chromium logging APIs, the best way to do that is to
+override the environment used for logging in your fuzz target:
 
 ```cpp
 struct Environment {
@@ -152,20 +165,135 @@
 Environment* env = new Environment();
 ```
 
-## Submitting Fuzzer to ClusterFuzz
+## Mutating Multiple Inputs
+
+By default, a fuzzing engine such as libFuzzer mutates a single input referenced
+by `uint8_t* data, size_t size`. However, quite often an API under fuzz testing
+accepts multiple arguments of various types rather than a single buffer. There
+are three approaches for such cases:
+
+### 1) libprotobuf-mutator
+
+If you need to mutate multiple inputs of various types and length, please see
+[Getting Started with libprotobuf-mutator in Chromium]. That approach allows
+to mutate multiple inputs independently.
+
+**Caveats:** This approach requires an extra effort, but works with APIs and
+data structures of any complexity.
+
+### 2) hash-based argument
+
+Another frequent case of an API under fuzz testing is a function that accepts a
+buffer with data and some integer value meaning a bitwise combination of flags.
+For such cases, we recommend to calculate a hash value from `(data, size)` and
+use that value for fuzzing of an additional integer argument, for example:
+
+```cpp
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::string str = std::string(reinterpret_cast<const char*>(data), size);
+  std::size_t data_hash = std::hash<std::string>()(str);
+  APIToBeFuzzed(data, size, data_hash);
+  return 0;
+}
+
+```
+
+**Caveats:** Hash value derived from the data would be a random value rather
+than a meaningful value controlled by fuzzing engine, i.e. a single bit mutation
+would result in a completely different hash value that might lead to a new code
+coverage, but the next mutation would generate another hash value and trigger
+another code path, without providing a real guidance to the fuzzing engine.
+
+### 3) bytes taken from (data, size)
+
+You can extract one or more bytes from the data provided by fuzzing engine and
+use that value for fuzzing other arguments of the target API or making other
+decisions (e.g. number of iterations or attempts for calling some function).
+Note that those bytes should not be used as data for any other arguments, e.g.:
+
+```cpp
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // Don't forget to enfoce minimal data length.
+  if (size < 1)
+    return 0;
+
+  // Extract single byte for fuzzing "flags" value.
+  uint8_t flags = data[0];
+
+  // Wrong, there is a bias between flags and API input.
+  APIToBeFuzzed(data, size, flags);
+
+  // Good, API input and flags are independent.
+  APIToBeFuzzed(data + 1, size - 1, flags);
+
+  return 0;
+}
+```
+
+This approach addresses the problem of the *hash-based argument* approach, but
+has its own **caveats**:
+
+* If you extract any bytes from the input (either first or last ones), you
+cannot use valid samples as seed corpus. In that case, you'll have to generate
+seed corpus manually, i.e. append necessary bytes to the valid sample inputs.
+
+* Imagine that `APIToBeFuzzed()` had a bug, something like the following:
+
+```cpp
+void APIToBeFuzzed(uint8_t* buffer, size_t length, uint8_t options) {
+  ...
+  if (options == 0x66) {
+    // Yes, looks ridiculous, but things like that did happen in the real world.
+    *(buffer - 1) = -1;
+  }
+  ...
+}
+```
+
+assuming we used the fuzz target listed above, neither ASan nor other santizers
+would detect a buffer underwrite vulnerability, as the byte addressed by
+`buffer - 1` is actually a mapped memory allocated inside the fuzzing engine as
+`data[0]`.
+
+To avoid issues like that one, we would have to allocate a separate buffer and
+copy API input into it, or use a container object e.g.:
+
+```cpp
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // Don't forget to enfoce minimal data length.
+  if (size < 1)
+    return 0;
+
+  // Extract single byte for fuzzing flags value.
+  uint8_t flags = data[0];
+
+  // Put API input into a separate container.
+  std::vector<uint8_t> buffer(data + 1, data + size);
+
+  APIToBeFuzzed(buffer.data(), buffer.size(), flags);
+
+  return 0;
+}
+```
+
+There is [base::FuzzedDataProvider] class that might be helpful for writing
+fuzz targets using that approach.
+
+
+## Submitting Fuzz Target to ClusterFuzz
 
 ClusterFuzz builds and executes all `fuzzer_test` targets in the Chromium
-repository. It is extremely important to submit a fuzzer into Chromium
+repository. It is extremely important to land a fuzz target into Chromium
 repository so that ClusterFuzz can run it at scale. Do not rely on just
-running fuzzing locally in your own environment, as it will catch far less
-issues. It's crucial to run fuzzers continuously forever for catching
+running fuzzers locally in your own environment, as it will catch far less
+issues. It's crucial to run fuzz targets continuously forever for catching
 regressions and improving code coverage over time.
 
 ## Next Steps
 
-* After your fuzzer is submitted, you should check [ClusterFuzz status] page in
-a day or two.
-* Check the [Efficient Fuzzer Guide] to better understand your fuzzer
+* After your fuzz target is landed, you should check [ClusterFuzz status] page
+in a day or two.
+* Check the [Efficient Fuzzer Guide] to better understand your fuzz target
 performance and for optimization hints.
 
 
@@ -173,10 +301,12 @@
 [ClusterFuzz status]: clusterfuzz.md#Status-Links
 [Efficient Fuzzer Guide]: efficient_fuzzer.md
 [Fuzzer Dictionary]: efficient_fuzzer.md#Fuzzer-Dictionary
-[Maximum Testcase Length]: efficient_fuzzer.md#Maximum-Testcase-Length
 [Memory Sanitizer]: http://clang.llvm.org/docs/MemorySanitizer.html
 [Seed Corpus]: efficient_fuzzer.md#Seed-Corpus
 [Undefined Behavior Sanitizer]: http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
 [crbug/598448]: https://bugs.chromium.org/p/chromium/issues/detail?id=598448
 [quic_stream_factory_fuzzer.cc]: https://cs.chromium.org/chromium/src/net/quic/chromium/quic_stream_factory_fuzzer.cc
 [Build Config]: reference.md#Builder-configurations
+[its own documentation]: http://llvm.org/docs/LibFuzzer.html#output
+[Getting Started with libprotobuf-mutator in Chromium]: libprotobuf-mutator.md
+[base::FuzzedDataProvider]: https://cs.chromium.org/chromium/src/base/test/fuzzed_data_provider.h
\ No newline at end of file
diff --git a/testing/libfuzzer/reference.md b/testing/libfuzzer/reference.md
index aa6480e..04b3459c 100644
--- a/testing/libfuzzer/reference.md
+++ b/testing/libfuzzer/reference.md
@@ -1,6 +1,6 @@
 # libFuzzer Integration Reference
 
-## Additional sanitizer configuration
+## Additional Sanitizer Configuration
 
 ### MSan
 
@@ -79,11 +79,13 @@
 
 | Argument | Description |
 |----------|-------------|
-| sources | **required** list of fuzzer test source files |
-| deps | fuzzer dependencies |
-| additional_configs | additional GN configurations to be used for compilation |
-| dict | a dictionary file for the fuzzer |
-| libfuzzer_options | runtime options file for the fuzzer. See [Fuzzer Runtime Options](#Fuzzer-Runtime-Options) |
+| `sources` | **required** list of fuzzer test source files |
+| `deps` | fuzzer dependencies |
+| `additional_configs` | additional GN configurations to be used for compilation |
+| `dict` | a dictionary file for the fuzzer |
+| `libfuzzer_options` | runtime options file for the fuzzer. See [Fuzzer Runtime Options](#Fuzzer-Runtime-Options) |
+| `seed_corpus` | single directory containing test inputs, parsed recursively |
+| `seed_corpuses` | multiple directories with the same purpose as `seed_corpus` |
 
 
 ## Fuzzer Runtime Options
diff --git a/testing/trigger_scripts/PRESUBMIT.py b/testing/trigger_scripts/PRESUBMIT.py
index d47132a..f4042bc 100644
--- a/testing/trigger_scripts/PRESUBMIT.py
+++ b/testing/trigger_scripts/PRESUBMIT.py
@@ -40,7 +40,7 @@
     cl,
     [
       'luci.chromium.try:linux_optional_gpu_tests_rel',
-      'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel',
+      'luci.chromium.try:mac_optional_gpu_tests_rel',
       'master.tryserver.chromium.win:win_optional_gpu_tests_rel',
       'master.tryserver.chromium.android:android_optional_gpu_tests_rel',
     ],
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index b0c9d31..7689b43 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1586,38 +1586,19 @@
 crbug.com/738493 http/tests/performance-timing/longtask-v2/longtask-executescript.html [ Skip ]
 
 # Expected failures during incremental implementation of mojo notification.
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworker-notification-event.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworker-notification-properties.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworker-notificationclick-event-action-reflection.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworker-notificationclick-event-data-reflection.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworker-notificationclick-event-reply-reflection.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworker-notificationclick-openwindow-crash.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworker-notificationclose-event-data-reflection.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-document-click.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-document-close.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-document-data-invalid.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-document-direction.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-document-fetch-resources.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-document-image-404.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-document-image-redirect-loop.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-document-image-redirect.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-document-image-slow-404.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-document-image-slow.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-get-close.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-get-empty.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-get-filter.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-get-from-service-worker.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-get-replacement.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-get.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-service-worker-click.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-service-worker-fetch-resources.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-service-worker-get-filter.html [ Skip ]
 crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-service-worker-get.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-service-worker-image-404.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-service-worker-image-redirect-loop.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-service-worker-image-redirect.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-service-worker-image-slow-404.html [ Skip ]
-crbug.com/796991 virtual/mojo-notifications/http/tests/notifications/serviceworkerregistration-service-worker-image-slow.html [ Skip ]
 
 crbug.com/713587 external/wpt/css/css-ui/caret-color-006.html [ Skip ]
 
diff --git a/third_party/WebKit/LayoutTests/animations/web-animations/animation-state-changes-negative-playback-rate.html b/third_party/WebKit/LayoutTests/animations/web-animations/animation-state-changes-negative-playback-rate.html
index f5d57729..ec47fd65 100644
--- a/third_party/WebKit/LayoutTests/animations/web-animations/animation-state-changes-negative-playback-rate.html
+++ b/third_party/WebKit/LayoutTests/animations/web-animations/animation-state-changes-negative-playback-rate.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Test play state changes for animations with a negative playback rate</title>
-<link rel="help" href="http://w3c.github.io/web-animations/#play-state">
+<link rel="help" href="https://drafts.csswg.org/web-animations/#play-state">
 <script src="../../external/wpt/web-animations/testcommon.js"></script>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
diff --git a/third_party/WebKit/LayoutTests/animations/web-animations/animation-state-changes-positive-playback-rate.html b/third_party/WebKit/LayoutTests/animations/web-animations/animation-state-changes-positive-playback-rate.html
index db9910a8..0982146 100644
--- a/third_party/WebKit/LayoutTests/animations/web-animations/animation-state-changes-positive-playback-rate.html
+++ b/third_party/WebKit/LayoutTests/animations/web-animations/animation-state-changes-positive-playback-rate.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Tests for discrete animation</title>
-<link rel="help" href="https://w3c.github.io/web-animations/#play-state">
+<link rel="help" href="https://drafts.csswg.org/web-animations/#play-state">
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="../../external/wpt/web-animations/testcommon.js"></script>
diff --git a/third_party/WebKit/LayoutTests/animations/web-animations/the-effect-value-of-a-keyframe-effect.html b/third_party/WebKit/LayoutTests/animations/web-animations/the-effect-value-of-a-keyframe-effect.html
index 5dc6253..5ddfb15 100644
--- a/third_party/WebKit/LayoutTests/animations/web-animations/the-effect-value-of-a-keyframe-effect.html
+++ b/third_party/WebKit/LayoutTests/animations/web-animations/the-effect-value-of-a-keyframe-effect.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Keyframe handling tests</title>
-<link rel="help" href="https://w3c.github.io/web-animations/#the-effect-value-of-a-keyframe-animation-effect">
+<link rel="help" href="https://drafts.csswg.org/web-animations/#the-effect-value-of-a-keyframe-animation-effect">
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script src="../../external/wpt/web-animations/testcommon.js"></script>
diff --git a/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-expected.png b/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-expected.png
index 496856f..cb4f20a4 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-stacking-expected.png b/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-stacking-expected.png
index 7c833e9..b28d641 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-stacking-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-stacking-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-tiled-expected.png b/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-tiled-expected.png
index 6a85f94..7017949 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-tiled-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/effect-background-blend-mode-tiled-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-1-expected.png b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-1-expected.png
index e84839d..11079957 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-1-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolated-group-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/css3/filters/nested-filter-expected.png b/third_party/WebKit/LayoutTests/css3/filters/nested-filter-expected.png
index dcc6523..2dc9945 100644
--- a/third_party/WebKit/LayoutTests/css3/filters/nested-filter-expected.png
+++ b/third_party/WebKit/LayoutTests/css3/filters/nested-filter-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index 31fc301..4152679 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -172236,15 +172236,13 @@
      {}
     ]
    ],
-   "IndexedDB/interfaces.html": [
+   "IndexedDB/interfaces.any.js": [
     [
-     "/IndexedDB/interfaces.html",
+     "/IndexedDB/interfaces.any.html",
      {}
-    ]
-   ],
-   "IndexedDB/interfaces.worker.js": [
+    ],
     [
-     "/IndexedDB/interfaces.worker.html",
+     "/IndexedDB/interfaces.any.worker.html",
      {}
     ]
    ],
@@ -248827,12 +248825,8 @@
    "251a828d333bdd3face9d20a2a28ddf0c0ffeb49",
    "testharness"
   ],
-  "IndexedDB/interfaces.html": [
-   "348a7350d749c4ea11d383d1f3e07e54a9d0d377",
-   "testharness"
-  ],
-  "IndexedDB/interfaces.worker.js": [
-   "a74350d69819f73cf9f75ac636f8793f018ba2fd",
+  "IndexedDB/interfaces.any.js": [
+   "df07f5da63c34969a24fe43bc4268418ab0a5132",
    "testharness"
   ],
   "IndexedDB/interleaved-cursors-common.js": [
@@ -369928,11 +369922,11 @@
    "testharness"
   ],
   "streams/readable-byte-streams/general-expected.txt": [
-   "b3e8e0de49f3f6b9fc0cc5dd7b6e5a53bbba291b",
+   "ee74445ee891ea959b53a27ac3a87fb4350228a1",
    "support"
   ],
   "streams/readable-byte-streams/general.dedicatedworker-expected.txt": [
-   "b3e8e0de49f3f6b9fc0cc5dd7b6e5a53bbba291b",
+   "ee74445ee891ea959b53a27ac3a87fb4350228a1",
    "support"
   ],
   "streams/readable-byte-streams/general.dedicatedworker.html": [
@@ -369944,11 +369938,11 @@
    "testharness"
   ],
   "streams/readable-byte-streams/general.js": [
-   "a7262a490c4a913811c8d0c489b06a567a448fc6",
+   "ce3c72f3a552a6b9a42b4f37be7e6a053cad10a1",
    "support"
   ],
   "streams/readable-byte-streams/general.serviceworker.https-expected.txt": [
-   "838c17c8c3a41156cf7cc48fed92cb9a6bc22046",
+   "4533c8b2f6c2af140f39328017d66728966730ab",
    "support"
   ],
   "streams/readable-byte-streams/general.serviceworker.https.html": [
@@ -369956,7 +369950,7 @@
    "testharness"
   ],
   "streams/readable-byte-streams/general.sharedworker-expected.txt": [
-   "b3e8e0de49f3f6b9fc0cc5dd7b6e5a53bbba291b",
+   "ee74445ee891ea959b53a27ac3a87fb4350228a1",
    "support"
   ],
   "streams/readable-byte-streams/general.sharedworker.html": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general-expected.txt
index 3d6778f6..3644dc00 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 76 tests; 2 PASS, 74 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 77 tests; 2 PASS, 75 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS getReader({mode: "byob"}) throws on non-bytes streams
 FAIL ReadableStream with byte source can be constructed with no errors bytes type is not yet implemented
 FAIL getReader({mode}) must perform ToString() bytes type is not yet implemented
@@ -32,6 +32,7 @@
 FAIL ReadableStream with byte source: enqueue(), close(), getReader(), then read() bytes type is not yet implemented
 FAIL ReadableStream with byte source: Respond to pull() by enqueue() bytes type is not yet implemented
 FAIL ReadableStream with byte source: Respond to pull() by enqueue() asynchronously bytes type is not yet implemented
+FAIL ReadableStream with byte source: Respond to multiple pull() by separate enqueue() bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() with a transferred ArrayBuffer bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() with too big value bytes type is not yet implemented
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.dedicatedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.dedicatedworker-expected.txt
index 3d6778f6..3644dc00 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.dedicatedworker-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.dedicatedworker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 76 tests; 2 PASS, 74 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 77 tests; 2 PASS, 75 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS getReader({mode: "byob"}) throws on non-bytes streams
 FAIL ReadableStream with byte source can be constructed with no errors bytes type is not yet implemented
 FAIL getReader({mode}) must perform ToString() bytes type is not yet implemented
@@ -32,6 +32,7 @@
 FAIL ReadableStream with byte source: enqueue(), close(), getReader(), then read() bytes type is not yet implemented
 FAIL ReadableStream with byte source: Respond to pull() by enqueue() bytes type is not yet implemented
 FAIL ReadableStream with byte source: Respond to pull() by enqueue() asynchronously bytes type is not yet implemented
+FAIL ReadableStream with byte source: Respond to multiple pull() by separate enqueue() bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() with a transferred ArrayBuffer bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() with too big value bytes type is not yet implemented
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.js b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.js
index 4207277d..39dd708 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.js
@@ -300,22 +300,28 @@
 promise_test(() => {
   let pullCount = 0;
   let controller;
-  let byobRequest;
-  let viewDefined = false;
-  let viewInfo;
+  const byobRequests = [];
 
   const stream = new ReadableStream({
     start(c) {
       controller = c;
     },
     pull() {
-      byobRequest = controller.byobRequest;
+      const byobRequest = controller.byobRequest;
       const view = byobRequest.view;
-      viewDefined = view !== undefined;
-      viewInfo = extractViewInfo(view);
-
-      view[0] = 0x01;
-      byobRequest.respond(1);
+      byobRequests[pullCount] = {
+        defined: byobRequest !== undefined,
+        viewDefined: view !== undefined,
+        viewInfo: extractViewInfo(view)
+      };
+      if (pullCount === 0) {
+        view[0] = 0x01;
+        byobRequest.respond(1);
+      } else if (pullCount === 1) {
+        view[0] = 0x02;
+        view[1] = 0x03;
+        byobRequest.respond(2);
+      }
 
       ++pullCount;
     },
@@ -326,28 +332,52 @@
   });
 
   const reader = stream.getReader();
-  const readPromise = reader.read();
-  const ignoredReadPromise = reader.read();
+  const p0 = reader.read();
+  const p1 = reader.read();
 
   assert_equals(pullCount, 0, 'No pull() as start() just finished and is not yet reflected to the state of the stream');
 
   return Promise.resolve().then(() => {
     assert_equals(pullCount, 1, 'pull() must have been invoked once');
-    assert_not_equals(byobRequest, undefined, 'byobRequest must not be undefined');
-    assert_true(viewDefined, 'byobRequest.view must not be undefined');
-    assert_equals(viewInfo.constructor, Uint8Array, 'view.constructor should be Uint8Array');
-    assert_equals(viewInfo.bufferByteLength, 16, 'view.buffer.byteLength should be 16');
-    assert_equals(viewInfo.byteOffset, 0, 'view.byteOffset should be 0');
-    assert_equals(viewInfo.byteLength, 16, 'view.byteLength should be 16');
-    return readPromise;
+    const byobRequest = byobRequests[0];
+    assert_true(byobRequest.defined, 'first byobRequest must not be undefined');
+    assert_true(byobRequest.viewDefined, 'first byobRequest.view must not be undefined');
+    const viewInfo = byobRequest.viewInfo;
+    assert_equals(viewInfo.constructor, Uint8Array, 'first view.constructor should be Uint8Array');
+    assert_equals(viewInfo.bufferByteLength, 16, 'first view.buffer.byteLength should be 16');
+    assert_equals(viewInfo.byteOffset, 0, 'first view.byteOffset should be 0');
+    assert_equals(viewInfo.byteLength, 16, 'first view.byteLength should be 16');
+
+    return p0;
   }).then(result => {
-    assert_not_equals(result.value, undefined);
-    assert_equals(result.value.constructor, Uint8Array);
-    assert_equals(result.value.buffer.byteLength, 16);
-    assert_equals(result.value.byteOffset, 0);
-    assert_equals(result.value.byteLength, 1);
-    assert_equals(result.value[0], 0x01);
-    assert_equals(pullCount, 1, 'pull() should only be invoked once');
+    assert_equals(pullCount, 2, 'pull() must have been invoked twice');
+    const value = result.value;
+    assert_not_equals(value, undefined, 'first read should have a value');
+    assert_equals(value.constructor, Uint8Array, 'first value should be a Uint8Array');
+    assert_equals(value.buffer.byteLength, 16, 'first value.buffer.byteLength should be 16');
+    assert_equals(value.byteOffset, 0, 'first value.byteOffset should be 0');
+    assert_equals(value.byteLength, 1, 'first value.byteLength should be 1');
+    assert_equals(value[0], 0x01, 'first value[0] should be 0x01');
+    const byobRequest = byobRequests[1];
+    assert_true(byobRequest.defined, 'second byobRequest must not be undefined');
+    assert_true(byobRequest.viewDefined, 'second byobRequest.view must not be undefined');
+    const viewInfo = byobRequest.viewInfo;
+    assert_equals(viewInfo.constructor, Uint8Array, 'second view.constructor should be Uint8Array');
+    assert_equals(viewInfo.bufferByteLength, 16, 'second view.buffer.byteLength should be 16');
+    assert_equals(viewInfo.byteOffset, 0, 'second view.byteOffset should be 0');
+    assert_equals(viewInfo.byteLength, 16, 'second view.byteLength should be 16');
+
+    return p1;
+  }).then(result => {
+    assert_equals(pullCount, 2, 'pull() should only be invoked twice');
+    const value = result.value;
+    assert_not_equals(value, undefined, 'second read should have a value');
+    assert_equals(value.constructor, Uint8Array, 'second value should be a Uint8Array');
+    assert_equals(value.buffer.byteLength, 16, 'second value.buffer.byteLength should be 16');
+    assert_equals(value.byteOffset, 0, 'second value.byteOffset should be 0');
+    assert_equals(value.byteLength, 2, 'second value.byteLength should be 2');
+    assert_equals(value[0], 0x02, 'second value[0] should be 0x02');
+    assert_equals(value[1], 0x03, 'second value[1] should be 0x03');
   });
 }, 'ReadableStream with byte source: autoAllocateChunkSize');
 
@@ -387,12 +417,13 @@
 
   const reader = stream.getReader();
   return reader.read().then(result => {
-    assert_not_equals(result.value, undefined);
-    assert_equals(result.value.constructor, Uint8Array);
-    assert_equals(result.value.buffer.byteLength, 16);
-    assert_equals(result.value.byteOffset, 0);
-    assert_equals(result.value.byteLength, 1);
-    assert_equals(result.value[0], 0x01);
+    const value = result.value;
+    assert_not_equals(value, undefined, 'first read should have a value');
+    assert_equals(value.constructor, Uint8Array, 'first value should be a Uint8Array');
+    assert_equals(value.buffer.byteLength, 16, 'first value.buffer.byteLength should be 16');
+    assert_equals(value.byteOffset, 0, 'first value.byteOffset should be 0');
+    assert_equals(value.byteLength, 1, 'first value.byteLength should be 1');
+    assert_equals(value[0], 0x01, 'first value[0] should be 0x01');
     const byobRequest = byobRequests[0];
     assert_true(byobRequest.defined, 'first byobRequest must not be undefined');
     assert_true(byobRequest.viewDefined, 'first byobRequest.view must not be undefined');
@@ -406,13 +437,14 @@
     const byobReader = stream.getReader({ mode: 'byob' });
     return byobReader.read(new Uint8Array(32));
   }).then(result => {
-    assert_not_equals(result.value, undefined);
-    assert_equals(result.value.constructor, Uint8Array);
-    assert_equals(result.value.buffer.byteLength, 32);
-    assert_equals(result.value.byteOffset, 0);
-    assert_equals(result.value.byteLength, 2);
-    assert_equals(result.value[0], 0x02);
-    assert_equals(result.value[1], 0x03);
+    const value = result.value;
+    assert_not_equals(value, undefined, 'second read should have a value');
+    assert_equals(value.constructor, Uint8Array, 'second value should be a Uint8Array');
+    assert_equals(value.buffer.byteLength, 32, 'second value.buffer.byteLength should be 32');
+    assert_equals(value.byteOffset, 0, 'second value.byteOffset should be 0');
+    assert_equals(value.byteLength, 2, 'second value.byteLength should be 2');
+    assert_equals(value[0], 0x02, 'second value[0] should be 0x02');
+    assert_equals(value[1], 0x03, 'second value[1] should be 0x03');
     const byobRequest = byobRequests[1];
     assert_true(byobRequest.defined, 'second byobRequest must not be undefined');
     assert_true(byobRequest.viewDefined, 'second byobRequest.view must not be undefined');
@@ -693,7 +725,7 @@
     },
     type: 'bytes'
   }, {
-    highWaterMark: 256
+    highWaterMark: 0
   });
 
   const reader = stream.getReader();
@@ -717,14 +749,63 @@
     assert_equals(result[2].done, false, 'result[2].done');
     assert_equals(result[2].value.byteLength, 1, 'result[2].value.byteLength');
     assert_equals(byobRequest, undefined, 'byobRequest should be undefined');
-    assert_equals(desiredSizes[0], 256, 'desiredSize on pull should be 256');
-    assert_equals(desiredSizes[1], 256, 'desiredSize after 1st enqueue() should be 256');
-    assert_equals(desiredSizes[2], 256, 'desiredSize after 2nd enqueue() should be 256');
+    assert_equals(desiredSizes[0], 0, 'desiredSize on pull should be 0');
+    assert_equals(desiredSizes[1], 0, 'desiredSize after 1st enqueue() should be 0');
+    assert_equals(desiredSizes[2], 0, 'desiredSize after 2nd enqueue() should be 0');
     assert_equals(pullCount, 1, 'pull() should only be called once');
   });
 }, 'ReadableStream with byte source: Respond to pull() by enqueue() asynchronously');
 
 promise_test(() => {
+  let pullCount = 0;
+
+  let byobRequest;
+  const desiredSizes = [];
+
+  const stream = new ReadableStream({
+    pull(c) {
+      byobRequest = c.byobRequest;
+      desiredSizes.push(c.desiredSize);
+
+      if (pullCount < 3) {
+        c.enqueue(new Uint8Array(1));
+      } else {
+        c.close();
+      }
+
+      ++pullCount;
+    },
+    type: 'bytes'
+  }, {
+    highWaterMark: 256
+  });
+
+  const reader = stream.getReader();
+
+  const p0 = reader.read();
+  const p1 = reader.read();
+  const p2 = reader.read();
+
+  assert_equals(pullCount, 0, 'No pull as start() just finished and is not yet reflected to the state of the stream');
+
+  return Promise.all([p0, p1, p2]).then(result => {
+    assert_equals(pullCount, 4, 'pullCount after completion of all read()s');
+
+    assert_equals(result[0].done, false, 'result[0].done');
+    assert_equals(result[0].value.byteLength, 1, 'result[0].value.byteLength');
+    assert_equals(result[1].done, false, 'result[1].done');
+    assert_equals(result[1].value.byteLength, 1, 'result[1].value.byteLength');
+    assert_equals(result[2].done, false, 'result[2].done');
+    assert_equals(result[2].value.byteLength, 1, 'result[2].value.byteLength');
+    assert_equals(byobRequest, undefined, 'byobRequest should be undefined');
+    assert_equals(desiredSizes[0], 256, 'desiredSize on pull should be 256');
+    assert_equals(desiredSizes[1], 256, 'desiredSize after 1st enqueue() should be 256');
+    assert_equals(desiredSizes[2], 256, 'desiredSize after 2nd enqueue() should be 256');
+    assert_equals(desiredSizes[3], 256, 'desiredSize after 3rd enqueue() should be 256');
+  });
+}, 'ReadableStream with byte source: Respond to multiple pull() by separate enqueue()');
+
+promise_test(() => {
   let controller;
 
   let pullCount = 0;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.serviceworker.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.serviceworker.https-expected.txt
index bce9964..de4f914 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.serviceworker.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.serviceworker.https-expected.txt
@@ -32,6 +32,7 @@
 FAIL ReadableStream with byte source: enqueue(), close(), getReader(), then read() bytes type is not yet implemented
 FAIL ReadableStream with byte source: Respond to pull() by enqueue() bytes type is not yet implemented
 FAIL ReadableStream with byte source: Respond to pull() by enqueue() asynchronously bytes type is not yet implemented
+FAIL ReadableStream with byte source: Respond to multiple pull() by separate enqueue() bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() with a transferred ArrayBuffer bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() with too big value bytes type is not yet implemented
diff --git a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.sharedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.sharedworker-expected.txt
index 3d6778f6..3644dc00 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.sharedworker-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/streams/readable-byte-streams/general.sharedworker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 76 tests; 2 PASS, 74 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 77 tests; 2 PASS, 75 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS getReader({mode: "byob"}) throws on non-bytes streams
 FAIL ReadableStream with byte source can be constructed with no errors bytes type is not yet implemented
 FAIL getReader({mode}) must perform ToString() bytes type is not yet implemented
@@ -32,6 +32,7 @@
 FAIL ReadableStream with byte source: enqueue(), close(), getReader(), then read() bytes type is not yet implemented
 FAIL ReadableStream with byte source: Respond to pull() by enqueue() bytes type is not yet implemented
 FAIL ReadableStream with byte source: Respond to pull() by enqueue() asynchronously bytes type is not yet implemented
+FAIL ReadableStream with byte source: Respond to multiple pull() by separate enqueue() bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() with a transferred ArrayBuffer bytes type is not yet implemented
 FAIL ReadableStream with byte source: read(view), then respond() with too big value bytes type is not yet implemented
diff --git a/third_party/WebKit/LayoutTests/fast/backgrounds/svg-as-mask-expected.png b/third_party/WebKit/LayoutTests/fast/backgrounds/svg-as-mask-expected.png
index a766be8..e991e62 100644
--- a/third_party/WebKit/LayoutTests/fast/backgrounds/svg-as-mask-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/backgrounds/svg-as-mask-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/js-update-bounce-expected.png b/third_party/WebKit/LayoutTests/paint/invalidation/svg/js-update-bounce-expected.png
index c7b22bf..a5cb355d 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/js-update-bounce-expected.png
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/js-update-bounce-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed-expected.png
index cf07b3d6..103a4fc 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png
index a1d97b3..d1bf01c6 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/nested-reflection-transformed2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/reflection-in-composited-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/reflection-in-composited-expected.png
index d8c2374..c349cfe3 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/reflection-in-composited-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/reflections/reflection-in-composited-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-expected.png
index b0c4012..a83ce6b 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/inline-mask-overlay-image-outset-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/css/transformed-mask-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/css/transformed-mask-expected.png
index d0519af..8c0517e2 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/css/transformed-mask-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/css/transformed-mask-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/reflections/opacity-reflection-transform-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/reflections/opacity-reflection-transform-expected.png
index 3befae5..dd62f6d1 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/reflections/opacity-reflection-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/reflections/opacity-reflection-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/linux/images/color-profile-image-filter-all-expected.png
index 2149dbc..b01938c 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/images/color-profile-image-filter-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/reflection/reflection-with-rotation-expected.png b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/reflection/reflection-with-rotation-expected.png
index 3feb84c7..f3c6567 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/reflection/reflection-with-rotation-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/reflection/reflection-with-rotation-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/as-background-image/svg-as-background-6-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/as-background-image/svg-as-background-6-expected.png
index 576dbcb..ff38505 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/as-background-image/svg-as-background-6-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/as-background-image/svg-as-background-6-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-modify-container-in-target-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-modify-container-in-target-expected.png
index 2569653c..188918b 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-modify-container-in-target-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-modify-container-in-target-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-modify-target-container-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-modify-target-container-expected.png
index a1f6443..16615c3 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-modify-target-container-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-modify-target-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-g-containing-use-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-g-containing-use-expected.png
index 417b7b4e..a9c4a082 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-g-containing-use-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-g-containing-use-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-g-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-g-expected.png
index 76adf282..6be19ce 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-g-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-g-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-use-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-use-expected.png
index 76adf282..6be19ce 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-use-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/use-on-use-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png
index 9bbb5f1..8e3e4de 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
index 5b7d327f..e773019a 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/complex-text-opacity-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/complex-text-opacity-expected.png
index bba29e1a..ffd72f9 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/complex-text-opacity-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/complex-text-opacity-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
new file mode 100644
index 0000000..5d54f98
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
new file mode 100644
index 0000000..6a8b794
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/complex-text-opacity-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/complex-text-opacity-expected.png
index 07c88577..4440daa 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/complex-text-opacity-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/complex-text-opacity-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/svg/as-background-image/svg-as-background-6-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/svg/as-background-image/svg-as-background-6-expected.png
index 8b0dcc3..16c5c3d 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/svg/as-background-image/svg-as-background-6-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/svg/as-background-image/svg-as-background-6-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed-expected.png
index 6cf33580..5050e02e 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png
index d1898f9..a344d8c9 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/nested-reflection-transformed2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/reflection-in-composited-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/reflection-in-composited-expected.png
index 42a29d4..09073d2 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/reflection-in-composited-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/reflections/reflection-in-composited-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/css/transformed-mask-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/css/transformed-mask-expected.png
index e2e8177..5a12b1f28 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/css/transformed-mask-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/css/transformed-mask-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/reflections/opacity-reflection-transform-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/reflections/opacity-reflection-transform-expected.png
index 20409607..1498516 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/reflections/opacity-reflection-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/reflections/opacity-reflection-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/complex-text-opacity-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/text/complex-text-opacity-expected.png
index 68aa2191..c79519f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/text/complex-text-opacity-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/complex-text-opacity-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/color-profile-image-filter-all-expected.png
index fd971118..1dcba1c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/images/color-profile-image-filter-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/reflection/reflection-with-rotation-expected.png b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/reflection/reflection-with-rotation-expected.png
index 9278f54..0b3c73a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/reflection/reflection-with-rotation-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/reflection/reflection-with-rotation-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/svg-as-background-6-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/svg-as-background-6-expected.png
index ef23439..7244078 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/svg-as-background-6-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/svg-as-background-6-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/textProperties-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/textProperties-expected.png
index 8d0edd65..21b0f8d9 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/textProperties-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/batik/text/textProperties-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-modify-container-in-target-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-modify-container-in-target-expected.png
index a9f03fd9..55857de 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-modify-container-in-target-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-modify-container-in-target-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-modify-target-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-modify-target-container-expected.png
index 028c56e2..152b1fd 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-modify-target-container-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-modify-target-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-g-containing-use-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-g-containing-use-expected.png
index 5ddb6d8..cde12bc 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-g-containing-use-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-g-containing-use-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-g-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-g-expected.png
index 2f6f213..ff435a5 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-g-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-g-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-use-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-use-expected.png
index 2f6f213..ff435a5 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-use-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/use-on-use-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png
index 59298bf..0279054 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
index 6a8b794..5d54f98 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed-expected.png
index 54d8a6c..3349df3 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png
index a80877f..19c47c13 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/nested-reflection-transformed2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/reflection-in-composited-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/reflection-in-composited-expected.png
index 81930342..6f6ae4c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/reflection-in-composited-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/reflections/reflection-in-composited-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-expected.png
index edf945e..b70efdb4c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/borders/inline-mask-overlay-image-outset-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/css/transformed-mask-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/css/transformed-mask-expected.png
index 7b3ffcb..ffe69da 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/css/transformed-mask-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/css/transformed-mask-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/reflections/opacity-reflection-transform-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/reflections/opacity-reflection-transform-expected.png
index a2cc665..298a6a55 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/reflections/opacity-reflection-transform-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/reflections/opacity-reflection-transform-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/images/color-profile-filter-expected.png b/third_party/WebKit/LayoutTests/platform/win/images/color-profile-filter-expected.png
index 9f0c22b..1006e4e8 100644
--- a/third_party/WebKit/LayoutTests/platform/win/images/color-profile-filter-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/images/color-profile-filter-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/win/images/color-profile-image-filter-all-expected.png
index be25732..cc8f2a8 100644
--- a/third_party/WebKit/LayoutTests/platform/win/images/color-profile-image-filter-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/reflection/reflection-with-rotation-expected.png b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/reflection/reflection-with-rotation-expected.png
index 101baa2..62c820c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/reflection/reflection-with-rotation-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/reflection/reflection-with-rotation-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/render-groups-01-b-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/render-groups-01-b-expected.png
index 6d38a78..2d01404 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/render-groups-01-b-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/W3C-SVG-1.1/render-groups-01-b-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/as-background-image/svg-as-background-6-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/as-background-image/svg-as-background-6-expected.png
index 0642bf1..b2c78b2 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/as-background-image/svg-as-background-6-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/as-background-image/svg-as-background-6-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-modify-container-in-target-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-modify-container-in-target-expected.png
index 504798d..7de0059 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-modify-container-in-target-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-modify-container-in-target-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-modify-target-container-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-modify-target-container-expected.png
index 453140f2..7ae902f 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-modify-target-container-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-modify-target-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-g-containing-use-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-g-containing-use-expected.png
index 21bf084..79bf3b4 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-g-containing-use-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-g-containing-use-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-g-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-g-expected.png
index 1d343ff..0a27b6c3 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-g-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-g-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-use-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-use-expected.png
index 1d343ff..0a27b6c3 100644
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-use-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/svg/custom/use-on-use-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-filter-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-filter-expected.png
index b2718f1..1131e67 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-filter-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-filter-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png
index 281ce1b..a66da2f 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-filter-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-filter-expected.png
index 6482a48..c7ce59f 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-filter-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-filter-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
index 68d2136..37ee211 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/svg/custom/group-opacity-expected.png b/third_party/WebKit/LayoutTests/svg/custom/group-opacity-expected.png
index 8a858024..98607db 100644
--- a/third_party/WebKit/LayoutTests/svg/custom/group-opacity-expected.png
+++ b/third_party/WebKit/LayoutTests/svg/custom/group-opacity-expected.png
Binary files differ
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8BindingForCore.cpp b/third_party/WebKit/Source/bindings/core/v8/V8BindingForCore.cpp
index a139bcb..351c9d8d 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8BindingForCore.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8BindingForCore.cpp
@@ -740,6 +740,8 @@
 
 v8::Local<v8::Context> ToV8ContextEvenIfDetached(LocalFrame* frame,
                                                  DOMWrapperWorld& world) {
+  // TODO(yukishiino): this method probably should not force context creation,
+  // but it does through WindowProxy() call.
   DCHECK(frame);
   return frame->WindowProxy(world)->ContextIfInitialized();
 }
diff --git a/third_party/WebKit/Source/core/animation/Animation.idl b/third_party/WebKit/Source/core/animation/Animation.idl
index 87ed0c8..dbce735 100644
--- a/third_party/WebKit/Source/core/animation/Animation.idl
+++ b/third_party/WebKit/Source/core/animation/Animation.idl
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// https://w3c.github.io/web-animations/#animation
+// https://drafts.csswg.org/web-animations/#animation
 
 enum AnimationPlayState { "idle", "pending", "running", "paused", "finished" };
 
diff --git a/third_party/WebKit/Source/core/animation/AnimationEffectReadOnly.h b/third_party/WebKit/Source/core/animation/AnimationEffectReadOnly.h
index 8690e3c..60085ef0e 100644
--- a/third_party/WebKit/Source/core/animation/AnimationEffectReadOnly.h
+++ b/third_party/WebKit/Source/core/animation/AnimationEffectReadOnly.h
@@ -59,7 +59,7 @@
 }
 
 // Represents the content of an Animation and its fractional timing state.
-// http://w3c.github.io/web-animations/#animation-effect
+// http://drafts.csswg.org/web-animations/#animation-effect
 class CORE_EXPORT AnimationEffectReadOnly : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
   // Calls Attach/Detach, GetAnimation, UpdateInheritedTime.
diff --git a/third_party/WebKit/Source/core/animation/AnimationEffectReadOnly.idl b/third_party/WebKit/Source/core/animation/AnimationEffectReadOnly.idl
index 5f46ae56..7c4a980 100644
--- a/third_party/WebKit/Source/core/animation/AnimationEffectReadOnly.idl
+++ b/third_party/WebKit/Source/core/animation/AnimationEffectReadOnly.idl
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// https://w3c.github.io/web-animations/#the-animationeffectreadonly-interface
+// https://drafts.csswg.org/web-animations/#the-animationeffectreadonly-interface
 
 [
     RuntimeEnabled=WebAnimationsAPI
diff --git a/third_party/WebKit/Source/core/animation/AnimationEffectTiming.idl b/third_party/WebKit/Source/core/animation/AnimationEffectTiming.idl
index 502da5e..13f3633f 100644
--- a/third_party/WebKit/Source/core/animation/AnimationEffectTiming.idl
+++ b/third_party/WebKit/Source/core/animation/AnimationEffectTiming.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#the-animationeffecttiming-interface
+// https://drafts.csswg.org/web-animations/#the-animationeffecttiming-interface
 
 [
     RuntimeEnabled=WebAnimationsAPI
diff --git a/third_party/WebKit/Source/core/animation/AnimationEffectTimingProperties.idl b/third_party/WebKit/Source/core/animation/AnimationEffectTimingProperties.idl
index 005894a..bd71345a 100644
--- a/third_party/WebKit/Source/core/animation/AnimationEffectTimingProperties.idl
+++ b/third_party/WebKit/Source/core/animation/AnimationEffectTimingProperties.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#the-animationeffecttimingproperties-dictionary
+// https://drafts.csswg.org/web-animations/#the-animationeffecttimingproperties-dictionary
 
 enum FillMode { "none", "forwards", "backwards", "both", "auto" };
 enum PlaybackDirection { "normal", "reverse", "alternate", "alternate-reverse" };
diff --git a/third_party/WebKit/Source/core/animation/AnimationEffectTimingReadOnly.idl b/third_party/WebKit/Source/core/animation/AnimationEffectTimingReadOnly.idl
index 294bb70..2c00b09e 100644
--- a/third_party/WebKit/Source/core/animation/AnimationEffectTimingReadOnly.idl
+++ b/third_party/WebKit/Source/core/animation/AnimationEffectTimingReadOnly.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#the-animationeffecttimingreadonly-interface
+// https://drafts.csswg.org/web-animations/#the-animationeffecttimingreadonly-interface
 // TODO(suzyh): Use enums instead of DOMStrings where specced
 
 [
diff --git a/third_party/WebKit/Source/core/animation/AnimationTimeline.idl b/third_party/WebKit/Source/core/animation/AnimationTimeline.idl
index dd32f96f..4d4d6ea 100644
--- a/third_party/WebKit/Source/core/animation/AnimationTimeline.idl
+++ b/third_party/WebKit/Source/core/animation/AnimationTimeline.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#the-animationtimeline-interface
+// https://drafts.csswg.org/web-animations/#the-animationtimeline-interface
 
 [
     RuntimeEnabled=WebAnimationsAPI
diff --git a/third_party/WebKit/Source/core/animation/ComputedTimingProperties.idl b/third_party/WebKit/Source/core/animation/ComputedTimingProperties.idl
index 1719f70e..9334d74 100644
--- a/third_party/WebKit/Source/core/animation/ComputedTimingProperties.idl
+++ b/third_party/WebKit/Source/core/animation/ComputedTimingProperties.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#the-computedtimingproperties-dictionary
+// https://drafts.csswg.org/web-animations/#the-computedtimingproperties-dictionary
 
 dictionary ComputedTimingProperties : AnimationEffectTimingProperties {
     unrestricted double  endTime;
diff --git a/third_party/WebKit/Source/core/animation/DocumentAnimation.idl b/third_party/WebKit/Source/core/animation/DocumentAnimation.idl
index 1ccdcc8..abefa7f 100644
--- a/third_party/WebKit/Source/core/animation/DocumentAnimation.idl
+++ b/third_party/WebKit/Source/core/animation/DocumentAnimation.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#extensions-to-the-document-interface
+// https://drafts.csswg.org/web-animations/#extensions-to-the-document-interface
 
 [
     ImplementedAs=DocumentAnimation,
diff --git a/third_party/WebKit/Source/core/animation/DocumentTimeline.idl b/third_party/WebKit/Source/core/animation/DocumentTimeline.idl
index 54f9faa3..8a5aed4 100644
--- a/third_party/WebKit/Source/core/animation/DocumentTimeline.idl
+++ b/third_party/WebKit/Source/core/animation/DocumentTimeline.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#the-documenttimeline-interface
+// https://drafts.csswg.org/web-animations/#the-documenttimeline-interface
 
 [
     Constructor(optional DocumentTimelineOptions options),
diff --git a/third_party/WebKit/Source/core/animation/DocumentTimelineOptions.idl b/third_party/WebKit/Source/core/animation/DocumentTimelineOptions.idl
index 6730f6e..d3ea63bc 100644
--- a/third_party/WebKit/Source/core/animation/DocumentTimelineOptions.idl
+++ b/third_party/WebKit/Source/core/animation/DocumentTimelineOptions.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#dictdef-documenttimelineoptions
+// https://drafts.csswg.org/web-animations/#dictdef-documenttimelineoptions
 
 dictionary DocumentTimelineOptions {
   DOMHighResTimeStamp originTime = 0;
diff --git a/third_party/WebKit/Source/core/animation/ElementAnimation.idl b/third_party/WebKit/Source/core/animation/ElementAnimation.idl
index a029c1e..3e56e3d 100644
--- a/third_party/WebKit/Source/core/animation/ElementAnimation.idl
+++ b/third_party/WebKit/Source/core/animation/ElementAnimation.idl
@@ -28,8 +28,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// https://w3c.github.io/web-animations/#the-animatable-interface
-// https://w3c.github.io/web-animations/#extensions-to-the-element-interface
+// https://drafts.csswg.org/web-animations/#the-animatable-interface
+// https://drafts.csswg.org/web-animations/#extensions-to-the-element-interface
 
 // TODO(dstockwell): This should be an Animatable interface, where Element
 // implements Animatable.
diff --git a/third_party/WebKit/Source/core/animation/Keyframe.h b/third_party/WebKit/Source/core/animation/Keyframe.h
index 0d0211f..7c2192e0 100644
--- a/third_party/WebKit/Source/core/animation/Keyframe.h
+++ b/third_party/WebKit/Source/core/animation/Keyframe.h
@@ -40,7 +40,7 @@
 //     an underlying value. If this is null, the keyframe effect composite
 //     operation is used instead.
 //
-// For spec details, refer to: http://w3c.github.io/web-animations/#keyframe
+// For spec details, refer to: https://drafts.csswg.org/web-animations/#keyframe
 //
 // Implementation-wise the base Keyframe class captures the offset, composite
 // operation, and timing function. It is left to subclasses to define and store
diff --git a/third_party/WebKit/Source/core/animation/KeyframeAnimationOptions.idl b/third_party/WebKit/Source/core/animation/KeyframeAnimationOptions.idl
index d83463e..1a62ca1 100644
--- a/third_party/WebKit/Source/core/animation/KeyframeAnimationOptions.idl
+++ b/third_party/WebKit/Source/core/animation/KeyframeAnimationOptions.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#dictdef-keyframeanimationoptions
+// https://drafts.csswg.org/web-animations/#dictdef-keyframeanimationoptions
 
 dictionary KeyframeAnimationOptions : KeyframeEffectOptions {
     DOMString id = "";
diff --git a/third_party/WebKit/Source/core/animation/KeyframeEffect.h b/third_party/WebKit/Source/core/animation/KeyframeEffect.h
index cf9966f7..175b034 100644
--- a/third_party/WebKit/Source/core/animation/KeyframeEffect.h
+++ b/third_party/WebKit/Source/core/animation/KeyframeEffect.h
@@ -43,7 +43,7 @@
 class UnrestrictedDoubleOrKeyframeEffectOptions;
 
 // Represents the effect of an Animation on an Element's properties.
-// http://w3c.github.io/web-animations/#keyframe-effect
+// https://drafts.csswg.org/web-animations/#keyframe-effect
 class CORE_EXPORT KeyframeEffect final : public KeyframeEffectReadOnly {
   DEFINE_WRAPPERTYPEINFO();
 
diff --git a/third_party/WebKit/Source/core/animation/KeyframeEffect.idl b/third_party/WebKit/Source/core/animation/KeyframeEffect.idl
index ee6b903..4d8aab90 100644
--- a/third_party/WebKit/Source/core/animation/KeyframeEffect.idl
+++ b/third_party/WebKit/Source/core/animation/KeyframeEffect.idl
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// https://w3c.github.io/web-animations/#the-keyframeeffect-interfaces
+// https://drafts.csswg.org/web-animations/#the-keyframeeffect-interfaces
 
 [
     Constructor(Element? target, object? keyframes, optional (unrestricted double or KeyframeEffectOptions) options),
diff --git a/third_party/WebKit/Source/core/animation/KeyframeEffectOptions.idl b/third_party/WebKit/Source/core/animation/KeyframeEffectOptions.idl
index 0cb3f64..1fdcc6c 100644
--- a/third_party/WebKit/Source/core/animation/KeyframeEffectOptions.idl
+++ b/third_party/WebKit/Source/core/animation/KeyframeEffectOptions.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#the-keyframeeffectoptions-dictionary
+// https://drafts.csswg.org/web-animations/#the-keyframeeffectoptions-dictionary
 
 dictionary KeyframeEffectOptions : AnimationEffectTimingProperties {
     // TODO(alancutter): Implement iterationComposite
diff --git a/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.cpp b/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.cpp
index 190a1f8..c731c83db 100644
--- a/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.cpp
+++ b/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.cpp
@@ -345,7 +345,7 @@
   // consists of the normal keyframe data combined with the computed offset for
   // the given keyframe.
   //
-  // https://w3c.github.io/web-animations/#dom-keyframeeffectreadonly-getkeyframes
+  // https://drafts.csswg.org/web-animations/#dom-keyframeeffectreadonly-getkeyframes
   const KeyframeVector& keyframes = model_->GetFrames();
   Vector<double> computed_offsets =
       KeyframeEffectModelBase::GetComputedOffsets(keyframes);
diff --git a/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.h b/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.h
index 9c1dfd37..b7de7289 100644
--- a/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.h
+++ b/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.h
@@ -21,7 +21,7 @@
 class UnrestrictedDoubleOrKeyframeEffectOptions;
 
 // Represents the effect of an Animation on an Element's properties.
-// http://w3c.github.io/web-animations/#the-keyframeeffect-interfaces
+// https://drafts.csswg.org/web-animations/#the-keyframeeffect-interfaces
 class CORE_EXPORT KeyframeEffectReadOnly : public AnimationEffectReadOnly {
   DEFINE_WRAPPERTYPEINFO();
 
diff --git a/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.idl b/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.idl
index 72fc2ed..1f3d7c3 100644
--- a/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.idl
+++ b/third_party/WebKit/Source/core/animation/KeyframeEffectReadOnly.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#the-keyframeeffect-interfaces
+// https://drafts.csswg.org/web-animations/#the-keyframeeffect-interfaces
 
 enum CompositeOperation { "replace", "add", "accumulate" };
 
diff --git a/third_party/WebKit/Source/core/animation/TimingCalculations.h b/third_party/WebKit/Source/core/animation/TimingCalculations.h
index 874c2db..2212692 100644
--- a/third_party/WebKit/Source/core/animation/TimingCalculations.h
+++ b/third_party/WebKit/Source/core/animation/TimingCalculations.h
@@ -175,7 +175,7 @@
   double iteration_time = fmod(scaled_active_time, iteration_duration);
 
   // This implements step 3 of
-  // http://w3c.github.io/web-animations/#calculating-the-simple-iteration-progress
+  // https://drafts.csswg.org/web-animations/#calculating-the-simple-iteration-progress
   if (iteration_time == 0 && phase == AnimationEffectReadOnly::kPhaseAfter &&
       repeated_duration != 0 && scaled_active_time != 0)
     return iteration_duration;
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
index 2a79ae6..af084f2 100644
--- a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
+++ b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
@@ -1228,7 +1228,7 @@
 }
 
 // Properties that affect animations are not allowed to be affected by
-// animations. http://w3c.github.io/web-animations/#not-animatable-section
+// animations. https://drafts.csswg.org/web-animations/#not-animatable-section
 bool CSSAnimations::IsAnimationAffectingProperty(const CSSProperty& property) {
   switch (property.PropertyID()) {
     case CSSPropertyAnimation:
diff --git a/third_party/WebKit/Source/core/events/AnimationPlaybackEvent.idl b/third_party/WebKit/Source/core/events/AnimationPlaybackEvent.idl
index 7558260..b26c27d 100644
--- a/third_party/WebKit/Source/core/events/AnimationPlaybackEvent.idl
+++ b/third_party/WebKit/Source/core/events/AnimationPlaybackEvent.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#the-animationplaybackevent-interface
+// https://drafts.csswg.org/web-animations/#the-animationplaybackevent-interface
 
 [
     Constructor(DOMString type, optional AnimationPlaybackEventInit eventInitDict),
diff --git a/third_party/WebKit/Source/core/events/AnimationPlaybackEventInit.idl b/third_party/WebKit/Source/core/events/AnimationPlaybackEventInit.idl
index c8c82b1..bce162f 100644
--- a/third_party/WebKit/Source/core/events/AnimationPlaybackEventInit.idl
+++ b/third_party/WebKit/Source/core/events/AnimationPlaybackEventInit.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://w3c.github.io/web-animations/#the-animationplaybackevent-interface
+// https://drafts.csswg.org/web-animations/#the-animationplaybackevent-interface
 
 dictionary AnimationPlaybackEventInit : EventInit {
     double? currentTime = null;
diff --git a/third_party/WebKit/Source/core/exported/WebDocumentLoaderImpl.cpp b/third_party/WebKit/Source/core/exported/WebDocumentLoaderImpl.cpp
index be4454f..68fd3aa 100644
--- a/third_party/WebKit/Source/core/exported/WebDocumentLoaderImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebDocumentLoaderImpl.cpp
@@ -200,12 +200,8 @@
   DocumentLoader::SetUserActivated();
 }
 
-void WebDocumentLoaderImpl::SetIsAdSubframe(bool is_ad_subframe) {
-  GetSubresourceFilter()->SetIsAdSubframe(is_ad_subframe);
-}
-
 bool WebDocumentLoaderImpl::GetIsAdSubframe() const {
-  return GetSubresourceFilter()->GetIsAdSubframe();
+  return GetSubresourceFilter()->GetIsAssociatedWithAdSubframe();
 }
 
 void WebDocumentLoaderImpl::Trace(blink::Visitor* visitor) {
diff --git a/third_party/WebKit/Source/core/exported/WebDocumentLoaderImpl.h b/third_party/WebKit/Source/core/exported/WebDocumentLoaderImpl.h
index a2a2abc..ca60add 100644
--- a/third_party/WebKit/Source/core/exported/WebDocumentLoaderImpl.h
+++ b/third_party/WebKit/Source/core/exported/WebDocumentLoaderImpl.h
@@ -85,7 +85,6 @@
   void SetSourceLocation(const WebSourceLocation&) override;
   void ResetSourceLocation() override;
   void SetUserActivated() override;
-  void SetIsAdSubframe(bool is_ad_subframe) override;
   bool GetIsAdSubframe() const override;
 
   static WebNavigationType ToWebNavigationType(NavigationType);
diff --git a/third_party/WebKit/Source/core/exported/WebDocumentSubresourceFilterTest.cpp b/third_party/WebKit/Source/core/exported/WebDocumentSubresourceFilterTest.cpp
index db9ea7d..751519a 100644
--- a/third_party/WebKit/Source/core/exported/WebDocumentSubresourceFilterTest.cpp
+++ b/third_party/WebKit/Source/core/exported/WebDocumentSubresourceFilterTest.cpp
@@ -53,6 +53,8 @@
     return queried_subresource_paths_;
   }
 
+  bool GetIsAssociatedWithAdSubframe() const override { return false; }
+
  private:
   std::vector<std::string> queried_subresource_paths_;
   bool allow_loads_;
diff --git a/third_party/WebKit/Source/core/exported/WebFrame.cpp b/third_party/WebKit/Source/core/exported/WebFrame.cpp
index 17674e29..a641f8d0 100644
--- a/third_party/WebKit/Source/core/exported/WebFrame.cpp
+++ b/third_party/WebKit/Source/core/exported/WebFrame.cpp
@@ -126,7 +126,7 @@
   Frame* new_frame = ToCoreFrame(*frame);
 
   if (parent_ && old_frame->HasBeenActivated())
-    new_frame->UpdateUserActivationInFrameTree();
+    new_frame->NotifyUserActivationInLocalTree();
 
   new_frame->GetWindowProxyManager()->SetGlobalProxies(global_proxies);
 
diff --git a/third_party/WebKit/Source/core/exported/WebFrameTest.cpp b/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
index 31ccf93..4b0451f 100644
--- a/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/core/exported/WebFrameTest.cpp
@@ -12195,7 +12195,8 @@
 
   // Simulate an input element focus leading to Element::focus() call with a
   // user gesture.
-  local_frame->SetHasReceivedUserGesture();
+  Frame::NotifyUserActivation(local_frame->GetFrame(),
+                              UserGestureToken::kNewGesture);
   local_frame->ExecuteScript(
       WebScriptSource("window.focus();"
                       "document.querySelector('input').focus();"));
diff --git a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
index 6acc971..8bd852f 100644
--- a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
@@ -331,7 +331,9 @@
 }
 
 void WebRemoteFrameImpl::SetHasReceivedUserGesture() {
-  Frame::NotifyUserActivation(GetFrame(), UserGestureToken::kNewGesture);
+  RemoteFrame* frame = GetFrame();
+  if (frame)
+    frame->NotifyUserActivationInLocalTree();
 }
 
 void WebRemoteFrameImpl::ScrollRectToVisible(
diff --git a/third_party/WebKit/Source/core/exported/WebUserGestureIndicator.cpp b/third_party/WebKit/Source/core/exported/WebUserGestureIndicator.cpp
index 60ef46b87..643a3c2e 100644
--- a/third_party/WebKit/Source/core/exported/WebUserGestureIndicator.cpp
+++ b/third_party/WebKit/Source/core/exported/WebUserGestureIndicator.cpp
@@ -38,14 +38,14 @@
 namespace blink {
 
 bool WebUserGestureIndicator::IsProcessingUserGesture(WebLocalFrame* frame) {
-  return Frame::HasTransientUserActivation(frame ? WebFrame::ToCoreFrame(*frame)
-                                                 : nullptr);
+  return Frame::HasTransientUserActivation(
+      frame ? ToWebLocalFrameImpl(frame)->GetFrame() : nullptr);
 }
 
 bool WebUserGestureIndicator::IsProcessingUserGestureThreadSafe(
     WebLocalFrame* frame) {
   return Frame::HasTransientUserActivation(
-      frame ? WebFrame::ToCoreFrame(*frame) : nullptr, true);
+      frame ? ToWebLocalFrameImpl(frame)->GetFrame() : nullptr, true);
 }
 
 // TODO(csharrison): consumeUserGesture() and currentUserGestureToken() use
@@ -53,13 +53,15 @@
 // updating them if they are in any sort of critical path or called often.
 bool WebUserGestureIndicator::ConsumeUserGesture(WebLocalFrame* frame) {
   return Frame::ConsumeTransientUserActivation(
-      frame ? WebFrame::ToCoreFrame(*frame) : nullptr, true);
+      frame ? ToWebLocalFrameImpl(frame)->GetFrame() : nullptr, true);
+
+  ;
 }
 
 bool WebUserGestureIndicator::ProcessedUserGestureSinceLoad(
     WebLocalFrame* frame) {
   DCHECK(frame);
-  return WebFrame::ToCoreFrame(*frame)->HasBeenActivated();
+  return ToWebLocalFrameImpl(frame)->GetFrame()->HasBeenActivated();
 }
 
 WebUserGestureToken WebUserGestureIndicator::CurrentUserGestureToken() {
diff --git a/third_party/WebKit/Source/core/frame/Frame.cpp b/third_party/WebKit/Source/core/frame/Frame.cpp
index 7eac0e9f..24b49cc 100644
--- a/third_party/WebKit/Source/core/frame/Frame.cpp
+++ b/third_party/WebKit/Source/core/frame/Frame.cpp
@@ -172,21 +172,21 @@
     child_frames[i]->DidChangeVisibilityState();
 }
 
-// TODO(mustaq): Should be merged with NotifyUserActivation() below but
-// not sure why this one doesn't update frame clients.  Could be related to
-// crbug.com/775930 .
-void Frame::UpdateUserActivationInFrameTree() {
+void Frame::NotifyUserActivationInLocalTree() {
   user_activation_state_.Activate();
-  if (Frame* parent = Tree().Parent())
-    parent->UpdateUserActivationInFrameTree();
+  for (Frame* parent = Tree().Parent(); parent;
+       parent = parent->Tree().Parent()) {
+    parent->user_activation_state_.Activate();
+  }
 }
 
 void Frame::NotifyUserActivation() {
   bool had_gesture = HasBeenActivated();
   if (RuntimeEnabledFeatures::UserActivationV2Enabled() || !had_gesture)
-    UpdateUserActivationInFrameTree();
-  if (IsLocalFrame())
-    ToLocalFrame(this)->Client()->SetHasReceivedUserGesture(had_gesture);
+    NotifyUserActivationInLocalTree();
+
+  DCHECK(IsLocalFrame());
+  ToLocalFrame(this)->Client()->SetHasReceivedUserGesture(had_gesture);
 }
 
 bool Frame::ConsumeTransientUserActivation() {
@@ -203,7 +203,7 @@
 
 // static
 std::unique_ptr<UserGestureIndicator> Frame::NotifyUserActivation(
-    Frame* frame,
+    LocalFrame* frame,
     UserGestureToken::Status status) {
   if (frame)
     frame->NotifyUserActivation();
@@ -211,7 +211,8 @@
 }
 
 // static
-bool Frame::HasTransientUserActivation(Frame* frame, bool checkIfMainThread) {
+bool Frame::HasTransientUserActivation(LocalFrame* frame,
+                                       bool checkIfMainThread) {
   if (RuntimeEnabledFeatures::UserActivationV2Enabled()) {
     return frame ? frame->HasTransientUserActivation() : false;
   }
@@ -222,7 +223,7 @@
 }
 
 // static
-bool Frame::ConsumeTransientUserActivation(Frame* frame,
+bool Frame::ConsumeTransientUserActivation(LocalFrame* frame,
                                            bool checkIfMainThread) {
   if (RuntimeEnabledFeatures::UserActivationV2Enabled()) {
     return frame ? frame->ConsumeTransientUserActivation() : false;
diff --git a/third_party/WebKit/Source/core/frame/Frame.h b/third_party/WebKit/Source/core/frame/Frame.h
index 2219f4fb..b0bfe04fc5 100644
--- a/third_party/WebKit/Source/core/frame/Frame.h
+++ b/third_party/WebKit/Source/core/frame/Frame.h
@@ -140,7 +140,8 @@
 
   virtual void DidChangeVisibilityState();
 
-  void UpdateUserActivationInFrameTree();
+  // This should never be called from outside Frame or WebFrame.
+  void NotifyUserActivationInLocalTree();
 
   bool HasBeenActivated() const {
     return user_activation_state_.HasBeenActive();
@@ -159,8 +160,10 @@
   // Creates a |UserGestureIndicator| that contains a |UserGestureToken| with
   // the given status.  Also activates the user activation state of the
   // |LocalFrame| (provided it's non-null) and all its ancestors.
+  //
+  // TODO(mustaq): Move the user activation entry-points to LocalFrame.
   static std::unique_ptr<UserGestureIndicator> NotifyUserActivation(
-      Frame*,
+      LocalFrame*,
       UserGestureToken::Status = UserGestureToken::kPossiblyExistingGesture);
 
   // Returns the transient user activation state of the |LocalFrame|, provided
@@ -171,7 +174,7 @@
   //
   // TODO(mustaq): clarify/enforce the relation between the two params after
   // null-frame main-thread cases (crbug.com/730690) have been removed.
-  static bool HasTransientUserActivation(Frame*,
+  static bool HasTransientUserActivation(LocalFrame*,
                                          bool checkIfMainThread = false);
 
   // Consumes the transient user activation state of the |LocalFrame|, provided
@@ -180,7 +183,7 @@
   //
   // The |checkIfMainThread| parameter determines if the token based gestures
   // (legacy code) must be used in a thread-safe manner.
-  static bool ConsumeTransientUserActivation(Frame*,
+  static bool ConsumeTransientUserActivation(LocalFrame*,
                                              bool checkIfMainThread = false);
 
   bool IsAttached() const {
@@ -211,7 +214,11 @@
   Member<FrameOwner> owner_;
   Member<DOMWindow> dom_window_;
 
+  // A LocalFrame is the primary "owner" of the activation state.  The state in
+  // a RemoteFrame serves as a cache for the corresponding LocalFrame state (to
+  // avoid double hops through the browser during reading).
   UserActivationState user_activation_state_;
+
   bool has_received_user_gesture_before_nav_ = false;
 
   FrameLifecycle lifecycle_;
@@ -223,6 +230,9 @@
 
  private:
   // Activates the user activation state of this frame and all its ancestors.
+  //
+  // TODO(mustaq): Move the user activation (private) entry-points to
+  // LocalFrame.
   void NotifyUserActivation();
 
   bool HasTransientUserActivation() {
diff --git a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
index 9b33674..6ccc799 100644
--- a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
@@ -2222,7 +2222,7 @@
   GetFrame()->DidSendResourceTimingInfoToParent();
 }
 
-void WebLocalFrameImpl::SetHasReceivedUserGesture() {
+void WebLocalFrameImpl::NotifyUserActivation() {
   Frame::NotifyUserActivation(GetFrame(), UserGestureToken::kNewGesture);
 }
 
diff --git a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.h b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.h
index d7d4c9f..fe164546 100644
--- a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.h
+++ b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.h
@@ -285,7 +285,7 @@
   bool IsLoading() const override;
   bool IsNavigationScheduledWithin(double interval) const override;
   void SetCommittedFirstRealLoad() override;
-  void SetHasReceivedUserGesture() override;
+  void NotifyUserActivation() override;
   void BlinkFeatureUsageReport(const std::set<int>& features) override;
   void MixedContentFound(const WebURL& main_resource_url,
                          const WebURL& mixed_content_url,
diff --git a/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp b/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp
index 595640d04..35f9ac7 100644
--- a/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp
@@ -392,8 +392,9 @@
         static_cast<String>(FastGetAttribute(downloadAttr)));
   }
   request.SetRequestContext(WebURLRequest::kRequestContextHyperlink);
-  FrameLoadRequest frame_request(&GetDocument(), request,
-                                 getAttribute(targetAttr));
+  FrameLoadRequest frame_request(
+      &GetDocument(), request,
+      hasAttribute(downloadAttr) ? g_null_atom : getAttribute(targetAttr));
   frame_request.SetTriggeringEvent(event);
   if (HasRel(kRelationNoReferrer)) {
     frame_request.SetShouldSendReferrer(kNeverSendReferrer);
diff --git a/third_party/WebKit/Source/core/inspector/ConsoleMessage.cpp b/third_party/WebKit/Source/core/inspector/ConsoleMessage.cpp
index ac90bd5..bc07cc9 100644
--- a/third_party/WebKit/Source/core/inspector/ConsoleMessage.cpp
+++ b/third_party/WebKit/Source/core/inspector/ConsoleMessage.cpp
@@ -102,7 +102,10 @@
 }
 
 LocalFrame* ConsoleMessage::Frame() const {
-  return frame_;
+  // Do not reference detached frames.
+  if (frame_ && frame_->Client())
+    return frame_;
+  return nullptr;
 }
 
 Vector<DOMNodeId>& ConsoleMessage::Nodes() {
diff --git a/third_party/WebKit/Source/core/layout/svg/SVGResourcesCycleSolver.cpp b/third_party/WebKit/Source/core/layout/svg/SVGResourcesCycleSolver.cpp
index 7236c42..eda50207 100644
--- a/third_party/WebKit/Source/core/layout/svg/SVGResourcesCycleSolver.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/SVGResourcesCycleSolver.cpp
@@ -33,15 +33,25 @@
 
 SVGResourcesCycleSolver::~SVGResourcesCycleSolver() = default;
 
-struct ActiveFrame {
+class ScopedTraversalPath {
+ public:
   typedef SVGResourcesCycleSolver::ResourceSet ResourceSet;
 
-  ActiveFrame(ResourceSet& active_set, LayoutSVGResourceContainer* resource)
-      : active_set_(active_set), resource_(resource) {
-    active_set_.insert(resource_);
+  ScopedTraversalPath(ResourceSet& active_set)
+      : active_set_(active_set), resource_(nullptr) {}
+  ~ScopedTraversalPath() {
+    if (resource_)
+      active_set_.erase(resource_);
   }
-  ~ActiveFrame() { active_set_.erase(resource_); }
 
+  bool Enter(LayoutSVGResourceContainer* resource) {
+    if (!active_set_.insert(resource).is_new_entry)
+      return false;
+    resource_ = resource;
+    return true;
+  }
+
+ private:
   ResourceSet& active_set_;
   LayoutSVGResourceContainer* resource_;
 };
@@ -53,11 +63,10 @@
   if (dag_cache_.Contains(resource))
     return false;
 
-  if (active_resources_.Contains(resource))
+  ScopedTraversalPath scope(active_resources_);
+  if (!scope.Enter(resource))
     return true;
 
-  ActiveFrame frame(active_resources_, resource);
-
   LayoutObject* node = resource;
   while (node) {
     // Skip subtrees which are themselves resources. (They will be
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp
index 181de90..d1286fef 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp
@@ -101,8 +101,12 @@
 
 class FixedPolicySubresourceFilter : public WebDocumentSubresourceFilter {
  public:
-  FixedPolicySubresourceFilter(LoadPolicy policy, int* filtered_load_counter)
-      : policy_(policy), filtered_load_counter_(filtered_load_counter) {}
+  FixedPolicySubresourceFilter(LoadPolicy policy,
+                               int* filtered_load_counter,
+                               bool is_associated_with_ad_subframe)
+      : policy_(policy),
+        filtered_load_counter_(filtered_load_counter),
+        is_associated_with_ad_subframe_(is_associated_with_ad_subframe) {}
 
   LoadPolicy GetLoadPolicy(const WebURL& resource_url,
                            WebURLRequest::RequestContext) override {
@@ -117,9 +121,14 @@
 
   bool ShouldLogToConsole() override { return false; }
 
+  bool GetIsAssociatedWithAdSubframe() const override {
+    return is_associated_with_ad_subframe_;
+  }
+
  private:
   const LoadPolicy policy_;
   int* filtered_load_counter_;
+  bool is_associated_with_ad_subframe_;
 };
 
 class FrameFetchContextTest : public ::testing::Test {
@@ -191,10 +200,12 @@
     return filtered_load_callback_counter_;
   }
 
-  void SetFilterPolicy(WebDocumentSubresourceFilter::LoadPolicy policy) {
+  void SetFilterPolicy(WebDocumentSubresourceFilter::LoadPolicy policy,
+                       bool is_associated_with_ad_subframe = false) {
     document->Loader()->SetSubresourceFilter(SubresourceFilter::Create(
         *document, std::make_unique<FixedPolicySubresourceFilter>(
-                       policy, &filtered_load_callback_counter_)));
+                       policy, &filtered_load_callback_counter_,
+                       is_associated_with_ad_subframe)));
   }
 
   ResourceRequestBlockedReason CanRequest() {
@@ -1255,8 +1266,8 @@
 // is fetched from a frame that is tagged as an ad, then the subresource should
 // be tagged as well.
 TEST_F(FrameFetchContextSubresourceFilterTest, AdTaggingBasedOnFrame) {
-  SetFilterPolicy(WebDocumentSubresourceFilter::kAllow);
-  document->Loader()->GetSubresourceFilter()->SetIsAdSubframe(true);
+  SetFilterPolicy(WebDocumentSubresourceFilter::kAllow,
+                  true /* is_associated_with_ad_subframe */);
 
   EXPECT_EQ(ResourceRequestBlockedReason::kNone, CanRequestAndVerifyIsAd(true));
   EXPECT_EQ(0, GetFilteredLoadCallCount());
diff --git a/third_party/WebKit/Source/core/loader/SubresourceFilter.cpp b/third_party/WebKit/Source/core/loader/SubresourceFilter.cpp
index d66fd8f..0a5262d 100644
--- a/third_party/WebKit/Source/core/loader/SubresourceFilter.cpp
+++ b/third_party/WebKit/Source/core/loader/SubresourceFilter.cpp
@@ -45,8 +45,7 @@
     ExecutionContext* execution_context,
     std::unique_ptr<WebDocumentSubresourceFilter> subresource_filter)
     : execution_context_(execution_context),
-      subresource_filter_(std::move(subresource_filter)),
-      is_ad_subframe_(false) {}
+      subresource_filter_(std::move(subresource_filter)) {}
 
 SubresourceFilter::~SubresourceFilter() = default;
 
@@ -88,6 +87,10 @@
   return load_policy != WebDocumentSubresourceFilter::kDisallow;
 }
 
+bool SubresourceFilter::GetIsAssociatedWithAdSubframe() {
+  return subresource_filter_->GetIsAssociatedWithAdSubframe();
+}
+
 bool SubresourceFilter::IsAdResource(
     const KURL& resource_url,
     WebURLRequest::RequestContext request_context) {
@@ -102,7 +105,8 @@
 
   // If the subresource cannot be identified as an ad via load_policy, check if
   // its frame is identified as an ad.
-  return load_policy != WebDocumentSubresourceFilter::kAllow || is_ad_subframe_;
+  return load_policy != WebDocumentSubresourceFilter::kAllow ||
+         subresource_filter_->GetIsAssociatedWithAdSubframe();
 }
 
 void SubresourceFilter::ReportLoad(
diff --git a/third_party/WebKit/Source/core/loader/SubresourceFilter.h b/third_party/WebKit/Source/core/loader/SubresourceFilter.h
index 9a14cb4..2bcbe67 100644
--- a/third_party/WebKit/Source/core/loader/SubresourceFilter.h
+++ b/third_party/WebKit/Source/core/loader/SubresourceFilter.h
@@ -36,11 +36,7 @@
                  SecurityViolationReportingPolicy);
   bool AllowWebSocketConnection(const KURL&);
 
-  void SetIsAdSubframe(bool is_ad_subframe) {
-    is_ad_subframe_ = is_ad_subframe;
-  }
-
-  bool GetIsAdSubframe() { return is_ad_subframe_; }
+  bool GetIsAssociatedWithAdSubframe();
 
   // Returns if |resource_url| is an ad resource.
   bool IsAdResource(const KURL& resource_url, WebURLRequest::RequestContext);
@@ -56,7 +52,6 @@
 
   Member<ExecutionContext> execution_context_;
   std::unique_ptr<WebDocumentSubresourceFilter> subresource_filter_;
-  bool is_ad_subframe_;
 
   // Save the last resource check's result in the single element cache.
   std::pair<std::pair<KURL, WebURLRequest::RequestContext>,
diff --git a/third_party/WebKit/Source/core/page/ContextMenuController.cpp b/third_party/WebKit/Source/core/page/ContextMenuController.cpp
index 0f4803e..f838bf5d 100644
--- a/third_party/WebKit/Source/core/page/ContextMenuController.cpp
+++ b/third_party/WebKit/Source/core/page/ContextMenuController.cpp
@@ -344,6 +344,7 @@
     } else if (IsHTMLAudioElement(*media_element))
       data.media_type = WebContextMenuData::kMediaTypeAudio;
 
+    data.suggested_filename = media_element->title();
     if (media_element->error())
       data.media_flags |= WebContextMenuData::kMediaInError;
     if (media_element->paused())
diff --git a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
index 632b2db6..b5ed32b7 100644
--- a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
+++ b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
@@ -180,13 +180,32 @@
 bool PrePaintTreeWalk::NeedsTreeBuilderContextUpdate(
     const LayoutObject& object,
     const PrePaintTreeWalkContext& parent_context) {
-  return object.NeedsPaintPropertyUpdate() ||
-         object.DescendantNeedsPaintPropertyUpdate() ||
-         (parent_context.tree_builder_context &&
-          parent_context.tree_builder_context->force_subtree_update) ||
-         // If the object needs visual rect update, we should update tree
-         // builder context which is needed by visual rect update.
-         parent_context.paint_invalidator_context.NeedsVisualRectUpdate(object);
+  if (parent_context.tree_builder_context &&
+      parent_context.tree_builder_context->force_subtree_update) {
+    return true;
+  }
+  // The following CHECKs are for debugging crbug.com/816810.
+  if (object.NeedsPaintPropertyUpdate()) {
+    CHECK(parent_context.tree_builder_context) << "NeedsPaintPropertyUpdate";
+    return true;
+  }
+  if (object.DescendantNeedsPaintPropertyUpdate()) {
+    CHECK(parent_context.tree_builder_context)
+        << "DescendantNeedsPaintPropertyUpdate";
+    return true;
+  }
+  if (parent_context.paint_invalidator_context.NeedsVisualRectUpdate(object)) {
+    // If the object needs visual rect update, we should update tree
+    // builder context which is needed by visual rect update.
+    if (object.NeedsPaintOffsetAndVisualRectUpdate()) {
+      CHECK(parent_context.tree_builder_context)
+          << "NeedsPaintOffsetAndVisualRectUpdate";
+    } else {
+      CHECK(parent_context.tree_builder_context) << "kSubtreeVisualRectUpdate";
+    }
+    return true;
+  }
+  return false;
 }
 
 void PrePaintTreeWalk::WalkInternal(const LayoutObject& object,
diff --git a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.h b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.h
index f34a27b..380a8ae 100644
--- a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.h
+++ b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.h
@@ -40,8 +40,7 @@
           ancestor_overflow_paint_layer(
               parent_context.ancestor_overflow_paint_layer) {
       if (needs_tree_builder_context || DCHECK_IS_ON()) {
-        // Speculative CHECK to debug crbug.com/816810.
-        CHECK(parent_context.tree_builder_context);
+        DCHECK(parent_context.tree_builder_context);
         tree_builder_context.emplace(*parent_context.tree_builder_context);
       }
 #if DCHECK_IS_ON()
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js b/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js
index 4d1b3c3..3eacb7ac 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js
@@ -786,8 +786,12 @@
   }
 
   _updateTabSlider() {
-    if (!this._currentTab || !this._sliderEnabled)
+    if (!this._sliderEnabled)
       return;
+    if (!this._currentTab) {
+      this._tabSlider.style.width = 0;
+      return;
+    }
     let left = 0;
     for (let i = 0; i < this._tabs.length && this._currentTab !== this._tabs[i]; i++) {
       if (this._tabs[i]._shown)
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/tabbedPane.css b/third_party/WebKit/Source/devtools/front_end/ui/tabbedPane.css
index 62ba904e..fcbd3d8c 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/tabbedPane.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/tabbedPane.css
@@ -251,8 +251,7 @@
     border-left: 6px solid #666;
 }
 
-.tabbed-pane-tab-slider,
-.-theme-selection-color {
+.tabbed-pane-tab-slider {
     height: 2px;
     position: absolute;
     bottom: -1px;
@@ -262,7 +261,10 @@
     transform-origin: 0 100%;
     transition: transform 150ms cubic-bezier(0, 0, 0.2, 1);
     visibility: hidden;
-    border-top: 1px solid var(--tab-selected-fg-color);
+}
+
+:host-context(.-theme-with-dark-background) .tabbed-pane-tab-slider {
+    display: none;
 }
 
 @media (-webkit-min-device-pixel-ratio: 1.1) {
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectTest.cpp b/third_party/WebKit/Source/modules/accessibility/AXObjectTest.cpp
index 911b73f..e74476a 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectTest.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectTest.cpp
@@ -78,7 +78,7 @@
             br->PreviousSibling()->RoleValue());
 }
 
-TEST_F(AccessibilityTest, ComparisonOperators) {
+TEST_F(AccessibilityTest, AXObjectComparisonOperators) {
   SetBodyInnerHTML(R"HTML(<input id='input' type='text' value='value'>"
                    R"<p id='paragraph'>hello<br id='br'>there</p>"
                    R"<button id='button'>button</button>)HTML");
diff --git a/third_party/WebKit/Source/modules/accessibility/AXPosition.cpp b/third_party/WebKit/Source/modules/accessibility/AXPosition.cpp
index cd2c2e6..fa4e592 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXPosition.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXPosition.cpp
@@ -24,7 +24,7 @@
   if (child.GetNode() && child.GetNode()->IsTextNode())
     return CreateFirstPositionInContainerObject(child);
 
-  const AXObject* parent = child.ParentObject();
+  const AXObject* parent = child.ParentObjectUnignored();
   DCHECK(parent);
   AXPosition position(*parent);
   position.text_offset_or_child_index_ = child.IndexInParent();
@@ -39,7 +39,7 @@
   if (child.GetNode() && child.GetNode()->IsTextNode())
     return CreateLastPositionInContainerObject(child);
 
-  const AXObject* parent = child.ParentObject();
+  const AXObject* parent = child.ParentObjectUnignored();
   DCHECK(parent);
   AXPosition position(*parent);
   position.text_offset_or_child_index_ = child.IndexInParent() + 1;
@@ -88,6 +88,8 @@
     const AXObject& container,
     int offset,
     TextAffinity affinity) {
+  DCHECK(container.GetNode() && container.GetNode()->IsTextNode())
+      << "Text positions should be anchored to a text node.";
   AXPosition position(container);
   position.text_offset_or_child_index_ = offset;
   position.affinity_ = affinity;
@@ -248,25 +250,69 @@
 }
 
 bool operator==(const AXPosition& a, const AXPosition& b) {
-  DCHECK(!a.IsValid() || !b.IsValid());
-  return a.ContainerObject() == b.ContainerObject() &&
-         a.ChildIndex() == b.ChildIndex() && a.TextOffset() == b.TextOffset() &&
-         a.Affinity() == b.Affinity();
+  DCHECK(a.IsValid() && b.IsValid());
+  if (*a.ContainerObject() != *b.ContainerObject())
+    return false;
+  if (a.IsTextPosition() && b.IsTextPosition())
+    return a.TextOffset() == b.TextOffset() && a.Affinity() == b.Affinity();
+  if (!a.IsTextPosition() && !b.IsTextPosition())
+    return a.ChildIndex() == b.ChildIndex();
+  NOTREACHED() << "AXPosition objects having the same container object should "
+                  "have the same type.";
+  return false;
 }
 
 bool operator!=(const AXPosition& a, const AXPosition& b) {
   return !(a == b);
 }
 
+bool operator<(const AXPosition& a, const AXPosition& b) {
+  DCHECK(a.IsValid() && b.IsValid());
+  if (*a.ContainerObject() > *b.ContainerObject())
+    return false;
+  if (*a.ContainerObject() < *b.ContainerObject())
+    return true;
+  if (a.IsTextPosition() && b.IsTextPosition())
+    return a.TextOffset() < b.TextOffset();
+  if (!a.IsTextPosition() && !b.IsTextPosition())
+    return a.ChildIndex() < b.ChildIndex();
+  NOTREACHED() << "AXPosition objects having the same container object should "
+                  "have the same type.";
+  return false;
+}
+
+bool operator<=(const AXPosition& a, const AXPosition& b) {
+  return a < b || a == b;
+}
+
+bool operator>(const AXPosition& a, const AXPosition& b) {
+  DCHECK(a.IsValid() && b.IsValid());
+  if (*a.ContainerObject() < *b.ContainerObject())
+    return false;
+  if (*a.ContainerObject() > *b.ContainerObject())
+    return true;
+  if (a.IsTextPosition() && b.IsTextPosition())
+    return a.TextOffset() > b.TextOffset();
+  if (!a.IsTextPosition() && !b.IsTextPosition())
+    return a.ChildIndex() > b.ChildIndex();
+  NOTREACHED() << "AXPosition objects having the same container object should "
+                  "have the same type.";
+  return false;
+}
+
+bool operator>=(const AXPosition& a, const AXPosition& b) {
+  return a > b || a == b;
+}
+
 std::ostream& operator<<(std::ostream& ostream, const AXPosition& position) {
   if (!position.IsValid())
-    return ostream << "Invalid position";
+    return ostream << "Invalid AXPosition";
   if (position.IsTextPosition()) {
-    return ostream << "Text position in " << position.ContainerObject() << ", "
-                   << position.TextOffset();
+    return ostream << "AX text position in " << position.ContainerObject()
+                   << ", " << position.TextOffset();
   }
-  return ostream << "Object anchored position in " << position.ContainerObject()
-                 << ", " << position.ChildIndex();
+  return ostream << "AX object anchored position in "
+                 << position.ContainerObject() << ", " << position.ChildIndex();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/accessibility/AXPosition.h b/third_party/WebKit/Source/modules/accessibility/AXPosition.h
index 93990fdc..95512d3 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXPosition.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXPosition.h
@@ -96,6 +96,10 @@
 
 MODULES_EXPORT bool operator==(const AXPosition&, const AXPosition&);
 MODULES_EXPORT bool operator!=(const AXPosition&, const AXPosition&);
+MODULES_EXPORT bool operator<(const AXPosition&, const AXPosition&);
+MODULES_EXPORT bool operator<=(const AXPosition&, const AXPosition&);
+MODULES_EXPORT bool operator>(const AXPosition&, const AXPosition&);
+MODULES_EXPORT bool operator>=(const AXPosition&, const AXPosition&);
 MODULES_EXPORT std::ostream& operator<<(std::ostream&, const AXPosition&);
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/accessibility/AXPositionTest.cpp b/third_party/WebKit/Source/modules/accessibility/AXPositionTest.cpp
index bf69bb3..e8e4f6b 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXPositionTest.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXPositionTest.cpp
@@ -133,6 +133,64 @@
 TEST_F(AccessibilityTest, PositionFromPosition) {}
 
 //
+// Test comparing two AXPosition objects based on their position in the
+// accessibility tree.
+//
+
+TEST_F(AccessibilityTest, AXPositionComparisonOperators) {
+  SetBodyInnerHTML(R"HTML(<input id='input' type='text' value='value'>"
+                   R"<p id='paragraph'>hello<br>there</p>)HTML");
+
+  const AXObject* root = GetAXRootObject();
+  ASSERT_NE(nullptr, root);
+  const auto root_first =
+      AXPosition::CreateFirstPositionInContainerObject(*root);
+  const auto root_last = AXPosition::CreateLastPositionInContainerObject(*root);
+
+  const AXObject* input = GetAXObjectByElementId("input");
+  ASSERT_NE(nullptr, input);
+  const auto input_before = AXPosition::CreatePositionBeforeObject(*input);
+  const auto input_after = AXPosition::CreatePositionAfterObject(*input);
+
+  const AXObject* paragraph = GetAXObjectByElementId("paragraph");
+  ASSERT_NE(nullptr, paragraph);
+  ASSERT_NE(nullptr, paragraph->FirstChild());
+  ASSERT_NE(nullptr, paragraph->LastChild());
+  const auto paragraph_before =
+      AXPosition::CreatePositionBeforeObject(*paragraph->FirstChild());
+  const auto paragraph_after =
+      AXPosition::CreatePositionAfterObject(*paragraph->LastChild());
+  const auto paragraph_start =
+      AXPosition::CreatePositionInTextObject(*paragraph->FirstChild(), 0);
+  const auto paragraph_end =
+      AXPosition::CreatePositionInTextObject(*paragraph->LastChild(), 5);
+
+  EXPECT_TRUE(root_first == root_first);
+  EXPECT_TRUE(root_last == root_last);
+  EXPECT_FALSE(root_first != root_first);
+  EXPECT_TRUE(root_first != root_last);
+
+  EXPECT_TRUE(root_first < root_last);
+  EXPECT_TRUE(root_first <= root_first);
+  EXPECT_TRUE(root_last > root_first);
+  EXPECT_TRUE(root_last >= root_last);
+
+  EXPECT_TRUE(input_before == root_first);
+  EXPECT_TRUE(input_after > root_first);
+  EXPECT_TRUE(input_after >= root_first);
+  EXPECT_FALSE(input_before < root_first);
+  EXPECT_TRUE(input_before <= root_first);
+
+  //
+  // Text positions.
+  //
+
+  EXPECT_TRUE(paragraph_before == paragraph_start);
+  EXPECT_TRUE(paragraph_after == paragraph_end);
+  EXPECT_TRUE(paragraph_start < paragraph_end);
+}
+
+//
 // Test converting to and from visible text with white space.
 // The accessibility tree is based on visible text with white space compressed,
 // vs. the DOM tree where white space is preserved.
diff --git a/third_party/WebKit/Source/modules/accessibility/AXSelection.cpp b/third_party/WebKit/Source/modules/accessibility/AXSelection.cpp
index 1afcf7b..e68fc5a 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXSelection.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXSelection.cpp
@@ -127,7 +127,7 @@
 }
 
 bool operator==(const AXSelection& a, const AXSelection& b) {
-  DCHECK(!a.IsValid() || !b.IsValid());
+  DCHECK(a.IsValid() && b.IsValid());
   return a.Base() == b.Base() && a.Extent() == b.Extent();
 }
 
@@ -137,8 +137,8 @@
 
 std::ostream& operator<<(std::ostream& ostream, const AXSelection& selection) {
   if (!selection.IsValid())
-    return ostream << "Invalid selection";
-  return ostream << "Selection from " << selection.Base() << " to "
+    return ostream << "Invalid AXSelection";
+  return ostream << "AXSelection from " << selection.Base() << " to "
                  << selection.Extent();
 }
 
diff --git a/third_party/WebKit/Source/modules/canvas/canvas2d/BaseRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas/canvas2d/BaseRenderingContext2D.cpp
index 43f314a8..7299ede 100644
--- a/third_party/WebKit/Source/modules/canvas/canvas2d/BaseRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/canvas/canvas2d/BaseRenderingContext2D.cpp
@@ -618,6 +618,9 @@
 
   SkPath sk_path = path.GetSkPath();
   FloatRect bounds = path.BoundingRect();
+  if (isnan(bounds.X()) || isnan(bounds.Y()) || isnan(bounds.Width()) ||
+      isnan(bounds.Height()))
+    return;
   sk_path.setFillType(fill_type);
 
   if (paint_type == CanvasRenderingContext2DState::kStrokePaintType)
diff --git a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlDownloadButtonElement.cpp b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlDownloadButtonElement.cpp
index c0fc6a5..f7d348c5 100644
--- a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlDownloadButtonElement.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlDownloadButtonElement.cpp
@@ -79,7 +79,7 @@
         (event->PlatformTimeStamp() - TimeTicks()).InSecondsF());
     request.SetInputPerfMetricReportPolicy(
         InputToLoadPerfMetricReportPolicy::kReportLink);
-    request.SetSuggestedFilename(String());
+    request.SetSuggestedFilename(MediaElement().title());
     request.SetRequestContext(WebURLRequest::kRequestContextDownload);
     request.SetRequestorOrigin(SecurityOrigin::Create(GetDocument().Url()));
     GetDocument().GetFrame()->Client()->DownloadURL(request);
diff --git a/third_party/WebKit/Source/modules/notifications/OWNERS b/third_party/WebKit/Source/modules/notifications/OWNERS
index 20148c755..f3d5fb2 100644
--- a/third_party/WebKit/Source/modules/notifications/OWNERS
+++ b/third_party/WebKit/Source/modules/notifications/OWNERS
@@ -1,4 +1,4 @@
-mvanouwerkerk@chromium.org
+awdf@chromium.org
 peter@chromium.org
 
 # TEAM: platform-capabilities@chromium.org
diff --git a/third_party/WebKit/Source/modules/permissions/Permissions.cpp b/third_party/WebKit/Source/modules/permissions/Permissions.cpp
index d72a12c..bfde48e 100644
--- a/third_party/WebKit/Source/modules/permissions/Permissions.cpp
+++ b/third_party/WebKit/Source/modules/permissions/Permissions.cpp
@@ -189,7 +189,7 @@
 
   PermissionDescriptorPtr descriptor_copy = descriptor->Clone();
   Document* doc = ToDocumentOrNull(context);
-  Frame* frame = doc ? doc->GetFrame() : nullptr;
+  LocalFrame* frame = doc ? doc->GetFrame() : nullptr;
   GetService(ExecutionContext::From(script_state))
       .RequestPermission(
           std::move(descriptor),
@@ -267,7 +267,7 @@
     internal_permissions_copy.push_back(descriptor->Clone());
 
   Document* doc = ToDocumentOrNull(context);
-  Frame* frame = doc ? doc->GetFrame() : nullptr;
+  LocalFrame* frame = doc ? doc->GetFrame() : nullptr;
   GetService(ExecutionContext::From(script_state))
       .RequestPermissions(
           std::move(internal_permissions),
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.cpp
index f08b4e8..d5b00fb5f 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.cpp
@@ -80,7 +80,10 @@
   DCHECK(Context()->IsAudioThread());
 
   // Render and update the node state when the processor is ready with no error.
-  if (processor_ && !processor_->hasErrorOccured()) {
+  // We also need to check if the global scope is valid before we request
+  // the rendering in the AudioWorkletGlobalScope.
+  if (processor_ && !processor_->hasErrorOccured() &&
+      Context()->CheckWorkletGlobalScopeOnRenderingThread()) {
     Vector<AudioBus*> input_buses;
     Vector<AudioBus*> output_buses;
     for (unsigned i = 0; i < NumberOfInputs(); ++i) {
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
index 3f8cc8a5..0087a97 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
@@ -1036,4 +1036,11 @@
   }
 }
 
+bool BaseAudioContext::CheckWorkletGlobalScopeOnRenderingThread() {
+  DCHECK(!IsMainThread());
+
+  return worklet_backing_worker_thread_ &&
+         worklet_backing_worker_thread_->GlobalScope();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
index 1a68c59..4e17d180 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
@@ -346,6 +346,17 @@
   // does not exist.
   void UpdateWorkletGlobalScopeOnRenderingThread();
 
+  // In the shut-down process, the AudioWorkletGlobalScope can already be gone
+  // while the backing worker thread is still running. This is called by
+  // AudioWorkletHandler before it requests the render task to the processor
+  // which lives on the AudioWorkletGlobalScope. Returns true if there is a
+  // valid WorkletGloblaScope for the worklet-related task.
+  //
+  // TODO(hongchan): This is a short-term fix for https://crbug.com/822725.
+  // The lifetime of the render task should be managed by not the explicit
+  // context check but per-global-scope task queues.
+  bool CheckWorkletGlobalScopeOnRenderingThread();
+
  protected:
   enum ContextType { kRealtimeContext, kOfflineContext };
 
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
index 44f2dbf..bcc8b05 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContextTest.cpp
@@ -653,7 +653,8 @@
 // document previous received a user gesture.
 MAYBE_TEST_P(BaseAudioContextAutoplayTest,
              AutoplayMetrics_DocumentReceivedGesture_Child) {
-  ChildDocument().GetFrame()->UpdateUserActivationInFrameTree();
+  Frame::NotifyUserActivation(ChildDocument().GetFrame(),
+                              UserGestureToken::kNewGesture);
 
   BaseAudioContext* audio_context = BaseAudioContext::Create(
       ChildDocument(), AudioContextOptions(), ASSERT_NO_EXCEPTION);
@@ -689,7 +690,8 @@
 // document previous received a user gesture.
 MAYBE_TEST_P(BaseAudioContextAutoplayTest,
              AutoplayMetrics_DocumentReceivedGesture_Main) {
-  GetDocument().GetFrame()->UpdateUserActivationInFrameTree();
+  Frame::NotifyUserActivation(ChildDocument().GetFrame(),
+                              UserGestureToken::kNewGesture);
 
   BaseAudioContext* audio_context = BaseAudioContext::Create(
       GetDocument(), AudioContextOptions(), ASSERT_NO_EXCEPTION);
diff --git a/third_party/WebKit/Source/modules/webgl/PRESUBMIT.py b/third_party/WebKit/Source/modules/webgl/PRESUBMIT.py
index 2b84253..12569e2 100644
--- a/third_party/WebKit/Source/modules/webgl/PRESUBMIT.py
+++ b/third_party/WebKit/Source/modules/webgl/PRESUBMIT.py
@@ -20,7 +20,7 @@
     return output_api.EnsureCQIncludeTrybotsAreAdded(
         cl,
         ['luci.chromium.try:linux_optional_gpu_tests_rel',
-         'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel',
+         'luci.chromium.try:mac_optional_gpu_tests_rel',
          'master.tryserver.chromium.win:win_optional_gpu_tests_rel',
          'master.tryserver.chromium.android:android_optional_gpu_tests_rel'],
         'Automatically added optional GPU tests to run on CQ.')
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
index b480e95..8a25d09 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
@@ -105,14 +105,13 @@
       contents_layer_(nullptr),
       contents_layer_id_(0),
       scrollable_area_(nullptr),
-      rendering_context3d_(0),
-      weak_ptr_factory_(this) {
+      rendering_context3d_(0) {
 #if DCHECK_IS_ON()
   client.VerifyNotPainting();
 #endif
   layer_ = Platform::Current()->CompositorSupport()->CreateContentLayer(this);
   layer_->Layer()->SetDrawsContent(draws_content_ && contents_visible_);
-  layer_->Layer()->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
+  layer_->Layer()->SetLayerClient(this);
 
   UpdateTrackingRasterInvalidations();
 }
@@ -557,7 +556,7 @@
     contents_layer_id_ = 0;
     return;
   }
-  contents_layer_->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
+  contents_layer_->SetLayerClient(this);
   contents_layer_id_ = contents_layer_->Id();
 }
 
@@ -1293,7 +1292,7 @@
 void GraphicsLayer::AddLinkHighlight(LinkHighlight* link_highlight) {
   DCHECK(link_highlight && !link_highlights_.Contains(link_highlight));
   link_highlights_.push_back(link_highlight);
-  link_highlight->Layer()->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
+  link_highlight->Layer()->SetLayerClient(this);
   UpdateChildList();
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h
index af74800..048d9c2 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.h
@@ -28,7 +28,6 @@
 #define GraphicsLayer_h
 
 #include <memory>
-#include "base/memory/weak_ptr.h"
 #include "cc/layers/layer_client.h"
 #include "platform/PlatformExport.h"
 #include "platform/geometry/FloatPoint.h"
@@ -431,8 +430,6 @@
   std::unique_ptr<LayerState> layer_state_;
 
   std::unique_ptr<CompositedLayerRasterInvalidator> raster_invalidator_;
-
-  base::WeakPtrFactory<GraphicsLayer> weak_ptr_factory_;
 };
 
 // ObjectPaintInvalidatorWithContext::InvalidatePaintRectangleWithContext uses
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
index fd78bd0..3fc2f3052 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
@@ -114,11 +114,15 @@
   void ApplyTransform(const TransformPaintPropertyNode* target_transform) {
     if (target_transform == current_transform_)
       return;
+    cc_list_.push<cc::ConcatOp>(GetSkMatrix(target_transform));
+  }
 
-    cc_list_.push<cc::ConcatOp>(
-        static_cast<SkMatrix>(TransformationMatrix::ToSkMatrix44(
-            GeometryMapper::SourceToDestinationProjection(
-                target_transform, current_transform_))));
+  SkMatrix GetSkMatrix(
+      const TransformPaintPropertyNode* target_transform) const {
+    return AffineTransformToSkMatrix(
+        GeometryMapper::SourceToDestinationProjection(target_transform,
+                                                      current_transform_)
+            .ToAffineTransform());
   }
 
   void AppendRestore(size_t n) {
@@ -128,6 +132,10 @@
     cc_list_.EndPaintOfPairedEnd();
   }
 
+  void UpdateEffectBounds(const FloatRect&, const TransformPaintPropertyNode*);
+  void PopToParentEffect();
+  void PopClips();
+
   const PropertyTreeState& layer_state_;
   gfx::Vector2dF layer_offset_;
 
@@ -152,12 +160,30 @@
   };
   Vector<StateEntry> state_stack_;
 
+  // This structure accumulates bounds of all chunks under an effect. When an
+  // effect starts, we emit a SaveLayerOp with null bounds starts, and push a
+  // new |EffectBoundsInfo| onto |effect_bounds_stack_|. When the effect ends,
+  // we update the bounds of the SaveLayerOp.
+  struct EffectBoundsInfo {
+    // The id of the SaveLayerOp for this effect. It's recorded when we push the
+    // SaveLayerOp for this effect, and used when this effect ends in
+    // UpdateSaveLayerBounds().
+    size_t save_layer_id;
+    // The transform space when the SaveLayerOp was emitted.
+    const TransformPaintPropertyNode* transform;
+    // Records the bounds of the effect which initiated the entry. Note that
+    // the effect is not |this->effect| (which is the previous effect), but the
+    // |current_effect_| when this entry is the top of the stack.
+    FloatRect bounds;
+  };
+  Vector<EffectBoundsInfo> effect_bounds_stack_;
+
   cc::DisplayItemList& cc_list_;
 };
 
 ConversionContext::~ConversionContext() {
-  for (auto& entry : state_stack_)
-    AppendRestore(entry.saved_count);
+  while (state_stack_.size())
+    PopToParentEffect();
 }
 
 void ConversionContext::SwitchToClip(const ClipPaintPropertyNode* target_clip) {
@@ -298,13 +324,7 @@
 #endif
     if (!state_stack_.size())
       break;
-
-    StateEntry& previous_state = state_stack_.back();
-    AppendRestore(previous_state.saved_count);
-    current_transform_ = previous_state.transform;
-    current_clip_ = previous_state.clip;
-    current_effect_ = previous_state.effect;
-    state_stack_.pop_back();
+    PopToParentEffect();
   }
 
   // Step 2: Collect all effects between the target effect and the current
@@ -325,37 +345,29 @@
 
     // Step 3a: Before each effect can be applied, we must enter its output
     // clip first, or exit all clips if it doesn't have one.
-    if (sub_effect->OutputClip()) {
+    if (sub_effect->OutputClip())
       SwitchToClip(sub_effect->OutputClip());
-    } else {
-      while (state_stack_.size() &&
-             state_stack_.back().type == StateEntry::kClip) {
-        StateEntry& previous_state = state_stack_.back();
-        AppendRestore(previous_state.saved_count);
-        current_transform_ = previous_state.transform;
-        current_clip_ = previous_state.clip;
-        DCHECK_EQ(previous_state.effect, current_effect_);
-        state_stack_.pop_back();
-      }
-    }
+    else
+      PopClips();
 
-    // Step 3b: Apply non-spatial effects first, adjust CTM, then apply spatial
-    // effects. Strictly speaking the CTM shall be appled first, it is done
-    // in this particular order only to save one SaveOp.
+    // Step 3b: Apply effects.
     cc_list_.StartPaint();
-    cc::PaintFlags flags;
     int saved_count = 0;
+    size_t save_layer_id = kNotFound;
+    const auto* target_transform = current_transform_;
 
-    auto save_layer_once = [this, &flags, &saved_count]() {
-      if (!saved_count) {
-        saved_count = 1;
-        cc_list_.push<cc::SaveLayerOp>(nullptr, &flags);
-      }
-    };
+    // We always create separate effect nodes for normal effects and filter
+    // effects, so we can handle them separately.
+    bool has_filter = !sub_effect->Filter().IsEmpty();
+    bool has_other_effects = sub_effect->Opacity() != 1.f ||
+                             sub_effect->BlendMode() != SkBlendMode::kSrcOver ||
+                             sub_effect->GetColorFilter() != kColorFilterNone;
+    DCHECK(!has_filter || !has_other_effects);
 
-    if (sub_effect->BlendMode() != SkBlendMode::kSrcOver ||
-        sub_effect->Opacity() != 1.f ||
-        sub_effect->GetColorFilter() != kColorFilterNone) {
+    if (!has_filter) {
+      // No need to adjust transform for non-filter effects because transform
+      // doesn't matter.
+      cc::PaintFlags flags;
       flags.setBlendMode(sub_effect->BlendMode());
       // TODO(ajuma): This should really be rounding instead of flooring the
       // alpha value, but that breaks slimming paint reftests.
@@ -363,38 +375,37 @@
           static_cast<uint8_t>(gfx::ToFlooredInt(255 * sub_effect->Opacity())));
       flags.setColorFilter(GraphicsContext::WebCoreColorFilterToSkiaColorFilter(
           sub_effect->GetColorFilter()));
-      save_layer_once();
-    }
-
-    const TransformPaintPropertyNode* target_transform =
-        sub_effect->LocalTransformSpace();
-    if (current_transform_ != target_transform) {
-      save_layer_once();
-      ApplyTransform(target_transform);
-    }
-
-    if (sub_effect->Filter().IsEmpty()) {
-      save_layer_once();
+      save_layer_id = cc_list_.push<cc::SaveLayerOp>(nullptr, &flags);
+      saved_count++;
     } else {
+      // Handle filter effect. Adjust transform first.
+      target_transform = sub_effect->LocalTransformSpace();
       FloatPoint filter_origin = sub_effect->PaintOffset();
-      if (filter_origin != FloatPoint()) {
-        save_layer_once();
-        cc_list_.push<cc::TranslateOp>(filter_origin.X(), filter_origin.Y());
+      if (current_transform_ != target_transform ||
+          filter_origin != FloatPoint()) {
+        auto matrix = GetSkMatrix(target_transform);
+        matrix.preTranslate(filter_origin.X(), filter_origin.Y());
+        cc_list_.push<cc::SaveOp>();
+        cc_list_.push<cc::ConcatOp>(matrix);
+        saved_count++;
       }
+
       // The size parameter is only used to computed the origin of zoom
       // operation, which we never generate.
       gfx::SizeF empty;
       cc::PaintFlags filter_flags;
       filter_flags.setImageFilter(cc::RenderSurfaceFilters::BuildImageFilter(
           sub_effect->Filter().AsCcFilterOperations(), empty));
-      cc_list_.push<cc::SaveLayerOp>(nullptr, &filter_flags);
+      save_layer_id = cc_list_.push<cc::SaveLayerOp>(nullptr, &filter_flags);
+
       if (filter_origin != FloatPoint())
         cc_list_.push<cc::TranslateOp>(-filter_origin.X(), -filter_origin.Y());
-
       saved_count++;
     }
 
-    DCHECK(saved_count);
+    DCHECK_GT(saved_count, 0);
+    DCHECK_LE(saved_count, 2);
+    DCHECK_NE(save_layer_id, kNotFound);
     cc_list_.EndPaintOfPairedBegin();
 
     // Step 3c: Adjust state and push previous state onto effect stack.
@@ -403,12 +414,84 @@
     state_stack_.emplace_back(StateEntry{StateEntry::PairedType::kEffect,
                                          saved_count, current_transform_,
                                          current_clip_, current_effect_});
+    effect_bounds_stack_.emplace_back(
+        EffectBoundsInfo{save_layer_id, target_transform});
     current_transform_ = target_transform;
     current_clip_ = input_clip;
     current_effect_ = sub_effect;
   }
 }
 
+void ConversionContext::UpdateEffectBounds(
+    const FloatRect& bounds,
+    const TransformPaintPropertyNode* transform) {
+  if (effect_bounds_stack_.IsEmpty() || bounds.IsEmpty())
+    return;
+
+  auto& effect_bounds_info = effect_bounds_stack_.back();
+  FloatRect mapped_bounds = bounds;
+  GeometryMapper::SourceToDestinationRect(
+      transform, effect_bounds_info.transform, mapped_bounds);
+  effect_bounds_info.bounds.Unite(mapped_bounds);
+}
+
+// Pop clip states (if any) and one effect state (if any) on the top of the
+// stack. Update the bounds of the SaveLayerOp of the effect.
+void ConversionContext::PopToParentEffect() {
+  DCHECK(state_stack_.size());
+  PopClips();
+  if (state_stack_.IsEmpty())
+    return;
+
+  const auto& previous_state = state_stack_.back();
+  DCHECK_EQ(previous_state.type, StateEntry::kEffect);
+  DCHECK_EQ(current_effect_->Parent(), previous_state.effect);
+  DCHECK_EQ(current_clip_, previous_state.clip);
+
+  DCHECK(effect_bounds_stack_.size());
+  const auto& bounds_info = effect_bounds_stack_.back();
+  FloatRect bounds = bounds_info.bounds;
+  if (!bounds.IsEmpty()) {
+    if (current_effect_->Filter().IsEmpty()) {
+      cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id, bounds);
+    } else {
+      // The bounds for the SaveLayerOp should be the source bounds before the
+      // filter is applied, in the space of the TranslateOp which was emitted
+      // before the SaveLayerOp.
+      auto save_layer_bounds = bounds;
+      save_layer_bounds.MoveBy(-current_effect_->PaintOffset());
+      cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id,
+                                     save_layer_bounds);
+      // We need to propagate the filtered bounds to the parent.
+      bounds = current_effect_->MapRect(bounds);
+    }
+  }
+
+  effect_bounds_stack_.pop_back();
+  current_effect_ = previous_state.effect;
+
+  // Propagate the bounds to the parent effect.
+  UpdateEffectBounds(bounds, current_transform_);
+
+  AppendRestore(previous_state.saved_count);
+  current_effect_ = previous_state.effect;
+  current_transform_ = previous_state.transform;
+  state_stack_.pop_back();
+}
+
+// Pop clip states on the top of the stack until the top is an effect state
+// or the stack is empty.
+void ConversionContext::PopClips() {
+  while (state_stack_.size() && state_stack_.back().type == StateEntry::kClip) {
+    const auto& previous_state = state_stack_.back();
+    AppendRestore(previous_state.saved_count);
+    current_transform_ = previous_state.transform;
+    current_clip_ = previous_state.clip;
+    DCHECK_EQ(previous_state.effect, current_effect_);
+    state_stack_.pop_back();
+  }
+}
+
 void ConversionContext::Convert(const Vector<const PaintChunk*>& paint_chunks,
                                 const DisplayItemList& display_items) {
   bool translated = false;
@@ -478,6 +561,7 @@
     }
     if (transformed)
       AppendRestore(1);
+    UpdateEffectBounds(chunk.bounds, chunk_state.Transform());
   }
   if (translated)
     AppendRestore(1);
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayerTest.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayerTest.cpp
index 0dde3ff..aad4ca3 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayerTest.cpp
@@ -28,6 +28,12 @@
 }
 }  // namespace cc
 
+std::ostream& operator<<(std::ostream& os, const SkRect& rect) {
+  if (cc::PaintOp::IsUnsetRect(rect))
+    return os << "(unset)";
+  return os << blink::FloatRect(rect).ToString();
+}
+
 namespace blink {
 namespace {
 
@@ -39,9 +45,7 @@
   PaintChunksToCcLayerTest() : ScopedSlimmingPaintV2ForTest(true) {}
 };
 
-// A simple matcher that only looks for a few ops, ignoring all else.
-// Recognized ops:
-//   ClipPath, ClipRect, Concat, DrawRecord, Save, SaveLayer, Restore.
+// Matches PaintOpTypes in a PaintRecord.
 class PaintRecordMatcher
     : public ::testing::MatcherInterface<const cc::PaintOpBuffer&> {
  public:
@@ -55,51 +59,30 @@
   bool MatchAndExplain(
       const cc::PaintOpBuffer& buffer,
       ::testing::MatchResultListener* listener) const override {
-    auto next = expected_ops_.begin();
-    size_t op_idx = 0;
-    for (cc::PaintOpBuffer::Iterator it(&buffer); it; ++it, ++op_idx) {
-      cc::PaintOpType op = (*it)->GetType();
-      switch (op) {
-        case cc::PaintOpType::ClipPath:
-        case cc::PaintOpType::ClipRect:
-        case cc::PaintOpType::ClipRRect:
-        case cc::PaintOpType::Concat:
-        case cc::PaintOpType::DrawRecord:
-        case cc::PaintOpType::Save:
-        case cc::PaintOpType::SaveLayer:
-        case cc::PaintOpType::Restore:
-        case cc::PaintOpType::Translate:
-          if (next == expected_ops_.end()) {
-            if (listener->IsInterested()) {
-              *listener << "unexpected op " << cc::PaintOpTypeToString(op)
-                        << " at index " << op_idx << ", expecting end of list.";
-            }
-            return false;
-          }
-          if (*next == op) {
-            next++;
-            continue;
-          }
-          if (listener->IsInterested()) {
-            *listener << "unexpected op " << cc::PaintOpTypeToString(op)
-                      << " at index " << op_idx << ", expecting "
-                      << cc::PaintOpTypeToString(*next) << "(#"
-                      << (next - expected_ops_.begin()) << ").";
-          }
-          return false;
-        default:
-          continue;
+    size_t index = 0;
+    for (cc::PaintOpBuffer::Iterator it(&buffer); it; ++it, ++index) {
+      auto op = (*it)->GetType();
+      if (index < expected_ops_.size() && expected_ops_[index] == op)
+        continue;
+
+      if (listener->IsInterested()) {
+        *listener << "unexpected op " << op << " at index " << index << ",";
+        if (index < expected_ops_.size())
+          *listener << " expecting " << expected_ops_[index] << ".";
+        else
+          *listener << " expecting end of list.";
       }
+      return false;
     }
-    if (next == expected_ops_.end())
+    if (index == expected_ops_.size())
       return true;
     if (listener->IsInterested()) {
-      *listener << "unexpected end of list, expecting "
-                << cc::PaintOpTypeToString(*next) << "(#"
-                << (next - expected_ops_.begin()) << ").";
+      *listener << "unexpected end of list, expecting " << expected_ops_[index]
+                << " at index " << index << ".";
     }
     return false;
   }
+
   void DescribeTo(::std::ostream* os) const override {
     *os << "[";
     bool first = true;
@@ -108,7 +91,7 @@
         first = false;
       else
         *os << ", ";
-      *os << cc::PaintOpTypeToString(op);
+      *os << op;
     }
     *os << "]";
   }
@@ -117,6 +100,30 @@
   Vector<cc::PaintOpType> expected_ops_;
 };
 
+#define EXPECT_EFFECT_BOUNDS(x, y, width, height, op_buffer, index)           \
+  do {                                                                        \
+    const auto* save_layer =                                                  \
+        (op_buffer).GetOpAtForTesting<cc::SaveLayerOp>(index);                \
+    ASSERT_NE(nullptr, save_layer);                                           \
+    EXPECT_EQ(FloatRect(x, y, width, height), FloatRect(save_layer->bounds)); \
+  } while (false)
+
+#define EXPECT_TRANSFORM_MATRIX(transform, op_buffer, index)                 \
+  do {                                                                       \
+    const auto* concat = (op_buffer).GetOpAtForTesting<cc::ConcatOp>(index); \
+    ASSERT_NE(nullptr, concat);                                              \
+    EXPECT_EQ(transform, TransformationMatrix(concat->matrix));              \
+  } while (false)
+
+#define EXPECT_TRANSLATE(x, y, op_buffer, index)               \
+  do {                                                         \
+    const auto* translate =                                    \
+        (op_buffer).GetOpAtForTesting<cc::TranslateOp>(index); \
+    ASSERT_NE(nullptr, translate);                             \
+    EXPECT_EQ(x, translate->dx);                               \
+    EXPECT_EQ(y, translate->dy);                               \
+  } while (false)
+
 // Convenient shorthands.
 const TransformPaintPropertyNode* t0() {
   return TransformPaintPropertyNode::Root();
@@ -141,23 +148,25 @@
   // Add a paint chunk with a non-empty paint record and given property nodes.
   void AddChunk(const TransformPaintPropertyNode* t,
                 const ClipPaintPropertyNode* c,
-                const EffectPaintPropertyNode* e) {
+                const EffectPaintPropertyNode* e,
+                const FloatRect& bounds = FloatRect(0, 0, 100, 100)) {
     auto record = sk_make_sp<PaintRecord>();
-    record->push<cc::DrawRectOp>(SkRect::MakeXYWH(0, 0, 100, 100),
-                                 cc::PaintFlags());
-    AddChunk(std::move(record), t, c, e);
+    record->push<cc::DrawRectOp>(bounds, cc::PaintFlags());
+    AddChunk(std::move(record), t, c, e, bounds);
   }
 
   // Add a paint chunk with a given paint record and property nodes.
   void AddChunk(sk_sp<PaintRecord> record,
                 const TransformPaintPropertyNode* t,
                 const ClipPaintPropertyNode* c,
-                const EffectPaintPropertyNode* e) {
+                const EffectPaintPropertyNode* e,
+                const FloatRect& bounds = FloatRect(0, 0, 100, 100)) {
     size_t i = items.size();
     items.AllocateAndConstruct<DrawingDisplayItem>(
         DefaultId().client, DefaultId().type, std::move(record));
     chunks.emplace_back(i, i + 1, DefaultId(),
                         PaintChunkProperties(PropertyTreeState(t, c, e)));
+    chunks.back().bounds = bounds;
   }
 
   Vector<const PaintChunk*> GetChunkList() const {
@@ -173,8 +182,8 @@
   scoped_refptr<EffectPaintPropertyNode> e1 =
       CreateOpacityOnlyEffect(e0(), 0.5f);
   TestChunks chunks;
-  chunks.AddChunk(t0(), c0(), e1.get());
-  chunks.AddChunk(t0(), c0(), e1.get());
+  chunks.AddChunk(t0(), c0(), e1.get(), FloatRect(0, 0, 50, 50));
+  chunks.AddChunk(t0(), c0(), e1.get(), FloatRect(20, 20, 70, 70));
 
   sk_sp<PaintRecord> output =
       PaintChunksToCcLayer::Convert(
@@ -187,6 +196,7 @@
                                         cc::PaintOpType::DrawRecord,  // <p0/>
                                         cc::PaintOpType::DrawRecord,  // <p1/>
                                         cc::PaintOpType::Restore}));  // </e1>
+  EXPECT_EFFECT_BOUNDS(0, 0, 90, 90, *output, 0);
 }
 
 TEST_F(PaintChunksToCcLayerTest, EffectGroupingNested) {
@@ -199,7 +209,7 @@
       CreateOpacityOnlyEffect(e1.get(), 0.5f);
   TestChunks chunks;
   chunks.AddChunk(t0(), c0(), e2.get());
-  chunks.AddChunk(t0(), c0(), e3.get());
+  chunks.AddChunk(t0(), c0(), e3.get(), FloatRect(111, 222, 333, 444));
 
   sk_sp<PaintRecord> output =
       PaintChunksToCcLayer::Convert(
@@ -216,6 +226,62 @@
                                         cc::PaintOpType::DrawRecord,  // <p1/>
                                         cc::PaintOpType::Restore,     // </e3>
                                         cc::PaintOpType::Restore}));  // </e1>
+  EXPECT_EFFECT_BOUNDS(0, 0, 444, 666, *output, 0);
+  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 1);
+  EXPECT_EFFECT_BOUNDS(111, 222, 333, 444, *output, 4);
+}
+
+TEST_F(PaintChunksToCcLayerTest, EffectFilterGroupingNestedWithTransforms) {
+  // This test verifies nested effects with transforms are grouped properly.
+  auto t1 = TransformPaintPropertyNode::Create(
+      t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
+  auto t2 = TransformPaintPropertyNode::Create(
+      t1.get(), TransformationMatrix().Translate(-50, -50), FloatPoint3D());
+  auto e1 = EffectPaintPropertyNode::Create(e0(), t2.get(), c0(), ColorFilter(),
+                                            CompositorFilterOperations(), .5f,
+                                            SkBlendMode::kSrcOver);
+  CompositorFilterOperations filter;
+  filter.AppendBlurFilter(5);
+  auto e2 = EffectPaintPropertyNode::Create(
+      e1.get(), t2.get(), c0(), ColorFilter(), filter, 1.f,
+      SkBlendMode::kSrcOver, CompositingReason::kNone, CompositorElementId(),
+      FloatPoint(60, 60));
+  CreateOpacityOnlyEffect(e1.get(), 0.5f);
+  TestChunks chunks;
+  chunks.AddChunk(t2.get(), c0(), e1.get(), FloatRect(0, 0, 50, 50));
+  chunks.AddChunk(t1.get(), c0(), e2.get(), FloatRect(20, 20, 70, 70));
+
+  sk_sp<PaintRecord> output =
+      PaintChunksToCcLayer::Convert(
+          chunks.GetChunkList(), PropertyTreeState(t0(), c0(), e0()),
+          gfx::Vector2dF(), chunks.items,
+          cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+          ->ReleaseAsRecord();
+  EXPECT_THAT(
+      *output,
+      PaintRecordMatcher::Make(
+          {cc::PaintOpType::SaveLayer,                      // <e1>
+           cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t1*t2>
+           cc::PaintOpType::DrawRecord,                     // <p1/>
+           cc::PaintOpType::Restore,                        // </t1*t2>
+           cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t1*t2+e2_offset>
+           cc::PaintOpType::SaveLayer,                      // <e2>
+           cc::PaintOpType::Translate,                      // </e2_offset>
+           cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t2^-1>
+           cc::PaintOpType::DrawRecord,                     // <p2/>
+           cc::PaintOpType::Restore,                        // </t2^-1>
+           cc::PaintOpType::Restore,                        // </e2>
+           cc::PaintOpType::Restore,                        // </t1*t2>
+           cc::PaintOpType::Restore}));                     // </e1>
+  // t1(t2(chunk1.bounds + e2(t2^-1(chunk2.bounds))))
+  EXPECT_EFFECT_BOUNDS(-100, -100, 310, 310, *output, 0);
+  EXPECT_TRANSFORM_MATRIX(t1->Matrix() * t2->Matrix(), *output, 2);
+  EXPECT_TRANSFORM_MATRIX((t1->Matrix() * t2->Matrix()).Translate(60, 60),
+                          *output, 6);
+  // t2^-1(chunk2.bounds) - e2_offset
+  EXPECT_EFFECT_BOUNDS(10, 10, 70, 70, *output, 7);
+  EXPECT_TRANSLATE(-60, -60, *output, 8);
+  EXPECT_TRANSFORM_MATRIX(t2->Matrix().Inverse(), *output, 10);
 }
 
 TEST_F(PaintChunksToCcLayerTest, InterleavedClipEffect) {
@@ -241,8 +307,8 @@
   TestChunks chunks;
   chunks.AddChunk(t0(), c2.get(), e0());
   chunks.AddChunk(t0(), c3.get(), e0());
-  chunks.AddChunk(t0(), c4.get(), e2.get());
-  chunks.AddChunk(t0(), c3.get(), e1.get());
+  chunks.AddChunk(t0(), c4.get(), e2.get(), FloatRect(0, 0, 50, 50));
+  chunks.AddChunk(t0(), c3.get(), e1.get(), FloatRect(20, 20, 70, 70));
   chunks.AddChunk(t0(), c4.get(), e0());
 
   sk_sp<PaintRecord> output =
@@ -273,6 +339,8 @@
            cc::PaintOpType::DrawRecord,                             // <p4/>
            cc::PaintOpType::Restore,                                // </c3+c4>
            cc::PaintOpType::Restore}));                             // </c1+c2>
+  EXPECT_EFFECT_BOUNDS(0, 0, 90, 90, *output, 7);
+  EXPECT_EFFECT_BOUNDS(0, 0, 50, 50, *output, 10);
 }
 
 TEST_F(PaintChunksToCcLayerTest, ClipSpaceInversion) {
@@ -305,14 +373,13 @@
                    cc::PaintOpType::Restore}));                     // </c1 t1>
 }
 
-TEST_F(PaintChunksToCcLayerTest, EffectSpaceInversion) {
+TEST_F(PaintChunksToCcLayerTest, OpacityEffectSpaceInversion) {
   // This test verifies chunks that have a shallower transform state than
   // its effect can still be painted. The infamous CSS corner case:
   // <div style="overflow:scroll">
-  //     <div style="opacity:0.5">
-  //         <div style="position:absolute;">Transparent but not scroll
-  //         along.</div>
-  //     </div>
+  //   <div style="opacity:0.5">
+  //     <div style="position:absolute;">Transparent but not scroll along.</div>
+  //   </div>
   // </div>
   scoped_refptr<TransformPaintPropertyNode> t1 =
       TransformPaintPropertyNode::Create(
@@ -322,6 +389,7 @@
       SkBlendMode::kSrcOver);
   TestChunks chunks;
   chunks.AddChunk(t0(), c0(), e1.get());
+  chunks.AddChunk(t1.get(), c0(), e1.get());
 
   sk_sp<PaintRecord> output =
       PaintChunksToCcLayer::Convert(
@@ -329,14 +397,57 @@
           gfx::Vector2dF(), chunks.items,
           cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
           ->ReleaseAsRecord();
+  EXPECT_THAT(*output,
+              PaintRecordMatcher::Make({cc::PaintOpType::SaveLayer,   // <e1>
+                                        cc::PaintOpType::DrawRecord,  // <p0/>
+                                        cc::PaintOpType::Save,
+                                        cc::PaintOpType::Concat,      // <t1>
+                                        cc::PaintOpType::DrawRecord,  // <p1/>
+                                        cc::PaintOpType::Restore,     // </t1>
+                                        cc::PaintOpType::Restore}));  // </e1>
+  EXPECT_EFFECT_BOUNDS(0, 0, 200, 200, *output, 0);
+  EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 3);
+}
+
+TEST_F(PaintChunksToCcLayerTest, FilterEffectSpaceInversion) {
+  // This test verifies chunks that have a shallower transform state than
+  // its effect can still be painted. The infamous CSS corner case:
+  // <div style="overflow:scroll">
+  //   <div style="filter:blur(1px)">
+  //     <div style="position:absolute;">Filtered but not scroll along.</div>
+  //   </div>
+  // </div>
+  auto t1 = TransformPaintPropertyNode::Create(
+      t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
+  CompositorFilterOperations filter;
+  filter.AppendBlurFilter(5);
+  auto e1 = EffectPaintPropertyNode::Create(
+      e0(), t1.get(), c0(), ColorFilter(), filter, 1.f, SkBlendMode::kSrcOver,
+      CompositingReason::kNone, CompositorElementId(), FloatPoint(66, 88));
+  TestChunks chunks;
+  chunks.AddChunk(t0(), c0(), e1.get());
+
+  auto output = PaintChunksToCcLayer::Convert(
+                    chunks.GetChunkList(), PropertyTreeState(t0(), c0(), e0()),
+                    gfx::Vector2dF(), chunks.items,
+                    cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
+                    ->ReleaseAsRecord();
   EXPECT_THAT(
       *output,
       PaintRecordMatcher::Make(
-          {cc::PaintOpType::SaveLayer, cc::PaintOpType::Concat,  // <t1 e1>
-           cc::PaintOpType::Save, cc::PaintOpType::Concat,       // <t1^-1>
-           cc::PaintOpType::DrawRecord,                          // <p0/>
-           cc::PaintOpType::Restore,                             // </t1^-1>
-           cc::PaintOpType::Restore}));                          // </e1 t1>
+          {cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t1*e1_offset>
+           cc::PaintOpType::SaveLayer,                      // <e1>
+           cc::PaintOpType::Translate,                      // </e1_offset>
+           cc::PaintOpType::Save, cc::PaintOpType::Concat,  // <t1^-1>
+           cc::PaintOpType::DrawRecord,                     // <p0/>
+           cc::PaintOpType::Restore,                        // </t1^-1>
+           cc::PaintOpType::Restore,                        // </e1>
+           cc::PaintOpType::Restore}));                     // </t1>
+  EXPECT_TRANSFORM_MATRIX(TransformationMatrix(t1->Matrix()).Translate(66, 88),
+                          *output, 1);
+  EXPECT_EFFECT_BOUNDS(-66, -88, 50, 50, *output, 2);
+  EXPECT_TRANSLATE(-66, -88, *output, 3);
+  EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 5);
 }
 
 TEST_F(PaintChunksToCcLayerTest, NonRootLayerSimple) {
@@ -413,6 +524,7 @@
                                         cc::PaintOpType::DrawRecord,  // <p0/>
                                         cc::PaintOpType::Restore,     // </c2>
                                         cc::PaintOpType::Restore}));  // </e1>
+  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
 }
 
 TEST_F(PaintChunksToCcLayerTest,
@@ -444,6 +556,8 @@
                                         cc::PaintOpType::Restore,     // </c1>
                                         cc::PaintOpType::Restore,     // </e2>
                                         cc::PaintOpType::Restore}));  // </e1>
+  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
+  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 1);
 }
 
 TEST_F(PaintChunksToCcLayerTest,
@@ -473,6 +587,7 @@
                                         cc::PaintOpType::DrawRecord,  // <p0/>
                                         cc::PaintOpType::Restore,     // </c1>
                                         cc::PaintOpType::Restore}));  // </e2>
+  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
 }
 
 TEST_F(PaintChunksToCcLayerTest,
@@ -499,6 +614,7 @@
               PaintRecordMatcher::Make({cc::PaintOpType::SaveLayer,   // <e2>
                                         cc::PaintOpType::DrawRecord,  // <p0/>
                                         cc::PaintOpType::Restore}));  // </e2>
+  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
 }
 
 TEST_F(PaintChunksToCcLayerTest, VisualRect) {
@@ -643,6 +759,7 @@
                            cc::PaintOpType::SaveLayer,  // <e1>
                            cc::PaintOpType::Restore,    // </e1>
                        }));
+  EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0);
 }
 
 TEST_F(PaintChunksToCcLayerTest, CombineClips) {
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
index bb0351349..fd79812 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
@@ -107,8 +107,16 @@
     [[CONTENT_SHELL_FONTS_DIR], 'Arimo-BoldItalic.ttf', None],
     [[CONTENT_SHELL_FONTS_DIR], 'Arimo-Italic.ttf', None],
     [[CONTENT_SHELL_FONTS_DIR], 'Arimo-Regular.ttf', None],
+    [[CONTENT_SHELL_FONTS_DIR], 'Cousine-Bold.ttf', None],
+    [[CONTENT_SHELL_FONTS_DIR], 'Cousine-BoldItalic.ttf', None],
+    [[CONTENT_SHELL_FONTS_DIR], 'Cousine-Italic.ttf', None],
+    [[CONTENT_SHELL_FONTS_DIR], 'Cousine-Regular.ttf', None],
     [[CONTENT_SHELL_FONTS_DIR], 'DejaVuSans.ttf', None],
     [[CONTENT_SHELL_FONTS_DIR], 'Garuda.ttf', None],
+    [[CONTENT_SHELL_FONTS_DIR], 'Gelasio-Bold.ttf', None],
+    [[CONTENT_SHELL_FONTS_DIR], 'Gelasio-BoldItalic.ttf', None],
+    [[CONTENT_SHELL_FONTS_DIR], 'Gelasio-Italic.ttf', None],
+    [[CONTENT_SHELL_FONTS_DIR], 'Gelasio-Regular.ttf', None],
     [[CONTENT_SHELL_FONTS_DIR], 'Lohit-Devanagari.ttf', None],
     [[CONTENT_SHELL_FONTS_DIR], 'Lohit-Gurmukhi.ttf', None],
     [[CONTENT_SHELL_FONTS_DIR], 'Lohit-Tamil.ttf', None],
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py
index 61f7e74..209550d 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py
@@ -39,24 +39,22 @@
 
 _log = logging.getLogger(__name__)
 
+# Directory for imported tests relative to the layout tests base directory.
+DEST_DIR_NAME = 'external'
 
 class TestCopier(object):
 
-    def __init__(self, host, source_repo_path, dest_dir_name='external'):
+    def __init__(self, host, source_repo_path):
         """Initializes variables to prepare for copying and converting files.
 
         Args:
             host: An instance of Host.
             source_repo_path: Path to the local checkout of web-platform-tests.
-            dest_dir_name: The name of the directory under the layout tests
-                directory where imported tests should be copied to.
-                TODO(qyearsley): This can be made into a constant.
         """
         self.host = host
 
         assert self.host.filesystem.exists(source_repo_path)
         self.source_repo_path = source_repo_path
-        self.dest_dir_name = dest_dir_name
 
         self.filesystem = self.host.filesystem
         self.path_finder = PathFinder(self.filesystem)
@@ -64,7 +62,7 @@
         self.destination_directory = self.filesystem.normpath(
             self.filesystem.join(
                 self.layout_tests_dir,
-                dest_dir_name,
+                DEST_DIR_NAME,
                 self.filesystem.basename(self.source_repo_path)))
         self.import_in_place = (self.source_repo_path == self.destination_directory)
         self.dir_above_repo = self.filesystem.dirname(self.source_repo_path)
@@ -100,7 +98,7 @@
                         dirs.remove(name)
 
                 for path in paths_to_skip:
-                    path_base = path.replace(self.dest_dir_name + '/', '')
+                    path_base = path.replace(DEST_DIR_NAME + '/', '')
                     path_base = path_base.replace(cur_dir, '')
                     path_full = self.filesystem.join(root, path_base)
                     if path_base in dirs:
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier_unittest.py
index 8bc25913..e6aac52 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier_unittest.py
@@ -51,7 +51,7 @@
         host = MockHost()
         host.executive = MockExecutive(exception=ScriptError('error'))
         host.filesystem = MockFileSystem(files=FAKE_FILES)
-        copier = TestCopier(host, FAKE_SOURCE_REPO_DIR, 'destination')
+        copier = TestCopier(host, FAKE_SOURCE_REPO_DIR)
         copier.do_import()  # No exception raised.
 
     def test_does_not_import_owner_files(self):
diff --git a/third_party/WebKit/public/platform/WebDocumentSubresourceFilter.h b/third_party/WebKit/public/platform/WebDocumentSubresourceFilter.h
index 75770ef..8c85dbf9 100644
--- a/third_party/WebKit/public/platform/WebDocumentSubresourceFilter.h
+++ b/third_party/WebKit/public/platform/WebDocumentSubresourceFilter.h
@@ -35,6 +35,8 @@
   // Returns true if disallowed resource loads should be logged to the devtools
   // console.
   virtual bool ShouldLogToConsole() = 0;
+
+  virtual bool GetIsAssociatedWithAdSubframe() const = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/platform/WebLayer.h b/third_party/WebKit/public/platform/WebLayer.h
index 82b63b4..b9dd379 100644
--- a/third_party/WebKit/public/platform/WebLayer.h
+++ b/third_party/WebKit/public/platform/WebLayer.h
@@ -241,7 +241,7 @@
   virtual void SetSnapContainerData(base::Optional<cc::SnapContainerData>) = 0;
 
   // Sets the cc-side layer client.
-  virtual void SetLayerClient(base::WeakPtr<cc::LayerClient>) = 0;
+  virtual void SetLayerClient(cc::LayerClient*) = 0;
 
   // Gets the underlying cc layer.
   virtual const cc::Layer* CcLayer() const = 0;
diff --git a/third_party/WebKit/public/web/WebDocumentLoader.h b/third_party/WebKit/public/web/WebDocumentLoader.h
index 53463bcf..19ec4cc 100644
--- a/third_party/WebKit/public/web/WebDocumentLoader.h
+++ b/third_party/WebKit/public/web/WebDocumentLoader.h
@@ -144,8 +144,6 @@
   // initiated loads that may have had a user activation from the browser UI.
   virtual void SetUserActivated() = 0;
 
-  // Sets if the document is an ad identified subframe.
-  virtual void SetIsAdSubframe(bool is_ad_subframe) = 0;
   virtual bool GetIsAdSubframe() const = 0;
 
  protected:
diff --git a/third_party/WebKit/public/web/WebFrame.h b/third_party/WebKit/public/web/WebFrame.h
index 99e6346..df23b67 100644
--- a/third_party/WebKit/public/web/WebFrame.h
+++ b/third_party/WebKit/public/web/WebFrame.h
@@ -204,9 +204,6 @@
   void DetachFromParent();
 #endif
 
-  // Mark this frame's document as having received a user gesture.
-  virtual void SetHasReceivedUserGesture() = 0;
-
  protected:
   explicit WebFrame(WebTreeScopeType);
   virtual ~WebFrame();
diff --git a/third_party/WebKit/public/web/WebLocalFrame.h b/third_party/WebKit/public/web/WebLocalFrame.h
index 37890c8..e0bc9bb 100644
--- a/third_party/WebKit/public/web/WebLocalFrame.h
+++ b/third_party/WebKit/public/web/WebLocalFrame.h
@@ -157,6 +157,9 @@
   // Sets the name of this frame.
   virtual void SetName(const WebString&) = 0;
 
+  // Notifies this frame about a user activation from the browser side.
+  virtual void NotifyUserActivation() = 0;
+
   // Hierarchy ----------------------------------------------------------
 
   // Returns true if the current frame is a local root.
diff --git a/third_party/WebKit/public/web/WebRemoteFrame.h b/third_party/WebKit/public/web/WebRemoteFrame.h
index 743d787..0302926 100644
--- a/third_party/WebKit/public/web/WebRemoteFrame.h
+++ b/third_party/WebKit/public/web/WebRemoteFrame.h
@@ -110,6 +110,10 @@
   // owner.
   virtual void WillEnterFullscreen() = 0;
 
+  // Mark the document for the corresponding LocalFrame as having received a
+  // user gesture.
+  virtual void SetHasReceivedUserGesture() = 0;
+
   virtual void SetHasReceivedUserGestureBeforeNavigation(bool value) = 0;
 
   // Scrolls the given rectangle into view. This kicks off the recursive scroll
diff --git a/third_party/WebKit/public/web/remote_objects.mojom b/third_party/WebKit/public/web/remote_objects.mojom
index 753c1fa..803acdea 100644
--- a/third_party/WebKit/public/web/remote_objects.mojom
+++ b/third_party/WebKit/public/web/remote_objects.mojom
@@ -41,6 +41,16 @@
   EXCEPTION_THROWN,
 };
 
+union RemoteInvocationResultValue {
+  double number_value;
+  bool boolean_value;
+  mojo_base.mojom.String16 string_value;
+  SingletonJavaScriptValue singleton_value;
+};
+
 struct RemoteInvocationResult {
   RemoteInvocationError error = OK;
+
+  // Must be set if |error == OK|.
+  RemoteInvocationResultValue? value;
 };
diff --git a/third_party/closure_compiler/externs/quick_unlock_private.js b/third_party/closure_compiler/externs/quick_unlock_private.js
index 4d7dd15..4229b89e 100644
--- a/third_party/closure_compiler/externs/quick_unlock_private.js
+++ b/third_party/closure_compiler/externs/quick_unlock_private.js
@@ -72,6 +72,18 @@
 chrome.quickUnlockPrivate.getAuthToken = function(accountPassword, onComplete) {};
 
 /**
+ * Sets the lock screen enabled state. NOTE: The lock enabled state is reflected
+ * in the settings.enable_screen_lock pref, which can be read but not written
+ * using the settings_private API (which also provides policy information). This
+ * API must be used to change the pref.
+ * @param {string} token The token returned by $(ref:getAuthToken).
+ * @param {boolean} enabled
+ * @param {function():void=} onComplete
+ * @see https://developer.chrome.com/extensions/quickUnlockPrivate#method-setLockScreenEnabled
+ */
+chrome.quickUnlockPrivate.setLockScreenEnabled = function(token, enabled, onComplete) {};
+
+/**
  * Returns the set of quick unlock modes that are available for the user to use.
  * Some quick unlock modes may be disabled by policy.
  * @param {function(!Array<!chrome.quickUnlockPrivate.QuickUnlockMode>):void}
@@ -119,10 +131,10 @@
  * @param {!Array<!chrome.quickUnlockPrivate.QuickUnlockMode>} modes The quick
  *     unlock modes that should be active.
  * @param {!Array<string>} credentials The associated credential for each mode.
- *     To keep the credential the same for the associated mode, pass an empty
- *     string.
+ *     To keep the     credential the same for the associated mode, pass an
+ *     empty string.
  * @param {function():void} onComplete Called with true if the quick unlock
- *     state was updated, false otherwise. The update is treated as a single
+ *     state was updated,     false otherwise. The update is treated as a single
  *     atomic operation.
  * @see https://developer.chrome.com/extensions/quickUnlockPrivate#method-setModes
  */
diff --git a/third_party/closure_compiler/interfaces/quick_unlock_private_interface.js b/third_party/closure_compiler/interfaces/quick_unlock_private_interface.js
index fc885a5..e8a060e4 100644
--- a/third_party/closure_compiler/interfaces/quick_unlock_private_interface.js
+++ b/third_party/closure_compiler/interfaces/quick_unlock_private_interface.js
@@ -24,6 +24,18 @@
   getAuthToken: assertNotReached,
 
   /**
+   * Sets the lock screen enabled state. NOTE: The lock enabled state is
+   * reflected in the settings.enable_screen_lock pref, which can be read but
+   * not written using the settings_private API (which also provides policy
+   * information). This API must be used to change the pref.
+   * @param {string} token The token returned by $(ref:getAuthToken).
+   * @param {boolean} enabled
+   * @param {function():void=} onComplete
+   * @see https://developer.chrome.com/extensions/quickUnlockPrivate#method-setLockScreenEnabled
+   */
+  setLockScreenEnabled: assertNotReached,
+
+  /**
    * Returns the set of quick unlock modes that are available for the user to
    * use. Some quick unlock modes may be disabled by policy.
    * @param {function(!Array<!chrome.quickUnlockPrivate.QuickUnlockMode>):void}
@@ -71,11 +83,11 @@
    * @param {!Array<!chrome.quickUnlockPrivate.QuickUnlockMode>} modes The quick
    *     unlock modes that should be active.
    * @param {!Array<string>} credentials The associated credential for each
-   *     mode. To keep the credential the same for the associated mode, pass an
-   *     empty string.
+   *     mode. To keep the     credential the same for the associated mode, pass
+   *     an empty string.
    * @param {function():void} onComplete Called with true if the quick unlock
-   *     state was updated, false otherwise. The update is treated as a single
-   *     atomic operation.
+   *     state was updated,     false otherwise. The update is treated as a
+   *     single atomic operation.
    * @see https://developer.chrome.com/extensions/quickUnlockPrivate#method-setModes
    */
   setModes: assertNotReached,
diff --git a/third_party/feed/BUILD.gn b/third_party/feed/BUILD.gn
index 245da84a..e43cd8e60 100644
--- a/third_party/feed/BUILD.gn
+++ b/third_party/feed/BUILD.gn
@@ -28,7 +28,7 @@
     "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/ModelChild.java",
     "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/ModelCursor.java",
     "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/ModelFeature.java",
-    "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/ModelMutator.java",
+    "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/ModelMutation.java",
     "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/ModelProvider.java",
     "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/ModelProviderFactory.java",
     "src/src/main/java/com/google/android/libraries/feed/api/modelprovider/ModelProviderObserver.java",
@@ -54,6 +54,7 @@
     "src/src/main/java/com/google/android/libraries/feed/basicstream/BasicStream.java",
     "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/StreamActionApiImpl.java",
     "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/StreamRecyclerViewAdapter.java",
+    "src/src/main/java/com/google/android/libraries/feed/common/Committer.java",
     "src/src/main/java/com/google/android/libraries/feed/common/Consumer.java",
     "src/src/main/java/com/google/android/libraries/feed/common/DateUtils.java",
     "src/src/main/java/com/google/android/libraries/feed/common/Dumpable.java",
@@ -65,11 +66,13 @@
     "src/src/main/java/com/google/android/libraries/feed/common/protoextensions/FeedExtensionRegistry.java",
     "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/FeedModelProvider.java",
     "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/FeedModelProviderFactory.java",
+    "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/ChildBinding.java",
+    "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/ChildStructureMutation.java",
     "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/CursorProvider.java",
     "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/FeatureChangeImpl.java",
     "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/ModelChildBinder.java",
     "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/ModelCursorImpl.java",
-    "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/ModelMutatorImpl.java",
+    "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/ModelMutationImpl.java",
     "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/UpdatableModelChild.java",
     "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/UpdatableModelFeature.java",
     "src/src/main/java/com/google/android/libraries/feed/feedmodelprovider/internal/UpdatableModelToken.java",
@@ -94,7 +97,6 @@
     "src/src/main/java/com/google/android/libraries/feed/host/imageloader/ImageLoaderApi.java",
     "src/src/main/java/com/google/android/libraries/feed/host/logging/LoggingApi.java",
     "src/src/main/java/com/google/android/libraries/feed/host/scheduler/SchedulerApi.java",
-    "src/src/main/java/com/google/android/libraries/feed/host/storage/CommitResult.java",
     "src/src/main/java/com/google/android/libraries/feed/host/storage/InMemoryJournalStorage.java",
     "src/src/main/java/com/google/android/libraries/feed/host/storage/InMemoryKeyValuePairStorage.java",
     "src/src/main/java/com/google/android/libraries/feed/host/storage/JournalMutation.java",
diff --git a/third_party/feed/README.chromium b/third_party/feed/README.chromium
index b60a0ed..56a0cf4e 100644
--- a/third_party/feed/README.chromium
+++ b/third_party/feed/README.chromium
@@ -2,7 +2,7 @@
 Short name: feed
 URL: https://chromium.googlesource.com/feed
 Version: 0
-Revision: 0ff595791bd7e6316f8bae206090dd2583f2cf27
+Revision: 089c8e9316ed3a2afd631ba2b773f13e69121dc7
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/polymer/v1_0/bower.json b/third_party/polymer/v1_0/bower.json
index 712548cd..ae1dbc2 100644
--- a/third_party/polymer/v1_0/bower.json
+++ b/third_party/polymer/v1_0/bower.json
@@ -14,7 +14,7 @@
     "iron-fit-behavior": "PolymerElements/iron-fit-behavior#2.1.0",
     "iron-flex-layout": "PolymerElements/iron-flex-layout#1.3.1",
     "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#1.0.6",
-    "iron-icon": "PolymerElements/iron-icon#1.0.10",
+    "iron-icon": "PolymerElements/iron-icon#2.0.1",
     "iron-iconset-svg": "PolymerElements/iron-iconset-svg#2.1.1",
     "iron-icons": "PolymerElements/iron-icons#2.0.1",
     "iron-input": "PolymerElements/iron-input#1.0.10",
diff --git a/third_party/polymer/v1_0/components-chromium/iron-icon/bower.json b/third_party/polymer/v1_0/components-chromium/iron-icon/bower.json
index d19a748..98243f5e 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-icon/bower.json
+++ b/third_party/polymer/v1_0/components-chromium/iron-icon/bower.json
@@ -1,7 +1,7 @@
 {
   "name": "iron-icon",
   "private": true,
-  "version": "1.0.10",
+  "version": "2.0.1",
   "license": "http://polymer.github.io/LICENSE.txt",
   "description": "An element that supports displaying an icon",
   "main": "iron-icon.html",
@@ -19,17 +19,41 @@
   },
   "ignore": [],
   "dependencies": {
-    "iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
-    "iron-meta": "polymerelements/iron-meta#^1.0.0",
-    "polymer": "Polymer/polymer#^1.1.0"
+    "iron-flex-layout": "PolymerElements/iron-flex-layout#1 - 2",
+    "iron-meta": "PolymerElements/iron-meta#1 - 2",
+    "polymer": "Polymer/polymer#1.9 - 2"
   },
   "devDependencies": {
-    "test-fixture": "polymerelements/test-fixture#^1.0.0",
-    "promise-polyfill": "polymerlabs/promise-polyfill#^1.0.0",
-    "iron-iconset": "polymerelements/iron-iconset#^1.0.0",
-    "iron-icons": "polymerelements/iron-icons#^1.0.0",
-    "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
-    "web-component-tester": "^4.0.0",
-    "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+    "promise-polyfill": "polymerlabs/promise-polyfill#1 - 2",
+    "iron-iconset": "PolymerElements/iron-iconset#1 - 2",
+    "iron-icons": "PolymerElements/iron-icons#1 - 2",
+    "iron-component-page": "polymerelements/iron-component-page#1 - 2",
+    "web-component-tester": "^6.0.0",
+    "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0",
+    "iron-demo-helpers": "PolymerElements/iron-demo-helpers#1 - 2"
+  },
+  "variants": {
+    "1.x": {
+      "dependencies": {
+        "iron-flex-layout": "polymerelements/iron-flex-layout#^1.0.0",
+        "iron-meta": "polymerelements/iron-meta#^1.0.0",
+        "polymer": "Polymer/polymer#^1.9"
+      },
+      "devDependencies": {
+        "promise-polyfill": "polymerlabs/promise-polyfill#^1.0.0",
+        "iron-iconset": "polymerelements/iron-iconset#^1.0.0",
+        "iron-icons": "polymerelements/iron-icons#^1.0.0",
+        "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
+        "web-component-tester": "^4.0.0",
+        "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
+        "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.2.5"
+      },
+      "resolutions": {
+        "webcomponentsjs": "^0.7"
+      }
+    }
+  },
+  "resolutions": {
+    "webcomponentsjs": "^1.0.0"
   }
 }
diff --git a/third_party/polymer/v1_0/components-chromium/iron-icon/iron-icon-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-icon/iron-icon-extracted.js
index 1f61c65..874116dc 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-icon/iron-icon-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/iron-icon/iron-icon-extracted.js
@@ -9,8 +9,7 @@
          * `iconset_name:icon_name`.
          */
         icon: {
-          type: String,
-          observer: '_iconChanged'
+          type: String
         },
 
         /**
@@ -18,8 +17,7 @@
          * iconset.
          */
         theme: {
-          type: String,
-          observer: '_updateIcon'
+          type: String
         },
 
         /**
@@ -28,20 +26,25 @@
          * precedence over a given icon attribute.
          */
         src: {
-          type: String,
-          observer: '_srcChanged'
+          type: String
         },
 
         /**
          * @type {!Polymer.IronMeta}
          */
         _meta: {
-          value: Polymer.Base.create('iron-meta', {type: 'iconset'}),
-          observer: '_updateIcon'
+          value: Polymer.Base.create('iron-meta', {type: 'iconset'})
         }
 
       },
 
+      observers: [
+        '_updateIcon(_meta, isAttached)',
+        '_updateIcon(theme, isAttached)',
+        '_srcChanged(src, isAttached)',
+        '_iconChanged(icon, isAttached)'
+      ],
+
       _DEFAULT_ICONSET: 'icons',
 
       _iconChanged: function(icon) {
diff --git a/third_party/polymer/v1_0/components-chromium/iron-icon/iron-icon.html b/third_party/polymer/v1_0/components-chromium/iron-icon/iron-icon.html
index f611bd0..df5dd36 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-icon/iron-icon.html
+++ b/third_party/polymer/v1_0/components-chromium/iron-icon/iron-icon.html
@@ -84,8 +84,8 @@
   <template>
     <style>
       :host {
-        @apply(--layout-inline);
-        @apply(--layout-center-center);
+        @apply --layout-inline;
+        @apply --layout-center-center;
         position: relative;
 
         vertical-align: middle;
@@ -95,7 +95,11 @@
 
         width: var(--iron-icon-width, 24px);
         height: var(--iron-icon-height, 24px);
-        @apply(--iron-icon);
+        @apply --iron-icon;
+      }
+
+      :host([hidden]) {
+        display: none;
       }
     </style>
   </template>
diff --git a/third_party/polymer/v1_0/components_summary.txt b/third_party/polymer/v1_0/components_summary.txt
index 7838082c..5506162f 100644
--- a/third_party/polymer/v1_0/components_summary.txt
+++ b/third_party/polymer/v1_0/components_summary.txt
@@ -72,9 +72,9 @@
 
 Name: iron-icon
 Repository: https://github.com/PolymerElements/iron-icon.git
-Tree: v1.0.10
-Revision: f4e146da4982ff96bb25db85290c09e8de4ec734
-Tree link: https://github.com/PolymerElements/iron-icon/tree/v1.0.10
+Tree: v2.0.1
+Revision: bfdf82f7c8eb4c205cb2d4af6996ffc98f41b1c1
+Tree link: https://github.com/PolymerElements/iron-icon/tree/v2.0.1
 
 Name: iron-icons
 Repository: https://github.com/PolymerElements/iron-icons.git
diff --git a/third_party/test_fonts/BUILD.gn b/third_party/test_fonts/BUILD.gn
index 1008d98..1d92bcb 100644
--- a/third_party/test_fonts/BUILD.gn
+++ b/third_party/test_fonts/BUILD.gn
@@ -9,9 +9,17 @@
     "test_fonts/Arimo-BoldItalic.ttf",
     "test_fonts/Arimo-Italic.ttf",
     "test_fonts/Arimo-Regular.ttf",
+    "test_fonts/Cousine-Bold.ttf",
+    "test_fonts/Cousine-BoldItalic.ttf",
+    "test_fonts/Cousine-Italic.ttf",
+    "test_fonts/Cousine-Regular.ttf",
     "test_fonts/DejaVuSans-Bold.ttf",
     "test_fonts/DejaVuSans.ttf",
     "test_fonts/Garuda.ttf",
+    "test_fonts/Gelasio-Bold.ttf",
+    "test_fonts/Gelasio-BoldItalic.ttf",
+    "test_fonts/Gelasio-Italic.ttf",
+    "test_fonts/Gelasio-Regular.ttf",
     "test_fonts/Lohit-Devanagari.ttf",
     "test_fonts/Lohit-Gurmukhi.ttf",
     "test_fonts/Lohit-Tamil.ttf",
diff --git a/third_party/test_fonts/LICENSE b/third_party/test_fonts/LICENSE
index 23256c1..50738f4 100644
--- a/third_party/test_fonts/LICENSE
+++ b/third_party/test_fonts/LICENSE
@@ -1,4 +1,8 @@
 The SIL OPEN FONT LICENSE applies to the following files:
+Gelasio-Bold.ttf
+Gelasio-BoldItalic.ttf
+Gelasio-Italic.ttf
+Gelasio-Regular.ttf
 Lohit-Devanagari.ttf
 Lohit-Gurmukhi.ttf
 Lohit-Tamil.ttf
@@ -743,6 +747,10 @@
 Arimo-BoldItalic.ttf
 Arimo-Italic.ttf
 Arimo-Regular.ttf
+Cousine-Bold.ttf
+Cousine-BoldItalic.ttf
+Cousine-Italic.ttf
+Cousine-Regular.ttf
 Tinos-Bold.ttf
 Tinos-BoldItalic.ttf
 Tinos-Italic.ttf
diff --git a/third_party/test_fonts/README.chromium b/third_party/test_fonts/README.chromium
index 2da72e9..f93c4eb4 100644
--- a/third_party/test_fonts/README.chromium
+++ b/third_party/test_fonts/README.chromium
@@ -40,8 +40,16 @@
 Arimo-BoldItalic.ttf       https://github.com/google/fonts/tree/master/apache/arimo
 Arimo-Italic.ttf           https://github.com/google/fonts/tree/master/apache/arimo
 Arimo-Regular.ttf          https://github.com/google/fonts/tree/master/apache/arimo
+Cousine-Bold.ttf           https://github.com/google/fonts/tree/master/apache/cousine
+Cousine-BoldItalic.ttf     https://github.com/google/fonts/tree/master/apache/cousine
+Cousine-Italic.ttf         https://github.com/google/fonts/tree/master/apache/cousine
+Cousine-Regular.ttf        https://github.com/google/fonts/tree/master/apache/cousine
 DejaVuSans.ttf             https://dejavu-fonts.github.io/Download.html
 Garuda.ttf                 https://linux.thai.net/projects/fonts-tlwg
+Gelasio-Bold.ttf           https://fontlibrary.org/en/font/gelasio
+Gelasio-BoldItalic.ttf     https://fontlibrary.org/en/font/gelasio
+Gelasio-Italic.ttf         https://fontlibrary.org/en/font/gelasio
+Gelasio-Regular.ttf        https://fontlibrary.org/en/font/gelasio
 Lohit-Devanagari.ttf       https://pagure.io/lohit
 Lohit-Gurmukhi.ttf         https://pagure.io/lohit
 Lohit-Tamil.ttf            https://pagure.io/lohit
diff --git a/third_party/test_fonts/test_fonts.tar.gz.sha1 b/third_party/test_fonts/test_fonts.tar.gz.sha1
index 9a1b2230..1ecedbc 100644
--- a/third_party/test_fonts/test_fonts.tar.gz.sha1
+++ b/third_party/test_fonts/test_fonts.tar.gz.sha1
@@ -1 +1 @@
-e494bc9cb34c0dd6629fb783acb7e8bff422a8dd
\ No newline at end of file
+2c437fdc6f6ff8db8737d29bf32351985a9e102c
\ No newline at end of file
diff --git a/third_party/widevine/cdm/BRANDING b/third_party/widevine/cdm/BRANDING
deleted file mode 100644
index de89794..0000000
--- a/third_party/widevine/cdm/BRANDING
+++ /dev/null
@@ -1,3 +0,0 @@
-PRODUCT_FULLNAME=Widevine Content Decryption Module Adapter
-PRODUCT_SHORTNAME=Widevine CDM Adapter
-PRODUCT_DESCRIPTION=Widevine Content Decryption Module Adapter enables the Widevine CDM to be used by Chrome.
diff --git a/third_party/widevine/cdm/BUILD.gn b/third_party/widevine/cdm/BUILD.gn
index b5e3d54..bf66c97e 100644
--- a/third_party/widevine/cdm/BUILD.gn
+++ b/third_party/widevine/cdm/BUILD.gn
@@ -4,9 +4,7 @@
 
 import("//build/config/chrome_build.gni")
 import("//build/config/features.gni")
-import("//chrome/process_version_rc_template.gni")
 import("//media/cdm/library_cdm/cdm_paths.gni")
-import("//media/cdm/library_cdm/ppapi_cdm_adapter.gni")
 import("//media/media_options.gni")
 import("//third_party/widevine/cdm/widevine.gni")
 
@@ -55,8 +53,9 @@
     widevine_cdm_version_h_file = "widevine_cdm_version.h"
   }
 } else if (enable_widevine) {
+  # TODO(crbug.com/349182): Remove after we replace WIDEVINE_CDM_AVAILABLE with
+  # ENABLE_WIDEVINE build flag.
   widevine_cdm_version_h_file = "stub/widevine_cdm_version.h"
-  widevine_cdm_manifest_file = [ "stub/manifest.json" ]
 } else {
   # No branding, use the default one.
   widevine_cdm_version_h_file = "widevine_cdm_version.h"
@@ -84,52 +83,9 @@
   public_deps = [
     ":version_h",  # Forward permission to use version header.
     "//media:media_features",
-    "//ppapi/features",
   ]
 }
 
-if (widevine_cdm_binary_files != []) {
-  copy("widevinecdm") {
-    sources = widevine_cdm_binary_files
-    outputs = [
-      "$root_out_dir/$widevine_cdm_path/{{source_file_part}}",
-    ]
-
-    # TODO(jrummell)
-    # 'COPY_PHASE_STRIP': 'NO',
-  }
-} else if (enable_widevine && enable_library_cdms) {
-  assert(!is_chrome_branded, "Branded Chrome should have binary files to copy.")
-  assert(!is_android, "Android should not have enable_library_cdms.")
-  shared_library("widevinecdm") {
-    output_dir = "$root_out_dir/$widevine_cdm_path"
-    sources = [
-      "//media/cdm/stub/stub_cdm.cc",
-      "//media/cdm/stub/stub_cdm.h",
-    ]
-
-    defines = [ "CDM_IMPLEMENTATION" ]
-
-    deps = [
-      ":version_h",
-      "//base",
-      "//build/config:exe_and_shlib_deps",
-    ]
-
-    if (is_posix && !is_mac) {
-      cflags = [ "-fvisibility=hidden" ]
-    }
-
-    if (is_mac) {
-      ldflags = [ "-Wl,-install_name,@loader_path/libwidevinecdm.dylib" ]
-    }
-  }
-} else {
-  group("widevinecdm") {
-    # NOP
-  }
-}
-
 if (widevine_cdm_manifest_file != []) {
   copy("widevine_cdm_manifest") {
     sources = widevine_cdm_manifest_file
@@ -143,50 +99,29 @@
   }
 }
 
-if ((is_chrome_branded || enable_widevine) && enable_library_cdms) {
-  # Produce and compile the .rc file.
-  process_version_rc_template("widevinecdmadapter_resources") {
-    visibility = [ ":*" ]
-    sources = [
-      "BRANDING",
-      "widevinecdmadapter.ver",
-    ]
-    output = "$target_gen_dir/widevinecdmadapter_version.rc"
-  }
-
-  ppapi_cdm_adapter("widevinecdmadapter") {
-    defines = []
-    output_dir = "$root_out_dir/$widevine_cdm_path"
-    deps = [
-      ":version_h",
-      ":widevinecdm",
-      ":widevinecdmadapter_resources",
-    ]
-    data_deps = [
-      ":widevine_cdm_manifest",
-      ":widevinecdm",
+if (widevine_cdm_binary_files != []) {
+  copy("widevine_cdm_binary") {
+    sources = widevine_cdm_binary_files
+    outputs = [
+      "$root_out_dir/$widevine_cdm_path/{{source_file_part}}",
     ]
 
-    if (is_linux) {
-      ldflags =
-          [ rebase_path("$root_out_dir/$widevine_cdm_path/libwidevinecdm.so",
-                        root_build_dir) ]
-    } else if (is_win) {
-      ldflags =
-          [ rebase_path("$root_out_dir/$widevine_cdm_path/widevinecdm.dll.lib",
-                        root_build_dir) ]
-    } else if (is_mac) {
-      ldflags =
-          [ rebase_path("$root_out_dir/$widevine_cdm_path/libwidevinecdm.dylib",
-                        root_build_dir) ]
-    }
+    # TODO(jrummell)
+    # 'COPY_PHASE_STRIP': 'NO',
   }
 } else {
-  # Placeholder when we're not compiling the adapter.
-  group("widevinecdmadapter") {
+  group("widevine_cdm_binary") {
+    # NOP
   }
 }
 
+group("cdm") {
+  public_deps = [
+    ":widevine_cdm_binary",
+    ":widevine_cdm_manifest",
+  ]
+}
+
 # This target exists for tests to depend on that pulls in a runtime dependency
 # on the license server.
 source_set("widevine_test_license_server") {
diff --git a/third_party/widevine/cdm/stub/manifest.json b/third_party/widevine/cdm/stub/manifest.json
deleted file mode 100644
index d466ccd..0000000
--- a/third_party/widevine/cdm/stub/manifest.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "manifest_version": 2,
-  "name": "WidevineCdm",
-  "description": "Widevine Content Decryption Module Stub",
-  "offline_enabled": false,
-  "version": "0.0.0.000",
-  "minimum_chrome_version": "54.0.0.0",
-  "x-cdm-module-versions": "4",
-  "x-cdm-interface-versions": "8",
-  "x-cdm-host-versions": "8",
-  "x-cdm-codecs": "vp8,vp9.0,avc1",
-  "platforms": [
-    {
-      "os": "win",
-      "arch": "x86",
-      "sub_package_path": "_platform_specific/win_x86/"
-    },
-    {
-      "os": "win",
-      "arch": "x64",
-      "sub_package_path": "_platform_specific/win_x64/"
-    },
-    {
-      "os": "mac",
-      "arch": "x64",
-      "sub_package_path": "_platform_specific/mac_x64/"
-    }
-  ]
-}
diff --git a/third_party/widevine/cdm/widevine.gni b/third_party/widevine/cdm/widevine.gni
index 78695ef..82a9362 100644
--- a/third_party/widevine/cdm/widevine.gni
+++ b/third_party/widevine/cdm/widevine.gni
@@ -2,11 +2,20 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chrome_build.gni")
+import("//media/media_options.gni")
+
 declare_args() {
-  # Allow widevinecdmadapter to be built in Chromium.
+  # Allow Widevine key system support in Chromium.
   enable_widevine = false
 }
 
+enable_widevine_cdm_host_verification =
+    (is_chrome_branded || enable_widevine) && enable_cdm_host_verification
+
+# Only bundle Widevine CDM in Google Chrome builds.
+should_bundle_widevine_cdm = is_chrome_branded && enable_library_cdms
+
 template("widevine_sign_file") {
   # For official builds, generate a signature file for |file| which will
   # be used by Widevine. If |signature_file| is not specified, the signature
diff --git a/third_party/widevine/cdm/widevinecdmadapter.ver b/third_party/widevine/cdm/widevinecdmadapter.ver
deleted file mode 100644
index c0897720..0000000
--- a/third_party/widevine/cdm/widevinecdmadapter.ver
+++ /dev/null
@@ -1,2 +0,0 @@
-INTERNAL_NAME=widevinecdmadapter_dll
-ORIGINAL_FILENAME=widevinecdmadapter.dll
diff --git a/tools/bisect_repackage/bisect_repackage.py b/tools/bisect_repackage/bisect_repackage.py
index 77e91e6..fd169ca 100644
--- a/tools/bisect_repackage/bisect_repackage.py
+++ b/tools/bisect_repackage/bisect_repackage.py
@@ -47,7 +47,6 @@
         'icudtl.dat',
         'libclearkeycdm.so',
         'libwidevinecdm.so',
-        'libwidevinecdmadapter.so',
         'locales/',
         'nacl_helper',
         'nacl_helper_bootstrap',
diff --git a/tools/fuchsia/local-sdk.py b/tools/fuchsia/local-sdk.py
index d9c842f4..cb3bb24 100755
--- a/tools/fuchsia/local-sdk.py
+++ b/tools/fuchsia/local-sdk.py
@@ -34,13 +34,13 @@
 def BuildForArch(arch):
   Run('scripts/build-zircon.sh', '-t', arch)
   Run('build/gn/gen.py', '--target_cpu=' + arch,
-      '--packages=garnet/packages/sdk', '--release')
+      '--packages=garnet/packages/sdk/base', '--release')
   Run('buildtools/ninja', '-C', 'out/release-' + arch)
   # Also build the deprecated bootfs-based image.
   # TODO(crbug.com/805057): Remove this once the bootfs path is turned down.
   build_dir_bootfs = 'out/release-' + arch + '-bootfs'
   Run('build/gn/gen.py', '--target_cpu=' + arch,
-      '--packages=garnet/packages/sdk_bootfs', '--release',
+      '--packages=garnet/packages/sdk/bootfs', '--release',
       '--args=bootfs_packages=true',
       '--build-dir='+build_dir_bootfs)
   Run('buildtools/ninja', '-C', build_dir_bootfs)
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index a1f7cc4..064a568 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -1381,6 +1381,7 @@
 </action>
 
 <action name="Android.ChromeHome.OpenedByExpandButton">
+  <obsolete>Unused as of 03/2018</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1389,6 +1390,7 @@
 </action>
 
 <action name="Android.ChromeHome.OpenedByNTP">
+  <obsolete>Unused as of 03/2018</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1397,6 +1399,7 @@
 </action>
 
 <action name="Android.ChromeHome.OpenedByOmnibox">
+  <obsolete>Unused as of 03/2018</obsolete>
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <description>
@@ -1405,6 +1408,7 @@
 </action>
 
 <action name="Android.ChromeHome.OpenedByStartup">
+  <obsolete>Unused as of 03/2018</obsolete>
   <owner>thildebr@chromium.org</owner>
   <description>
     The Chrome Home bottom sheet was automatically opened on startup after a
@@ -11616,6 +11620,13 @@
   </description>
 </action>
 
+<action name="MobileDownloadRetryDownload">
+  <owner>eugenebut@chromium.org</owner>
+  <description>
+    User attempted to retry downloading a file after the download failed.
+  </description>
+</action>
+
 <action name="MobileExternalNavigationDispatched">
   <owner>mariakhomenko@chromium.org</owner>
   <description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 1493c3e3..a1ca182b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -1199,6 +1199,9 @@
 </enum>
 
 <enum name="AppListEnableSource">
+  <obsolete>
+    Deprecated 03/2018 with Mash AppList refactoring.
+  </obsolete>
   <int value="0" label="Not enabled (should never be recorded)"/>
   <int value="1" label="Packaged app installed from Web Store"/>
   <int value="2" label="Clicked app launcher link from the Web Store"/>
@@ -14812,6 +14815,7 @@
   <int value="1226" label="WEBRTCLOGGINGPRIVATE_STARTEVENTLOGGING"/>
   <int value="1227" label="VIRTUALKEYBOARDPRIVATE_SETCONTAINERBEHAVIOR"/>
   <int value="1228" label="QUICKUNLOCKPRIVATE_GETAUTHTOKEN"/>
+  <int value="1229" label="QUICKUNLOCKPRIVATE_SETLOCKSCREENENABLED"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -23392,6 +23396,8 @@
   <int value="61" label="SETUP_SINGLETON_RELEASED"/>
   <int value="62" label="DELETE_OLD_VERSIONS_SUCCESS"/>
   <int value="63" label="DELETE_OLD_VERSIONS_TOO_MANY_ATTEMPTS"/>
+  <int value="64" label="SAVE_DMTOKEN_FAILED"/>
+  <int value="65" label="SAVE_DMTOKEN_SUCCESS"/>
 </enum>
 
 <enum name="InstanceIDResult">
diff --git a/tools/metrics/histograms/extract_histograms.py b/tools/metrics/histograms/extract_histograms.py
index 7c080e0a..c18e4ab 100644
--- a/tools/metrics/histograms/extract_histograms.py
+++ b/tools/metrics/histograms/extract_histograms.py
@@ -54,9 +54,10 @@
 """
 
 import bisect
-import datetime
 import copy
+import datetime
 import logging
+import re
 import xml.dom.minidom
 
 OWNER_FIELD_PLACEHOLDER = (
@@ -67,8 +68,8 @@
 DEFAULT_BASE_HISTOGRAM_OBSOLETE_REASON = (
     'Base histogram. Use suffixes of this histogram instead.')
 
-EXPIRY_DATE_PATTERN = "%Y/%m/%d"
-
+EXPIRY_DATE_PATTERN = "%Y-%m-%d"
+EXPIRY_MILESTONE_RE = re.compile(r'M[0-9]{2,3}\Z')
 
 class Error(Exception):
   pass
@@ -253,13 +254,13 @@
 
 
 def _ValidateDateString(date_str):
-  """Check if |date_str| matches 'YYYY/MM/DD'.
+  """Check if |date_str| matches 'YYYY-MM-DD'.
 
   Args:
     date_str: string
 
   Returns:
-    True iff |date_str| matches 'YYYY/MM/DD' format.
+    True iff |date_str| matches 'YYYY-MM-DD' format.
   """
   try:
     _ = datetime.datetime.strptime(date_str, EXPIRY_DATE_PATTERN).date()
@@ -267,6 +268,9 @@
     return False
   return True
 
+def _ValidateMilestoneString(milestone_str):
+  """Check if |milestone_str| matches 'M*'."""
+  return EXPIRY_MILESTONE_RE.match(milestone_str) is not None
 
 def _ProcessBaseHistogramAttribute(node, histogram_entry):
   if node.hasAttribute('base'):
@@ -296,15 +300,17 @@
       continue
     histograms[name] = histogram_entry = {}
 
-    # Handle expiry dates.
-    if histogram.hasAttribute('expiry_date'):
-      expiry_date_str = histogram.getAttribute('expiry_date')
-      if _ValidateDateString(expiry_date_str):
-        histogram_entry['expiry_date'] = expiry_date_str
+    # Handle expiry attribute.
+    if histogram.hasAttribute('expires_after'):
+      expiry_str = histogram.getAttribute('expires_after')
+      if _ValidateMilestoneString(expiry_str) or _ValidateDateString(
+          expiry_str):
+        histogram_entry['expires_after'] = expiry_str
       else:
         logging.error(
-            'Expiry date of histogram %s does not match expected format: "%s",'
-            ' found %s.', name, EXPIRY_DATE_PATTERN, expiry_date_str)
+            'Expiry of histogram %s does not match expected date format: "%s"'
+            ' or milestone format: M* found %s.', name, EXPIRY_DATE_PATTERN,
+            expiry_str)
         have_errors = True
 
     # Find <owner> tag.
diff --git a/tools/metrics/histograms/generate_expired_histograms_array.py b/tools/metrics/histograms/generate_expired_histograms_array.py
index 9ce33a0..bbf283f 100755
--- a/tools/metrics/histograms/generate_expired_histograms_array.py
+++ b/tools/metrics/histograms/generate_expired_histograms_array.py
@@ -14,7 +14,9 @@
 import extract_histograms
 import merge_xml
 
-_DATE_FILE_PATTERN = r".*MAJOR_BRANCH_DATE=(.+).*"
+_DATE_FILE_RE = re.compile(r".*MAJOR_BRANCH_DATE=(.+).*")
+_CURRENT_MILESTONE_RE = re.compile(r"MAJOR=([0-9]{2,3})\n")
+_MILESTONE_EXPIRY_RE = re.compile(r"\AM([0-9]{2,3})")
 
 _SCRIPT_NAME = "generate_expired_histograms_array.py"
 _HASH_DATATYPE = "uint64_t"
@@ -39,13 +41,15 @@
 #endif  // {include_guard}
 """
 
+_DATE_FORMAT_ERROR = "Unable to parse expiry {date} in histogram {name}."
+
 
 class Error(Exception):
   pass
 
 
-def _GetExpiredHistograms(histograms, base_date):
-  """Filters histograms to find expired ones.
+def _GetExpiredHistograms(histograms, base_date, current_milestone):
+  """Filters histograms to find expired ones if date format is used.
 
   Args:
     histograms(Dict[str, Dict]): Histogram descriptions in the form
@@ -60,39 +64,53 @@
   """
   expired_histograms_names = []
   for name, content in histograms.items():
-    if "obsolete" in content or "expiry_date" not in content:
+    if "obsolete" in content or "expires_after" not in content:
       continue
-    expiry_date_str = content["expiry_date"]
-    try:
-      expiry_date = datetime.datetime.strptime(
-          expiry_date_str, extract_histograms.EXPIRY_DATE_PATTERN).date()
-    except ValueError:
-      raise Error("Unable to parse expiry date {date} in histogram {name}.".
-                  format(date=expiry_date_str, name=name))
-    if expiry_date < base_date:
-      expired_histograms_names.append(name)
+    expiry_str = content["expires_after"]
+
+    match = _MILESTONE_EXPIRY_RE.search(expiry_str)
+    if match:
+      # if there is match then expiry is in Chrome milsetone format.
+      if int(match.group(1)) < current_milestone:
+        expired_histograms_names.append(name)
+    else:
+      # if no match then we try the date format.
+      try:
+        expiry_date = datetime.datetime.strptime(
+            expiry_str, extract_histograms.EXPIRY_DATE_PATTERN).date()
+      except ValueError:
+        raise Error(_DATE_FORMAT_ERROR.
+                    format(date=expiry_str, name=name))
+      if expiry_date < base_date:
+        expired_histograms_names.append(name)
   return expired_histograms_names
 
 
-def _GetBaseDate(content, pattern):
+def _FindMatch(content, regex, group_num):
+  match_result = regex.search(content)
+  if not match_result:
+    raise Error("Unable to match {pattern} with provided content: {content}".
+                format(pattern=regex.pattern, content=content))
+  return match_result.group(group_num)
+
+
+def _GetBaseDate(content, regex):
   """Fetches base date from |content| to compare expiry dates with.
 
   Args:
    content: A string with the base date.
-   pattern(str): A regular expression that matches the base date.
+   regex: A regular expression object that matches the base date.
 
   Returns:
    A base date as datetime.date object.
 
   Raises:
-    Error if |content| doesn't match |pattern| or the matched date has invalid
+    Error if |content| doesn't match |regex| or the matched date has invalid
     format.
   """
-  match_result = re.search(pattern, content)
-  if not match_result:
-    raise Error("Unable to match {pattern} with provided content: {content}".
-                format(pattern=pattern, content=content))
-  base_date_str = match_result.group(1)
+  base_date_str = _FindMatch(content, regex, 1)
+  if not base_date_str:
+    return None
   try:
     base_date = datetime.datetime.strptime(
         base_date_str, extract_histograms.EXPIRY_DATE_PATTERN).date()
@@ -102,6 +120,22 @@
                 format(date=base_date_str, content=content))
 
 
+def _GetCurrentMilestone(content, regex):
+  """Extracts current milestone from |content|.
+
+  Args:
+   content: A string with the version information.
+   regex: A regular expression object that matches milestone.
+
+  Returns:
+   A milestone  as int.
+
+  Raises:
+    Error if |content| doesn't match |regex|.
+  """
+  return int(_FindMatch(content, regex, 1))
+
+
 def _HashName(name):
   """Returns hash for the given histogram |name|."""
   return "0x" + hashlib.md5(name).hexdigest()[:16]
@@ -122,7 +156,6 @@
   Args:
     header_filename: A filename of the generated header file.
     namespace: A namespace to contain generated array.
-    hash_datatype: Datatype of histogram names' hash.
     histograms_map(Dict[str, str]): A dictionary {hash: histogram_name}.
 
   Returns:
@@ -153,8 +186,9 @@
       arguments.inputs: A list of xml files with histogram descriptions.
       arguments.header_filename: A filename of the generated header file.
       arguments.namespace: A namespace to contain generated array.
-      arguments.hash_datatype: Datatype of histogram names' hash.
       arguments.output_dir: A directory to put the generated file.
+      arguments.major_branch_date_filepath: File path for base date.
+      arguments.milestone_filepath: File path for milestone information.
 
   Raises:
     Error if there is an error in input xml files.
@@ -166,8 +200,13 @@
     raise Error("Error parsing inputs.")
   with open(arguments.major_branch_date_filepath, "r") as date_file:
     file_content = date_file.read()
-  base_date = _GetBaseDate(file_content, _DATE_FILE_PATTERN)
-  expired_histograms_names = _GetExpiredHistograms(histograms, base_date)
+  base_date = _GetBaseDate(file_content, _DATE_FILE_RE)
+  with open(arguments.milestone_filepath, "r") as milestone_file:
+    file_content = milestone_file.read()
+  current_milestone = _GetCurrentMilestone(file_content, _CURRENT_MILESTONE_RE)
+
+  expired_histograms_names = _GetExpiredHistograms(
+      histograms, base_date, current_milestone)
   expired_histograms_map = _GetHashToNameMap(expired_histograms_names)
   header_file_content = _GenerateHeaderFileContent(
       arguments.header_filename, arguments.namespace,
@@ -200,9 +239,14 @@
   arg_parser.add_argument(
       "--major_branch_date_filepath",
       "-d",
-      default="",
+      required=True,
       help="A path to the file with the base date.")
   arg_parser.add_argument(
+      "--milestone_filepath",
+      "-m",
+      required=True,
+      help="A path to the file with the milestone information.")
+  arg_parser.add_argument(
       "inputs",
       nargs="+",
       help="Paths to .xml files with histogram descriptions.")
diff --git a/tools/metrics/histograms/generate_expired_histograms_array_unittest.py b/tools/metrics/histograms/generate_expired_histograms_array_unittest.py
index cee1c13..7747d88 100644
--- a/tools/metrics/histograms/generate_expired_histograms_array_unittest.py
+++ b/tools/metrics/histograms/generate_expired_histograms_array_unittest.py
@@ -47,73 +47,86 @@
   def testGetExpiredHistograms(self):
     histograms = {
         "FirstHistogram": {
-            "expiry_date": "2000/10/01"
+            "expires_after": "2000-10-01"
         },
         "SecondHistogram": {
-            "expiry_date": "2002/10/01"
+            "expires_after": "2002-10-01"
         },
         "ThirdHistogram": {
-            "expiry_date": "2001/10/01"
+            "expires_after": "2001-10-01"
         },
         "FourthHistogram": {},
         "FifthHistogram": {
             "obsolete": "Has expired.",
-            "expiry_date": "2000/10/01"
-        }
+            "expires_after": "2000-10-01"
+        },
+        "SixthHistogram": {
+            "expires_after": "M22"
+        },
+        "SeventhHistogram": {
+            "expires_after": "M60"
+        },
+        "EigthHistogram": {
+            "expires_after": "M65"
+        },
     }
 
     base_date = datetime.date(2001, 10, 1)
+    current_milestone = 60
 
     expired_histograms_names = (
         generate_expired_histograms_array._GetExpiredHistograms(
-            histograms, base_date))
+            histograms, base_date, current_milestone))
 
-    self.assertEqual(expired_histograms_names, ["FirstHistogram"])
+    self.assertEqual(2, len(expired_histograms_names))
+    self.assertIn("FirstHistogram", expired_histograms_names)
+    self.assertIn("SixthHistogram", expired_histograms_names)
 
   def testBadExpiryDate(self):
     histograms = {
         "FirstHistogram": {
-            "expiry_date": "2000/10/01"
+            "expires_after": "2000-10-01"
         },
         "SecondHistogram": {
-            "expiry_date": "2000-10-01"
-        },
+            "expires_after": "2000/10/01"
+        }
     }
     base_date = datetime.date(2000, 10, 01)
+    current_milestone = 60
 
     with self.assertRaises(generate_expired_histograms_array.Error) as error:
         generate_expired_histograms_array._GetExpiredHistograms(histograms,
-            base_date)
+            base_date, current_milestone)
 
     self.assertEqual(
-        "Unable to parse expiry date 2000-10-01 in histogram SecondHistogram.",
-        str(error.exception))
+        generate_expired_histograms_array._DATE_FORMAT_ERROR.format(
+            date="2000/10/01", name="SecondHistogram"), str(error.exception))
 
   def testGetBaseDate(self):
-    pattern = generate_expired_histograms_array._DATE_FILE_PATTERN
+    regex = generate_expired_histograms_array._DATE_FILE_RE
 
     # Does not match the pattern.
-    content = "MAJOR_BRANCH__FAKE_DATE=2017/09/09"
+    content = "MAJOR_BRANCH__FAKE_DATE=2017-09-09"
     with self.assertRaises(generate_expired_histograms_array.Error):
-        generate_expired_histograms_array._GetBaseDate(content, pattern)
+        generate_expired_histograms_array._GetBaseDate(content, regex)
 
     # Has invalid format.
-    content = "MAJOR_BRANCH_DATE=2010-01-01"
+    content = "MAJOR_BRANCH_DATE=2010/01/01"
     with self.assertRaises(generate_expired_histograms_array.Error):
-        generate_expired_histograms_array._GetBaseDate(content, pattern)
+        generate_expired_histograms_array._GetBaseDate(content, regex)
 
     # Has invalid format.
-    content = "MAJOR_BRANCH_DATE=2010/20/02"
+    content = "MAJOR_BRANCH_DATE=2010-20-02"
     with self.assertRaises(generate_expired_histograms_array.Error):
-        generate_expired_histograms_array._GetBaseDate(content, pattern)
+        generate_expired_histograms_array._GetBaseDate(content, regex)
 
     # Has invalid date.
-    content = "MAJOR_BRANCH_DATE=2017/02/29"
+    content = "MAJOR_BRANCH_DATE=2017-02-29"
     with self.assertRaises(generate_expired_histograms_array.Error):
-        generate_expired_histograms_array._GetBaseDate(content, pattern)
+        generate_expired_histograms_array._GetBaseDate(content, regex)
 
-    content = "!!FOO!\nMAJOR_BRANCH_DATE=2010/01/01\n!FOO!!"
-    base_date = generate_expired_histograms_array._GetBaseDate(content, pattern)
+    content = "!!FOO!\nMAJOR_BRANCH_DATE=2010-01-01\n!FOO!!"
+    base_date = generate_expired_histograms_array._GetBaseDate(content, regex)
     self.assertEqual(base_date, datetime.date(2010, 01, 01))
 
   def testGenerateHeaderFileContent(self):
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e564e12..4b3ce9e 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -2214,6 +2214,9 @@
 </histogram>
 
 <histogram name="Apps.AppListHowEnabled" enum="AppListEnableSource">
+  <obsolete>
+    Deprecated 03/2018 with Mash AppList refactoring.
+  </obsolete>
   <owner>tapted@chromium.org</owner>
   <summary>
     The trigger that caused the app list to be enabled. Recorded when the user
@@ -12213,6 +12216,16 @@
   </summary>
 </histogram>
 
+<histogram name="ContextMenu.UnexpectedFindElementResultHandlerMessage"
+    enum="BooleanHit">
+  <owner>michaeldo@chromium.org</owner>
+  <summary>
+    Logged when an unexpected FindElementResultHandler message was received by
+    the application. An unexpected message may be sent by a malicious frame.
+    Only logged on iOS.
+  </summary>
+</histogram>
+
 <histogram name="Cookie.AgeForNonSecureCrossSiteRequest" units="days">
   <owner>mkwst@chromium.org</owner>
   <summary>
@@ -27237,6 +27250,9 @@
 </histogram>
 
 <histogram name="GCM.ConnectedViaProxy" enum="Boolean">
+  <obsolete>
+    Deprecated as of 03/2018 (M67).
+  </obsolete>
   <owner>zea@chromium.org</owner>
   <summary>Whether the GCM connection was made via a proxy or not.</summary>
 </histogram>
@@ -87324,6 +87340,9 @@
 </histogram>
 
 <histogram name="Startup.AppListFirstPaintColdStart" units="ms">
+  <obsolete>
+    Deprecated 03/2018 with Mash AppList refactoring.
+  </obsolete>
   <owner>tapted@chromium.org</owner>
   <summary>
     Time for a newly created browser process to perform the first paint of the
@@ -87333,6 +87352,9 @@
 </histogram>
 
 <histogram name="Startup.AppListFirstPaintWarmStart" units="ms">
+  <obsolete>
+    Deprecated 03/2018 with Mash AppList refactoring.
+  </obsolete>
   <owner>tapted@chromium.org</owner>
   <summary>
     Time for a running browser process to perform the first paint of the app
@@ -104500,6 +104522,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="AppListFirstPaintWarmStartFast" separator="">
+  <obsolete>
+    Deprecated 03/2018 with Mash AppList refactoring.
+  </obsolete>
   <suffix name="" label="Normal start."/>
   <suffix name="Fast"
       label="Fast start by skipping normal chrome.dll startup."/>
@@ -106817,6 +106842,13 @@
 
 <histogram_suffixes name="DrawQuadsType" separator=".">
   <suffix name="" label=""/>
+  <suffix name="Removed"
+      label="Captures only draw quads that have removed by draw occlusion.">
+    <obsolete>
+        Deprecated as of 03/2018. We'd learned enough from this metric and it
+      made no sense to keep it around.
+    </obsolete>
+  </suffix>
   <suffix name="Resized"
       label="Captures only draw quads that have changed size by draw
              occlusion.">
diff --git a/tools/metrics/histograms/print_style.py b/tools/metrics/histograms/print_style.py
index ecf9a6f..218d1d6 100644
--- a/tools/metrics/histograms/print_style.py
+++ b/tools/metrics/histograms/print_style.py
@@ -19,7 +19,7 @@
     'details': [],
     'enum': ['name'],
     'enums': [],
-    'histogram': ['base', 'name', 'enum', 'units', 'expiry_date'],
+    'histogram': ['base', 'name', 'enum', 'units', 'expires_after'],
     'histogram-configuration': ['logsource'],
     'histogram_suffixes': ['name', 'separator', 'ordering'],
     'histogram_suffixes_list': [],
diff --git a/tools/roll_angle.py b/tools/roll_angle.py
index 8928d08..4edc342bf 100755
--- a/tools/roll_angle.py
+++ b/tools/roll_angle.py
@@ -18,11 +18,11 @@
     "buildernames": ["win_optional_gpu_tests_rel"]
   },
   {
-    "mastername": "master.tryserver.chromium.mac",
+    "mastername": "luci.chromium.try",
     "buildernames": ["mac_optional_gpu_tests_rel"]
   },
   {
-    "mastername": "master.tryserver.chromium.linux",
+    "mastername": "luci.chromium.try",
     "buildernames": ["linux_optional_gpu_tests_rel"]
   },
   {
diff --git a/tools/roll_swiftshader.py b/tools/roll_swiftshader.py
index 5b4dd0e..3a938a8 100755
--- a/tools/roll_swiftshader.py
+++ b/tools/roll_swiftshader.py
@@ -18,13 +18,16 @@
     "buildernames": ["win_optional_gpu_tests_rel"]
   },
   {
-    "mastername": "master.tryserver.chromium.mac",
+    "mastername": "luci.chromium.try",
     "buildernames": ["mac_optional_gpu_tests_rel"]
   },
   {
+    "mastername": "luci.chromium.try",
+    "buildernames": ["linux_optional_gpu_tests_rel"]
+  },
+  {
     "mastername": "master.tryserver.chromium.linux",
-    "buildernames": ["linux_optional_gpu_tests_rel",
-                     "linux_chromium_cfi_rel_ng"]
+    "buildernames": ["linux_chromium_cfi_rel_ng"]
   },
   {
     "mastername": "master.tryserver.chromium.android",
diff --git a/tools/roll_webgl_conformance.py b/tools/roll_webgl_conformance.py
index f0fad65..bdc9988 100755
--- a/tools/roll_webgl_conformance.py
+++ b/tools/roll_webgl_conformance.py
@@ -18,11 +18,11 @@
     "buildernames": ["win_optional_gpu_tests_rel"]
   },
   {
-    "mastername": "master.tryserver.chromium.mac",
+    "mastername": "luci.chromium.try",
     "buildernames": ["mac_optional_gpu_tests_rel"]
   },
   {
-    "mastername": "master.tryserver.chromium.linux",
+    "mastername": "luci.chromium.try",
     "buildernames": ["linux_optional_gpu_tests_rel"]
   },
   {
@@ -32,7 +32,7 @@
   # Include the ANGLE tryservers which run the WebGL conformance tests
   # in some non-default configurations.
   {
-    "mastername": "master.tryserver.chromium.angle",
+    "mastername": "luci.chromium.try",
     "buildernames": ["linux_angle_rel_ng"]
   },
   {
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index a4ca6ad..9a5f703b 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -1044,6 +1044,8 @@
 if (is_mac) {
   mac_framework_bundle("ui_unittests_framework") {
     testonly = true
+    framework_version = "U"
+    framework_contents = [ "Resources" ]
     deps = [
       "//ui/resources:ui_test_pak_bundle_data",
     ]
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 5cfe98e6..1233687 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -113,8 +113,7 @@
       device_scale_factor_(1.0f),
       cache_render_surface_requests_(0),
       deferred_paint_requests_(0),
-      trilinear_filtering_request_(0),
-      weak_ptr_factory_(this) {
+      trilinear_filtering_request_(0) {
   CreateCcLayer();
 }
 
@@ -141,8 +140,7 @@
       device_scale_factor_(1.0f),
       cache_render_surface_requests_(0),
       deferred_paint_requests_(0),
-      trilinear_filtering_request_(0),
-      weak_ptr_factory_(this) {
+      trilinear_filtering_request_(0) {
   CreateCcLayer();
 }
 
@@ -630,7 +628,7 @@
     DCHECK(child->cc_layer_);
     cc_layer_->AddChild(child->cc_layer_);
   }
-  cc_layer_->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
+  cc_layer_->SetLayerClient(this);
   cc_layer_->SetTransformOrigin(gfx::Point3F());
   cc_layer_->SetContentsOpaque(fills_bounds_opaquely_);
   cc_layer_->SetIsDrawable(type_ != LAYER_NOT_DRAWN);
@@ -1265,7 +1263,7 @@
   cc_layer_->SetTransformOrigin(gfx::Point3F());
   cc_layer_->SetContentsOpaque(true);
   cc_layer_->SetIsDrawable(type_ != LAYER_NOT_DRAWN);
-  cc_layer_->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
+  cc_layer_->SetLayerClient(this);
   cc_layer_->SetElementId(cc::ElementId(cc_layer_->id()));
   RecomputePosition();
 }
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index 5a0eb3f..5127e11 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -14,7 +14,6 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/observer_list.h"
 #include "cc/base/region.h"
@@ -624,8 +623,6 @@
   // layer.
   unsigned trilinear_filtering_request_;
 
-  base::WeakPtrFactory<Layer> weak_ptr_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(Layer);
 };
 
diff --git a/ui/events/ozone/device/device_manager_manual.cc b/ui/events/ozone/device/device_manager_manual.cc
index 8010870a..0504b5e 100644
--- a/ui/events/ozone/device/device_manager_manual.cc
+++ b/ui/events/ozone/device/device_manager_manual.cc
@@ -17,11 +17,11 @@
 
 namespace {
 
-const char kDevInput[] = "/dev/input";
+const base::FilePath::CharType kDevInput[] = FILE_PATH_LITERAL("/dev/input");
 
 void ScanDevicesOnWorkerThread(std::vector<base::FilePath>* result) {
-  base::FileEnumerator file_enum(base::FilePath(FILE_PATH_LITERAL(kDevInput)),
-                                 false, base::FileEnumerator::FILES,
+  base::FileEnumerator file_enum(base::FilePath(kDevInput), false,
+                                 base::FileEnumerator::FILES,
                                  FILE_PATH_LITERAL("event*[0-9]"));
   for (base::FilePath path = file_enum.Next(); !path.empty();
        path = file_enum.Next()) {
@@ -58,7 +58,7 @@
 }
 
 void DeviceManagerManual::StartWatching() {
-  if (!watcher_.Watch(base::FilePath(FILE_PATH_LITERAL(kDevInput)), false,
+  if (!watcher_.Watch(base::FilePath(kDevInput), false,
                       base::Bind(&DeviceManagerManual::OnWatcherEvent,
                                  weak_ptr_factory_.GetWeakPtr()))) {
     LOG(ERROR) << "Failed to start FilePathWatcher";
diff --git a/ui/gfx/OWNERS b/ui/gfx/OWNERS
index ad40b66f..e73caf84 100644
--- a/ui/gfx/OWNERS
+++ b/ui/gfx/OWNERS
@@ -20,6 +20,9 @@
 per-file interpolated_transform*=danakj@chromium.org
 per-file interpolated_transform*=vollick@chromium.org
 
+# Skia geometry helpers.
+per-file skia_util*=danakj@chromium.org
+
 # GPU memory buffer and GpuFence interfaces.
 per-file gpu_fence*=reveman@chromium.org
 per-file gpu_memory_buffer*=reveman@chromium.org
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc
index 77458c9..6ef4a870 100644
--- a/ui/gfx/color_space.cc
+++ b/ui/gfx/color_space.cc
@@ -835,18 +835,16 @@
     }
 
     // BT2020_CL is a special case.
-    // Basically we return a matrix that transforms RGB values
-    // to RYB values. (We replace the green component with the
-    // the luminance.) Later steps will compute the Cb & Cr values.
+    // Basically we return a matrix that transforms RYB values
+    // to YUV values. (Note that the green component have been replaced
+    // with the luminance.)
     case ColorSpace::MatrixID::BT2020_CL: {
       Kr = 0.2627f;
       Kb = 0.0593f;
-      float data[16] = {
-          1.0f,           0.0f, 0.0f, 0.0f,  // R
-            Kr, 1.0f - Kr - Kb,   Kb, 0.0f,  // Y
-          0.0f,           0.0f, 1.0f, 0.0f,  // B
-          0.0f,           0.0f, 0.0f, 1.0f
-      };
+      float data[16] = {1.0f, 0.0f,           0.0f, 0.0f,  // R
+                        Kr,   1.0f - Kr - Kb, Kb,   0.0f,  // Y
+                        0.0f, 0.0f,           1.0f, 0.0f,  // B
+                        0.0f, 0.0f,           0.0f, 1.0f};
       matrix->setRowMajorf(data);
       return;
     }
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc
index 0bbf3852..6aad90f 100644
--- a/ui/gfx/color_transform.cc
+++ b/ui/gfx/color_transform.cc
@@ -275,8 +275,9 @@
   gfx::ColorSpace GetDstColorSpace() const override { return dst_; };
 
   void Transform(TriStim* colors, size_t num) const override {
-    for (const auto& step : steps_)
+    for (const auto& step : steps_) {
       step->Transform(colors, num);
+    }
   }
   bool CanGetShaderSource() const override;
   std::string GetShaderSource() const override;
@@ -443,8 +444,6 @@
   // ColorTransformPerChannelTransferFn implementation:
   float Evaluate(float v) const override {
     // Note that the sign-extension is performed by the caller.
-    if (v < 0.f)
-      return 0.f;
     return SkTransferFnEvalUnclamped(fn_, v);
   }
   void AppendTransferShaderSource(std::stringstream* result) const override {
@@ -728,7 +727,7 @@
       } else {
         V = R_Y / (2.0 * 0.4969);
       }
-      RYB[i] = ColorTransform::TriStim(RYB[i].y(), U, V);
+      RYB[i] = ColorTransform::TriStim(RYB[i].y(), U + 0.5, V + 0.5);
     }
   }
 
@@ -756,11 +755,11 @@
       return;
     for (size_t i = 0; i < num; i++) {
       float Y = YUV[i].x();
-      float U = YUV[i].y();
-      float V = YUV[i].z();
+      float U = YUV[i].y() - 0.5;
+      float V = YUV[i].z() - 0.5;
       float B_Y, R_Y;
       if (U <= 0) {
-        B_Y = Y * (-2.0 * -0.9702);
+        B_Y = U * (-2.0 * -0.9702);
       } else {
         B_Y = U * (2.0 * 0.7910);
       }
@@ -770,7 +769,7 @@
         R_Y = V * (2.0 * 0.4969);
       }
       // Return an RYB value, later steps will fix it.
-      YUV[i] = ColorTransform::TriStim(R_Y + Y, YUV[i].x(), B_Y + Y);
+      YUV[i] = ColorTransform::TriStim(R_Y + Y, Y, B_Y + Y);
     }
   }
   bool CanAppendShaderSource() override { return true; }
@@ -780,12 +779,12 @@
     *hdr << "vec3 BT2020_YUV_to_RYB_Step" << step_index << "(vec3 color) {"
          << endl;
     *hdr << "  float Y = color.x;" << endl;
-    *hdr << "  float U = color.y;" << endl;
-    *hdr << "  float V = color.z;" << endl;
+    *hdr << "  float U = color.y - 0.5;" << endl;
+    *hdr << "  float V = color.z - 0.5;" << endl;
     *hdr << "  float B_Y = 0.0;" << endl;
     *hdr << "  float R_Y = 0.0;" << endl;
     *hdr << "  if (U <= 0.0) {" << endl;
-    *hdr << "    B_Y = Y * (-2.0 * -0.9702);" << endl;
+    *hdr << "    B_Y = U * (-2.0 * -0.9702);" << endl;
     *hdr << "  } else {" << endl;
     *hdr << "    B_Y = U * (2.0 * 0.7910);" << endl;
     *hdr << "  }" << endl;
@@ -838,8 +837,13 @@
   steps_.push_back(
       std::make_unique<ColorTransformMatrix>(GetRangeAdjustMatrix(src)));
 
-  steps_.push_back(
-      std::make_unique<ColorTransformMatrix>(Invert(GetTransferMatrix(src))));
+  if (src.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
+    // BT2020 CL is a special case.
+    steps_.push_back(std::make_unique<ColorTransformFromBT2020CL>());
+  } else {
+    steps_.push_back(
+        std::make_unique<ColorTransformMatrix>(Invert(GetTransferMatrix(src))));
+  }
 
   // If the target color space is not defined, just apply the adjust and
   // tranfer matrices. This path is used by YUV to RGB color conversion
@@ -860,7 +864,8 @@
 
   if (src.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
     // BT2020 CL is a special case.
-    steps_.push_back(std::make_unique<ColorTransformFromBT2020CL>());
+    steps_.push_back(
+        std::make_unique<ColorTransformMatrix>(Invert(GetTransferMatrix(src))));
   }
   steps_.push_back(
       std::make_unique<ColorTransformMatrix>(GetPrimaryTransform(src)));
@@ -869,7 +874,8 @@
       std::make_unique<ColorTransformMatrix>(Invert(GetPrimaryTransform(dst))));
   if (dst.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
     // BT2020 CL is a special case.
-    steps_.push_back(std::make_unique<ColorTransformToBT2020CL>());
+    steps_.push_back(
+        std::make_unique<ColorTransformMatrix>(GetTransferMatrix(dst)));
   }
 
   SkColorSpaceTransferFn dst_from_linear_fn;
@@ -880,8 +886,12 @@
     steps_.push_back(std::make_unique<ColorTransformFromLinear>(dst.transfer_));
   }
 
-  steps_.push_back(
-      std::make_unique<ColorTransformMatrix>(GetTransferMatrix(dst)));
+  if (dst.matrix_ == ColorSpace::MatrixID::BT2020_CL) {
+    steps_.push_back(std::make_unique<ColorTransformToBT2020CL>());
+  } else {
+    steps_.push_back(
+        std::make_unique<ColorTransformMatrix>(GetTransferMatrix(dst)));
+  }
 
   steps_.push_back(std::make_unique<ColorTransformMatrix>(
       Invert(GetRangeAdjustMatrix(dst))));
diff --git a/ui/gfx/color_transform_unittest.cc b/ui/gfx/color_transform_unittest.cc
index 3a668f9..c965b47d 100644
--- a/ui/gfx/color_transform_unittest.cc
+++ b/ui/gfx/color_transform_unittest.cc
@@ -98,6 +98,36 @@
   EXPECT_GT(tmp.z(), tmp.y());
 }
 
+TEST(SimpleColorSpace, BT2020CLtoBT2020RGB) {
+  ColorSpace bt2020cl(
+      ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::BT2020_10,
+      ColorSpace::MatrixID::BT2020_CL, ColorSpace::RangeID::LIMITED);
+  ColorSpace bt2020rgb(ColorSpace::PrimaryID::BT2020,
+                       ColorSpace::TransferID::BT2020_10,
+                       ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
+  ColorSpace sRGB = ColorSpace::CreateSRGB();
+  std::unique_ptr<ColorTransform> t(ColorTransform::NewColorTransform(
+      bt2020cl, bt2020rgb, ColorTransform::Intent::INTENT_ABSOLUTE));
+
+  ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f);
+  t->Transform(&tmp, 1);
+  EXPECT_NEAR(tmp.x(), 0.0f, 0.001f);
+  EXPECT_NEAR(tmp.y(), 0.0f, 0.001f);
+  EXPECT_NEAR(tmp.z(), 0.0f, 0.001f);
+
+  tmp = ColorTransform::TriStim(235.0f / 255.0f, 0.5f, 0.5f);
+  t->Transform(&tmp, 1);
+  EXPECT_NEAR(tmp.x(), 1.0f, 0.001f);
+  EXPECT_NEAR(tmp.y(), 1.0f, 0.001f);
+  EXPECT_NEAR(tmp.z(), 1.0f, 0.001f);
+
+  // Test a blue color
+  tmp = ColorTransform::TriStim(128.0f / 255.0f, 240.0f / 255.0f, 0.5f);
+  t->Transform(&tmp, 1);
+  EXPECT_GT(tmp.z(), tmp.x());
+  EXPECT_GT(tmp.z(), tmp.y());
+}
+
 TEST(SimpleColorSpace, TransferFnCancel) {
   ColorSpace::PrimaryID primary = ColorSpace::PrimaryID::BT709;
   ColorSpace::MatrixID matrix = ColorSpace::MatrixID::RGB;
diff --git a/ui/gfx/skia_util.cc b/ui/gfx/skia_util.cc
index db2856f..4379e76c 100644
--- a/ui/gfx/skia_util.cc
+++ b/ui/gfx/skia_util.cc
@@ -68,6 +68,10 @@
                       SkFloatToScalar(size.height()));
 }
 
+SkISize SizeToSkISize(const Size& size) {
+  return SkISize::Make(size.width(), size.height());
+}
+
 SizeF SkSizeToSizeF(const SkSize& size) {
   return SizeF(SkScalarToFloat(size.width()), SkScalarToFloat(size.height()));
 }
diff --git a/ui/gfx/skia_util.h b/ui/gfx/skia_util.h
index 0c1f251..d0efa6b 100644
--- a/ui/gfx/skia_util.h
+++ b/ui/gfx/skia_util.h
@@ -35,6 +35,7 @@
 GFX_EXPORT SkRect RectFToSkRect(const RectF& rect);
 GFX_EXPORT RectF SkRectToRectF(const SkRect& rect);
 GFX_EXPORT SkSize SizeFToSkSize(const SizeF& size);
+GFX_EXPORT SkISize SizeToSkISize(const Size& size);
 GFX_EXPORT SizeF SkSizeToSizeF(const SkSize& size);
 GFX_EXPORT Size SkISizeToSize(const SkISize& size);
 
diff --git a/ui/gl/PRESUBMIT.py b/ui/gl/PRESUBMIT.py
index dd5141e6..01515c9 100644
--- a/ui/gl/PRESUBMIT.py
+++ b/ui/gl/PRESUBMIT.py
@@ -24,7 +24,7 @@
     cl,
     [
       'luci.chromium.try:linux_optional_gpu_tests_rel',
-      'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel',
+      'luci.chromium.try:mac_optional_gpu_tests_rel',
       'master.tryserver.chromium.win:win_optional_gpu_tests_rel',
       'master.tryserver.chromium.android:android_optional_gpu_tests_rel',
     ],
diff --git a/ui/gl/init/gl_initializer_mac.cc b/ui/gl/init/gl_initializer_mac.cc
index 91b2e0b..d45d7a1 100644
--- a/ui/gl/init/gl_initializer_mac.cc
+++ b/ui/gl/init/gl_initializer_mac.cc
@@ -143,11 +143,7 @@
     "Libraries/libswiftshader_libEGL.dylib";
 
 bool InitializeStaticEGLInternal(GLImplementation implementation) {
-  base::FilePath module_path;
-  if (!PathService::Get(base::DIR_MODULE, &module_path)) {
-    return false;
-  }
-
+  base::FilePath module_path = base::mac::FrameworkBundlePath();
   base::FilePath glesv2_path;
   base::FilePath egl_path;
   if (implementation == kGLImplementationSwiftShaderGL) {
diff --git a/ui/ozone/common/egl_util.cc b/ui/ozone/common/egl_util.cc
index 9a40d1d..86636c3f 100644
--- a/ui/ozone/common/egl_util.cc
+++ b/ui/ozone/common/egl_util.cc
@@ -14,12 +14,16 @@
 namespace ui {
 namespace {
 
-const char kDefaultEglSoname[] = "libEGL.so.1";
-const char kDefaultGlesSoname[] = "libGLESv2.so.2";
+const base::FilePath::CharType kDefaultEglSoname[] =
+    FILE_PATH_LITERAL("libEGL.so.1");
+const base::FilePath::CharType kDefaultGlesSoname[] =
+    FILE_PATH_LITERAL("libGLESv2.so.2");
 
 #if BUILDFLAG(ENABLE_SWIFTSHADER)
-const char kGLESv2SwiftShaderLibraryName[] = "libGLESv2.so";
-const char kEGLSwiftShaderLibraryName[] = "libEGL.so";
+const base::FilePath::CharType kGLESv2SwiftShaderLibraryName[] =
+    FILE_PATH_LITERAL("libGLESv2.so");
+const base::FilePath::CharType kEGLSwiftShaderLibraryName[] =
+    FILE_PATH_LITERAL("libEGL.so");
 #endif
 
 bool LoadEGLGLES2Bindings(const base::FilePath& egl_library_path,
@@ -69,7 +73,7 @@
     base::FilePath module_path;
     if (!PathService::Get(base::DIR_MODULE, &module_path))
       return false;
-    module_path = module_path.Append("swiftshader/");
+    module_path = module_path.Append(FILE_PATH_LITERAL("swiftshader/"));
 
     glesv2_path = module_path.Append(kGLESv2SwiftShaderLibraryName);
     egl_path = module_path.Append(kEGLSwiftShaderLibraryName);
diff --git a/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc b/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc
index 607bf22..dc7b44a 100644
--- a/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc
+++ b/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc
@@ -39,7 +39,7 @@
   // ClientNativePixmapFactoryCast implementation:
   bool IsConfigurationSupported(gfx::BufferFormat format,
                                 gfx::BufferUsage usage) const override {
-    return format == gfx::BufferFormat::RGBA_8888 &&
+    return format == gfx::BufferFormat::BGRA_8888 &&
            usage == gfx::BufferUsage::SCANOUT;
   }
 
diff --git a/ui/ozone/platform/drm/host/drm_overlay_manager.cc b/ui/ozone/platform/drm/host/drm_overlay_manager.cc
index dc0f75a1..d99d288 100644
--- a/ui/ozone/platform/drm/host/drm_overlay_manager.cc
+++ b/ui/ozone/platform/drm/host/drm_overlay_manager.cc
@@ -61,11 +61,6 @@
       continue;
     }
 
-    // Compositor doesn't have information about the total size of primary
-    // candidate. We get this information from display rect.
-    if (candidate.plane_z_order == 0)
-      candidate.buffer_size = gfx::ToNearestRect(candidate.display_rect).size();
-
     result_candidates.push_back(OverlaySurfaceCandidate(candidate));
     // Start out hoping that we can have an overlay.
     result_candidates.back().overlay_handled = true;
diff --git a/ui/ozone/platform/headless/headless_surface_factory.cc b/ui/ozone/platform/headless/headless_surface_factory.cc
index 6e64f51..bec0898 100644
--- a/ui/ozone/platform/headless/headless_surface_factory.cc
+++ b/ui/ozone/platform/headless/headless_surface_factory.cc
@@ -28,6 +28,8 @@
 
 namespace {
 
+const base::FilePath::CharType kDevNull[] = FILE_PATH_LITERAL("/dev/null");
+
 void WriteDataToFile(const base::FilePath& location, const SkBitmap& bitmap) {
   DCHECK(!location.empty());
   std::vector<unsigned char> png_data;
@@ -137,7 +139,7 @@
 
 base::FilePath HeadlessSurfaceFactory::GetPathForWidget(
     gfx::AcceleratedWidget widget) {
-  if (base_path_.empty() || base_path_ == base::FilePath("/dev/null"))
+  if (base_path_.empty() || base_path_ == base::FilePath(kDevNull))
     return base_path_;
 
   // Disambiguate multiple window output files with the window id.
@@ -177,7 +179,7 @@
     return;
 
   if (!DirectoryExists(base_path_) && !base::CreateDirectory(base_path_) &&
-      base_path_ != base::FilePath("/dev/null"))
+      base_path_ != base::FilePath(kDevNull))
     PLOG(FATAL) << "Unable to create output directory";
 
   if (!base::PathIsWritable(base_path_))
diff --git a/ui/ozone/platform/headless/headless_window.cc b/ui/ozone/platform/headless/headless_window.cc
index 3608ffa..2c038ad 100644
--- a/ui/ozone/platform/headless/headless_window.cc
+++ b/ui/ozone/platform/headless/headless_window.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "build/build_config.h"
 #include "ui/events/platform/platform_event_source.h"
 #include "ui/ozone/platform/headless/headless_window_manager.h"
 #include "ui/platform_window/platform_window_delegate.h"
@@ -16,12 +17,20 @@
                                HeadlessWindowManager* manager,
                                const gfx::Rect& bounds)
     : delegate_(delegate), manager_(manager), bounds_(bounds) {
+#if defined(OS_WIN)
+  widget_ = reinterpret_cast<gfx::AcceleratedWidget>(manager_->AddWindow(this));
+#else
   widget_ = manager_->AddWindow(this);
+#endif
   delegate_->OnAcceleratedWidgetAvailable(widget_, 1.f);
 }
 
 HeadlessWindow::~HeadlessWindow() {
+#if defined(OS_WIN)
+  manager_->RemoveWindow(reinterpret_cast<uint64_t>(widget_), this);
+#else
   manager_->RemoveWindow(widget_, this);
+#endif
 }
 
 gfx::Rect HeadlessWindow::GetBounds() {
diff --git a/ui/ozone/platform/wayland/fake_server.h b/ui/ozone/platform/wayland/fake_server.h
index d1bcf94..a9286261 100644
--- a/ui/ozone/platform/wayland/fake_server.h
+++ b/ui/ozone/platform/wayland/fake_server.h
@@ -25,7 +25,7 @@
 // Base class for managing the life cycle of server objects.
 class ServerObject {
  public:
-  ServerObject(wl_resource* resource);
+  explicit ServerObject(wl_resource* resource);
   virtual ~ServerObject();
 
   wl_resource* resource() { return resource_; }
@@ -71,7 +71,7 @@
 // Manage zxdg_toplevel for providing desktop UI.
 class MockXdgTopLevel : public MockXdgSurface {
  public:
-  MockXdgTopLevel(wl_resource* resource);
+  explicit MockXdgTopLevel(wl_resource* resource);
   ~MockXdgTopLevel() override;
 
   // TODO(msisov): mock other zxdg_toplevel specific methods once implementation
@@ -85,7 +85,7 @@
 // Manage client surface
 class MockSurface : public ServerObject {
  public:
-  MockSurface(wl_resource* resource);
+  explicit MockSurface(wl_resource* resource);
   ~MockSurface() override;
 
   static MockSurface* FromResource(wl_resource* resource);
@@ -103,7 +103,7 @@
 
 class MockPointer : public ServerObject {
  public:
-  MockPointer(wl_resource* resource);
+  explicit MockPointer(wl_resource* resource);
   ~MockPointer() override;
 
  private:
@@ -112,7 +112,7 @@
 
 class MockKeyboard : public ServerObject {
  public:
-  MockKeyboard(wl_resource* resource);
+  explicit MockKeyboard(wl_resource* resource);
   ~MockKeyboard() override;
 
  private:
diff --git a/ui/shell_dialogs/BUILD.gn b/ui/shell_dialogs/BUILD.gn
index e5018b27..80b4c176 100644
--- a/ui/shell_dialogs/BUILD.gn
+++ b/ui/shell_dialogs/BUILD.gn
@@ -89,6 +89,8 @@
 
   mac_framework_bundle("shell_dialogs_unittests_bundle") {
     testonly = true
+    framework_version = "S"
+    framework_contents = [ "Resources" ]
     info_plist = "//ui/base/test/framework-Info.plist"
     deps = [
       ":shell_dialogs_unittests_xibs",
diff --git a/ui/views/bubble/bubble_border.cc b/ui/views/bubble/bubble_border.cc
index e2292a8f..8a539bed 100644
--- a/ui/views/bubble/bubble_border.cc
+++ b/ui/views/bubble/bubble_border.cc
@@ -62,13 +62,12 @@
 
 namespace {
 
+// The border corner radius for material design bubble borders.
+constexpr int kMaterialDesignCornerRadius = 2;
+
 // The border is stroked at 1px, but for the purposes of reserving space we have
 // to deal in dip coordinates, so round up to 1dip.
-const int kBorderThicknessDip = 1;
-
-bool UseMaterialDesign() {
-  return ui::MaterialDesignController::IsSecondaryUiMaterial();
-}
+constexpr int kBorderThicknessDip = 1;
 
 // Utility functions for getting alignment points on the edge of a rectangle.
 gfx::Point CenterTop(const gfx::Rect& rect) {
@@ -177,13 +176,7 @@
       background_color_(color),
       use_theme_background_color_(false) {
   DCHECK(shadow_ < SHADOW_COUNT);
-  if (UseMaterialDesign()) {
-    // Harmony bubbles don't use arrows.
-    alignment_ = ALIGN_EDGE_TO_ANCHOR_EDGE;
-    arrow_paint_type_ = PAINT_NONE;
-  } else {
-    images_ = GetBorderImages(shadow_);
-  }
+  Init();
 }
 
 BubbleBorder::~BubbleBorder() {}
@@ -196,6 +189,11 @@
   return blur + offset;
 }
 
+void BubbleBorder::SetCornerRadius(int corner_radius) {
+  corner_radius_ = corner_radius;
+  Init();
+}
+
 void BubbleBorder::set_paint_arrow(ArrowPaintType value) {
   if (UseMaterialDesign())
     return;
@@ -303,7 +301,9 @@
 }
 
 int BubbleBorder::GetBorderCornerRadius() const {
-  return UseMaterialDesign() ? 2 : images_->corner_radius;
+  if (UseMaterialDesign())
+    return corner_radius_.value_or(kMaterialDesignCornerRadius);
+  return images_->corner_radius;
 }
 
 int BubbleBorder::GetArrowOffset(const gfx::Size& border_size) const {
@@ -338,6 +338,16 @@
     images_->border_thickness = border_interior_thickness;
 }
 
+void BubbleBorder::Init() {
+  if (UseMaterialDesign()) {
+    // Harmony bubbles don't use arrows.
+    alignment_ = ALIGN_EDGE_TO_ANCHOR_EDGE;
+    arrow_paint_type_ = PAINT_NONE;
+  } else {
+    images_ = GetBorderImages(shadow_);
+  }
+}
+
 void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) {
   if (UseMaterialDesign())
     return PaintMd(view, canvas);
@@ -561,6 +571,11 @@
   return images_;
 }
 
+bool BubbleBorder::UseMaterialDesign() const {
+  return ui::MaterialDesignController::IsSecondaryUiMaterial() ||
+         corner_radius_.has_value();
+}
+
 void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const {
   if (border_->shadow() == BubbleBorder::NO_SHADOW_OPAQUE_BORDER)
     canvas->DrawColor(border_->background_color());
diff --git a/ui/views/bubble/bubble_border.h b/ui/views/bubble/bubble_border.h
index 46531ed..68f4473c 100644
--- a/ui/views/bubble/bubble_border.h
+++ b/ui/views/bubble/bubble_border.h
@@ -192,6 +192,9 @@
     (canvas->sk_canvas()->*draw)(rect, GetBorderAndShadowFlags());
   }
 
+  // Set the corner radius, enables Material Design.
+  void SetCornerRadius(int radius);
+
   // Get or set the arrow type.
   void set_arrow(Arrow arrow) { arrow_ = arrow; }
   Arrow arrow() const { return arrow_; }
@@ -266,6 +269,9 @@
   // The border and arrow stroke size used in image assets, in pixels.
   static const int kStroke;
 
+  // Initializes the MD or non-MD BubbleBorder.
+  void Init();
+
   gfx::Size GetSizeForContentsSize(const gfx::Size& contents_size) const;
   gfx::ImageSkia* GetArrowImage() const;
   gfx::Rect GetArrowRect(const gfx::Rect& bounds) const;
@@ -287,8 +293,14 @@
 
   internal::BorderImages* GetImagesForTest() const;
 
+  // Whether to use material design.
+  bool UseMaterialDesign() const;
+
   Arrow arrow_;
   int arrow_offset_;
+  // Corner radius for the bubble border. If supplied the border will use
+  // material design.
+  base::Optional<int> corner_radius_;
   ArrowPaintType arrow_paint_type_;
   BubbleAlignment alignment_;
   Shadow shadow_;
diff --git a/ui/views/controls/menu/menu_config.h b/ui/views/controls/menu/menu_config.h
index a4ecc478..fb1ece4 100644
--- a/ui/views/controls/menu/menu_config.h
+++ b/ui/views/controls/menu/menu_config.h
@@ -121,6 +121,18 @@
   // Radius of the rounded corners of the menu border. Must be >= 0.
   int corner_radius;
 
+  // Radius of the rounded corners of the touchable menu border
+  int touchable_corner_radius;
+
+  // Height of child MenuItemViews for touchable menus.
+  int touchable_menu_height;
+
+  // Width of touchable menus.
+  int touchable_menu_width;
+
+  // Vertical padding for touchable menus.
+  int vertical_touchable_menu_item_padding;
+
  private:
   // Configures a MenuConfig as appropriate for the current platform.
   void Init();
diff --git a/ui/views/controls/menu/menu_config_chromeos.cc b/ui/views/controls/menu/menu_config_chromeos.cc
index 75250e7c..f6e8fe7 100644
--- a/ui/views/controls/menu/menu_config_chromeos.cc
+++ b/ui/views/controls/menu/menu_config_chromeos.cc
@@ -23,6 +23,10 @@
   align_arrow_and_shortcut = true;
   offset_context_menus = true;
   corner_radius = 2;
+  touchable_corner_radius = 8;
+  touchable_menu_height = 36;
+  touchable_menu_width = 256;
+  vertical_touchable_menu_item_padding = 8;
 
   // In Ash, the border is provided by the shadow.
   use_outer_border = false;
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index c30fb8e6..83a1a0f 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -1929,8 +1929,8 @@
     pref.set_width(std::max(pref.width(), state_.initial_bounds.width()));
 
   // Don't let the menu go too wide.
-  pref.set_width(std::min(pref.width(),
-                            item->GetDelegate()->GetMaxWidthForMenu(item)));
+  pref.set_width(
+      std::min(pref.width(), item->GetDelegate()->GetMaxWidthForMenu(item)));
   if (!state_.monitor_bounds.IsEmpty())
     pref.set_width(std::min(pref.width(), state_.monitor_bounds.width()));
 
@@ -2161,6 +2161,28 @@
     }
     submenu->GetScrollViewContainer()->SetBubbleArrowOffset(
         pref.width() / 2 - x + x_old);
+  } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE) {
+    // Align the left edges of the menu and anchor, and the bottom of the menu
+    // with the top of the anchor.
+    x = owner_bounds.origin().x();
+    y = owner_bounds.origin().y() - pref.height();
+    // Align the right of the container with the right of the app icon.
+    if (x + pref.width() > state_.monitor_bounds.width())
+      x = owner_bounds.right() - pref.width();
+    // Align the top of the menu with the bottom of the anchor.
+    if (y < 0)
+      y = owner_bounds.bottom();
+  } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT) {
+    // Align the right of the menu with the left of the anchor, and the top of
+    // the menu with the top of the anchor.
+    x = owner_bounds.origin().x() - pref.width();
+    y = owner_bounds.origin().y();
+    // Align the left of the menu with the right of the anchor.
+    if (x < 0)
+      x = owner_bounds.right();
+    // Align the bottom of the menu to the bottom of the anchor.
+    if (y + pref.height() > state_.monitor_bounds.height())
+      y = owner_bounds.bottom() - pref.height();
   } else {
     if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT)
       x = owner_bounds.right() - kBubbleTipSizeLeftRight;
diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h
index 1a056eb..7154724 100644
--- a/ui/views/controls/menu/menu_controller.h
+++ b/ui/views/controls/menu/menu_controller.h
@@ -198,6 +198,11 @@
   // Only used for testing.
   static void TurnOffMenuSelectionHoldForTest();
 
+  void set_use_touchable_layout(bool use_touchable_layout) {
+    use_touchable_layout_ = use_touchable_layout;
+  }
+  bool use_touchable_layout() const { return use_touchable_layout_; }
+
  private:
   friend class internal::MenuRunnerImpl;
   friend class test::MenuControllerTest;
@@ -681,6 +686,9 @@
   // Set to true if the menu item was selected by touch.
   bool item_selected_by_touch_ = false;
 
+  // Whether to use the touchable layout.
+  bool use_touchable_layout_ = false;
+
   // During mouse event handling, this is the RootView to forward mouse events
   // to. We need this, because if we forward one event to it (e.g., mouse
   // pressed), subsequent events (like dragging) should also go to it, even if
diff --git a/ui/views/controls/menu/menu_host.cc b/ui/views/controls/menu/menu_host.cc
index bcdd4af..d332a2d86 100644
--- a/ui/views/controls/menu/menu_host.cc
+++ b/ui/views/controls/menu/menu_host.cc
@@ -116,7 +116,9 @@
   const MenuController* menu_controller =
       submenu_->GetMenuItem()->GetMenuController();
   const MenuConfig& menu_config = MenuConfig::instance();
-  bool rounded_border = menu_controller && menu_config.corner_radius > 0;
+  bool rounded_border =
+      (menu_controller && menu_controller->use_touchable_layout()) ||
+      (menu_controller && menu_config.corner_radius > 0);
   bool bubble_border = submenu_->GetScrollViewContainer() &&
                        submenu_->GetScrollViewContainer()->HasBubbleBorder();
   params.shadow_type = bubble_border ? Widget::InitParams::SHADOW_TYPE_NONE
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 4cf7d61..a155298 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -199,7 +199,9 @@
   return anchor == MENU_ANCHOR_BUBBLE_LEFT ||
          anchor == MENU_ANCHOR_BUBBLE_RIGHT ||
          anchor == MENU_ANCHOR_BUBBLE_ABOVE ||
-         anchor == MENU_ANCHOR_BUBBLE_BELOW;
+         anchor == MENU_ANCHOR_BUBBLE_BELOW ||
+         anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE ||
+         anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT;
 }
 
 // static
@@ -1012,6 +1014,15 @@
   MenuItemDimensions dimensions;
   // Get the container height.
   dimensions.children_width = child_size.width();
+  const MenuConfig& menu_config = MenuConfig::instance();
+
+  if (GetMenuController() && GetMenuController()->use_touchable_layout()) {
+    // MenuItemViews that use the touchable layout have fixed height and width.
+    dimensions.height = menu_config.touchable_menu_height;
+    dimensions.standard_width = menu_config.touchable_menu_width;
+    return dimensions;
+  }
+
   dimensions.height = child_size.height();
   // Adjust item content height if menu has both items with and without icons.
   // This way all menu items will have the same height.
diff --git a/ui/views/controls/menu/menu_runner.h b/ui/views/controls/menu/menu_runner.h
index 56a53d9..88b4bb4 100644
--- a/ui/views/controls/menu/menu_runner.h
+++ b/ui/views/controls/menu/menu_runner.h
@@ -95,6 +95,9 @@
     // shelf uses the flag to continue dragging an item without lifting the
     // finger after the context menu of the item is opened.
     SEND_GESTURE_EVENTS_TO_OWNER = 1 << 7,
+
+    // Whether to use the touchable layout for this context menu.
+    USE_TOUCHABLE_LAYOUT = 1 << 8,
   };
 
   // Creates a new MenuRunner, which may use a native menu if available.
diff --git a/ui/views/controls/menu/menu_runner_impl.cc b/ui/views/controls/menu/menu_runner_impl.cc
index 0004fe4..cd9ac6df 100644
--- a/ui/views/controls/menu/menu_runner_impl.cc
+++ b/ui/views/controls/menu/menu_runner_impl.cc
@@ -128,6 +128,8 @@
   controller->set_is_combobox((run_types & MenuRunner::COMBOBOX) != 0);
   controller->set_send_gesture_events_to_owner(
       (run_types & MenuRunner::SEND_GESTURE_EVENTS_TO_OWNER) != 0);
+  controller->set_use_touchable_layout(
+      (run_types & MenuRunner::USE_TOUCHABLE_LAYOUT) != 0);
   controller_ = controller->AsWeakPtr();
   menu_->set_controller(controller_.get());
   menu_->PrepareForRun(owns_controller_, has_mnemonics,
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc
index 01069fe..2ab1fe6 100644
--- a/ui/views/controls/menu/menu_scroll_view_container.cc
+++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -25,7 +25,7 @@
 
 namespace {
 
-static const int kBorderPaddingDueToRoundedCorners = 1;
+static constexpr int kBorderPaddingDueToRoundedCorners = 1;
 
 // MenuScrollButton ------------------------------------------------------------
 
@@ -251,7 +251,10 @@
   gfx::Rect bounds(0, 0, width(), height());
   NativeTheme::ExtraParams extra;
   const MenuConfig& menu_config = MenuConfig::instance();
-  extra.menu_background.corner_radius = menu_config.corner_radius;
+  extra.menu_background.corner_radius =
+      content_view_->GetMenuItem()->GetMenuController()->use_touchable_layout()
+          ? menu_config.touchable_corner_radius
+          : menu_config.corner_radius;
   GetNativeTheme()->Paint(canvas->sk_canvas(),
       NativeTheme::kMenuPopupBackground, NativeTheme::kNormal, bounds, extra);
 }
@@ -300,9 +303,17 @@
 }
 
 void MenuScrollViewContainer::CreateBubbleBorder() {
-  bubble_border_ = new BubbleBorder(arrow_,
-                                    BubbleBorder::SMALL_SHADOW,
-                                    SK_ColorWHITE);
+  bubble_border_ =
+      new BubbleBorder(arrow_, BubbleBorder::SMALL_SHADOW, SK_ColorWHITE);
+  if (content_view_->GetMenuItem()
+          ->GetMenuController()
+          ->use_touchable_layout()) {
+    const MenuConfig& menu_config = MenuConfig::instance();
+    bubble_border_->SetCornerRadius(menu_config.touchable_corner_radius);
+    scroll_view_->GetContents()->SetBorder(CreateEmptyBorder(
+        gfx::Insets(menu_config.vertical_touchable_menu_item_padding, 0)));
+  }
+
   SetBorder(std::unique_ptr<Border>(bubble_border_));
   SetBackground(std::make_unique<BubbleBackground>(bubble_border_));
 }
@@ -318,6 +329,9 @@
       return BubbleBorder::BOTTOM_CENTER;
     case MENU_ANCHOR_BUBBLE_BELOW:
       return BubbleBorder::TOP_CENTER;
+    case MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE:
+    case MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT:
+      return BubbleBorder::FLOAT;
     default:
       return BubbleBorder::NONE;
   }
diff --git a/ui/views/controls/menu/menu_types.h b/ui/views/controls/menu/menu_types.h
index 2f99222..d95ce0b 100644
--- a/ui/views/controls/menu/menu_types.h
+++ b/ui/views/controls/menu/menu_types.h
@@ -21,7 +21,9 @@
   MENU_ANCHOR_BUBBLE_LEFT,
   MENU_ANCHOR_BUBBLE_RIGHT,
   MENU_ANCHOR_BUBBLE_ABOVE,
-  MENU_ANCHOR_BUBBLE_BELOW
+  MENU_ANCHOR_BUBBLE_BELOW,
+  MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE,
+  MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT
 };
 
 }  // namespace views
diff --git a/ui/views/controls/menu/submenu_view.cc b/ui/views/controls/menu/submenu_view.cc
index b8926ded..9b244c9 100644
--- a/ui/views/controls/menu/submenu_view.cc
+++ b/ui/views/controls/menu/submenu_view.cc
@@ -24,10 +24,10 @@
 namespace {
 
 // Height of the drop indicator. This should be an even number.
-const int kDropIndicatorHeight = 2;
+constexpr int kDropIndicatorHeight = 2;
 
 // Color of the drop indicator.
-const SkColor kDropIndicatorColor = SK_ColorBLACK;
+constexpr SkColor kDropIndicatorColor = SK_ColorBLACK;
 
 }  // namespace
 
@@ -69,7 +69,7 @@
   return false;
 }
 
-int SubmenuView::GetMenuItemCount() {
+int SubmenuView::GetMenuItemCount() const {
   int count = 0;
   for (int i = 0; i < child_count(); ++i) {
     if (child_at(i)->id() == MenuItemView::kMenuItemViewID)
@@ -144,6 +144,8 @@
   int max_complex_width = 0;
   // The max. width of items which contain a label and maybe an accelerator.
   int max_simple_width = 0;
+  // The minimum width of touchable items.
+  int touchable_minimum_width = 0;
 
   // We perform the size calculation in two passes. In the first pass, we
   // calculate the width of the menu. In the second, we calculate the height
@@ -163,6 +165,7 @@
           std::max(max_minor_text_width_, dimensions.minor_text_width);
       max_complex_width = std::max(max_complex_width,
           dimensions.standard_width + dimensions.children_width);
+      touchable_minimum_width = dimensions.standard_width;
     } else {
       max_complex_width = std::max(max_complex_width,
                                    child->GetPreferredSize().width());
@@ -178,6 +181,10 @@
                                     insets.width(),
                                 minimum_preferred_width_ - 2 * insets.width()));
 
+  if (GetMenuItem()->GetMenuController() &&
+      GetMenuItem()->GetMenuController()->use_touchable_layout())
+    width = std::max(touchable_minimum_width, width);
+
   // Then, the height for that width.
   int height = 0;
   int menu_item_width = width - insets.width();
diff --git a/ui/views/controls/menu/submenu_view.h b/ui/views/controls/menu/submenu_view.h
index c17c944..9ba6933 100644
--- a/ui/views/controls/menu/submenu_view.h
+++ b/ui/views/controls/menu/submenu_view.h
@@ -56,7 +56,7 @@
 
   // Returns the number of child views that are MenuItemViews.
   // MenuItemViews are identified by ID.
-  int GetMenuItemCount();
+  int GetMenuItemCount() const;
 
   // Returns the MenuItemView at the specified index.
   MenuItemView* GetMenuItemAt(int index);