diff --git a/DEPS b/DEPS
index 7174763b..6c769d5 100644
--- a/DEPS
+++ b/DEPS
@@ -171,7 +171,7 @@
   # 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': 'e107faa062c448e1cd405be4b5358170847a74fb',
+  'skia_revision': 'b653813d58146fb41cd1852e5f24cdfc0ad9532a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -183,7 +183,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': '3a8b8908605bbb8da453cf5cee466922736d70f2',
+  'angle_revision': '6b652b335259295d90702fff9f3b34988eef36f7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -234,7 +234,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '13b94d068bdf8672d1b722c82baeb609939dea05',
+  'catapult_revision': 'a38631cd74176fd9daf5a402115523d16d31b8b3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -314,7 +314,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.
-  'quiche_revision': '4cc508c720aca5e15d1e5d89b970f58c57414764',
+  'quiche_revision': 'b38e635502035083ead0d64e26026ee724238c65',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -874,7 +874,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '151f5fbf383de255e10c5712d6cfc7c5ff296328',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'cb771c6c486eea2b7e1eeab9ca04f55fff3a1a7d',
       'condition': 'checkout_linux',
   },
 
@@ -899,7 +899,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1906f405ba0f00b960b89b751d36630d558a4211',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b1a96724321f55a1ce761b171f1b0bca86f31f99',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1308,7 +1308,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '9d48adc199ee8c8a292483189ec82065c378e949',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '1ac605ab1a8858d4c16771bf08c21a735a4f666c',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1576,7 +1576,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@21b8d3ff0a35cffc1526f9ac23ef791d17191fff',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c09baddc696fa7022c7bc8422b04da4e89dcce89',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 8de4ce3..871bc6e1 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1311,6 +1311,7 @@
     'build/android/devil_chromium.pydeps',
     'build/android/gyp/aar.pydeps',
     'build/android/gyp/aidl.pydeps',
+    'build/android/gyp/allot_native_libraries.pydeps',
     'build/android/gyp/apkbuilder.pydeps',
     'build/android/gyp/assert_static_initializers.pydeps',
     'build/android/gyp/bytecode_processor.pydeps',
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index bced5247..e90b2bd 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -50,6 +50,7 @@
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
+#include "build/build_config.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/cdm/browser/cdm_message_filter_android.h"
 #include "components/cdm/browser/media_drm_storage_impl.h"
@@ -91,6 +92,7 @@
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "net/android/network_library.h"
 #include "net/http/http_util.h"
+#include "net/net_buildflags.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/ssl/ssl_info.h"
 #include "services/network/network_service.h"
@@ -413,10 +415,13 @@
 
   const std::string scheme = url.scheme();
   DCHECK_EQ(scheme, base::ToLowerASCII(scheme));
-  // See CreateJobFactory in aw_url_request_context_getter.cc for the
-  // list of protocols that are handled.
-  // TODO(mnaganov): Make this automatic.
   static const char* const kProtocolList[] = {
+    url::kHttpScheme,
+    url::kHttpsScheme,
+#if BUILDFLAG(ENABLE_WEBSOCKETS)
+    url::kWsScheme,
+    url::kWssScheme,
+#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
     url::kDataScheme,
     url::kBlobScheme,
     url::kFileSystemScheme,
@@ -428,11 +433,11 @@
     // even if access to file: scheme is not granted to the child process.
     return !IsAndroidSpecialFileUrl(url);
   }
-  for (size_t i = 0; i < base::size(kProtocolList); ++i) {
-    if (scheme == kProtocolList[i])
+  for (const char* supported_protocol : kProtocolList) {
+    if (scheme == supported_protocol)
       return true;
   }
-  return net::URLRequest::IsHandledProtocol(scheme);
+  return false;
 }
 
 bool AwContentBrowserClient::ForceSniffingFileUrlsForHtml() {
@@ -970,21 +975,6 @@
   return true;
 }
 
-void AwContentBrowserClient::WillCreateURLLoaderFactoryForAppCacheSubresource(
-    int render_process_id,
-    mojo::PendingRemote<network::mojom::URLLoaderFactory>* pending_factory) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  auto pending_proxy = std::move(*pending_factory);
-  mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver =
-      pending_factory->InitWithNewPipeAndPassReceiver();
-
-  base::PostTask(FROM_HERE, {content::BrowserThread::IO},
-                 base::BindOnce(&AwProxyingURLLoaderFactory::CreateProxy,
-                                render_process_id, std::move(factory_receiver),
-                                std::move(pending_proxy)));
-}
-
 uint32_t AwContentBrowserClient::GetWebSocketOptions(
     content::RenderFrameHost* frame) {
   uint32_t options = network::mojom::kWebSocketOptionNone;
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index d943ae21..68be1683f 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -210,10 +210,6 @@
       mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
           header_client,
       bool* bypass_redirect_checks) override;
-  void WillCreateURLLoaderFactoryForAppCacheSubresource(
-      int render_process_id,
-      mojo::PendingRemote<network::mojom::URLLoaderFactory>* pending_factory)
-      override;
   uint32_t GetWebSocketOptions(content::RenderFrameHost* frame) override;
   bool WillCreateRestrictedCookieManager(
       network::mojom::RestrictedCookieManagerRole role,
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
index 58ff811..7daa16f9 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
@@ -2403,7 +2403,6 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Preferences"})
-    @DisabledTest(message = "crbug.com/1010034")
     // As our implementation of network loads blocking uses the same net::URLRequest settings, make
     // sure that setting cache mode doesn't accidentally enable network loads.  The reference
     // behaviour is that when network loads are blocked, setting cache mode has no effect.
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index c911af5..34ddaae71 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1129,7 +1129,7 @@
         Input
       </message>
       <message name="IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED_EXCEEDED_MAXIMUM" desc="The label used when user connects more external displays than the maximum that the device can support.">
-        Your device is connected to the maximum number of monitors, so one of the screens has been disconnected.
+        This device couldn't support all of your displays, so one has been disconnected
       </message>
       <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING" desc="The label used in the tray to show that the current status is mirroring.">
         Mirroring to <ph name="DISPLAY_NAME">$1</ph>
diff --git a/ash/shelf/hotseat_widget.cc b/ash/shelf/hotseat_widget.cc
index 5091a49e..8e8a007 100644
--- a/ash/shelf/hotseat_widget.cc
+++ b/ash/shelf/hotseat_widget.cc
@@ -173,7 +173,8 @@
     // The shelf view observes the shelf model and creates icons as items are
     // added to the model.
     shelf_view_ = GetContentsView()->AddChildView(std::make_unique<ShelfView>(
-        ShelfModel::Get(), shelf, /*drag_and_drop_host=*/nullptr));
+        ShelfModel::Get(), shelf, /*drag_and_drop_host=*/nullptr,
+        /*shelf_button_delegate=*/nullptr));
     shelf_view_->Init();
   }
   delegate_view_->Init(scrollable_shelf_view(), GetLayer());
diff --git a/ash/shelf/scrollable_shelf_view.cc b/ash/shelf/scrollable_shelf_view.cc
index 0c0acbb4..af20c2f 100644
--- a/ash/shelf/scrollable_shelf_view.cc
+++ b/ash/shelf/scrollable_shelf_view.cc
@@ -330,7 +330,10 @@
 // ScrollableShelfView
 
 ScrollableShelfView::ScrollableShelfView(ShelfModel* model, Shelf* shelf)
-    : shelf_view_(new ShelfView(model, shelf, /*drag_and_drop_host=*/this)),
+    : shelf_view_(new ShelfView(model,
+                                shelf,
+                                /*drag_and_drop_host=*/this,
+                                /*shelf_button_delegate=*/this)),
       page_flip_time_threshold_(kShelfPageFlipDelay) {
   Shell::Get()->AddShellObserver(this);
   set_allow_deactivate_on_esc(true);
@@ -747,7 +750,24 @@
 
 void ScrollableShelfView::OnShelfButtonAboutToRequestFocusFromTabTraversal(
     ShelfButton* button,
-    bool reverse) {}
+    bool reverse) {
+  if ((button == left_arrow_) || (button == right_arrow_))
+    return;
+
+  shelf_view_->OnShelfButtonAboutToRequestFocusFromTabTraversal(button,
+                                                                reverse);
+}
+
+void ScrollableShelfView::ButtonPressed(views::Button* sender,
+                                        const ui::Event& event,
+                                        views::InkDrop* ink_drop) {
+  if ((sender == left_arrow_) || (sender == right_arrow_)) {
+    ScrollToNewPage(sender == right_arrow_);
+    return;
+  }
+
+  shelf_view_->ButtonPressed(sender, event, ink_drop);
+}
 
 void ScrollableShelfView::ShowContextMenuForViewImpl(
     views::View* source,
@@ -757,16 +777,6 @@
   shelf_view_->ShowContextMenuForViewImpl(shelf_view_, point, source_type);
 }
 
-void ScrollableShelfView::ButtonPressed(views::Button* sender,
-                                        const ui::Event& event,
-                                        views::InkDrop* ink_drop) {
-  // Verfies that |sender| is either |left_arrow_| or |right_arrow_|.
-  views::View* sender_view = sender;
-  DCHECK((sender_view == left_arrow_) || (sender_view == right_arrow_));
-
-  ScrollToNewPage(sender_view == right_arrow_);
-}
-
 void ScrollableShelfView::OnShelfAlignmentChanged(aura::Window* root_window) {
   const bool is_horizontal_alignment = GetShelf()->IsHorizontalAlignment();
   left_arrow_->set_is_horizontal_alignment(is_horizontal_alignment);
diff --git a/ash/shelf/scrollable_shelf_view.h b/ash/shelf/scrollable_shelf_view.h
index 3b7a002e..e7c1793 100644
--- a/ash/shelf/scrollable_shelf_view.h
+++ b/ash/shelf/scrollable_shelf_view.h
@@ -170,16 +170,15 @@
   // ShelfButtonDelegate:
   void OnShelfButtonAboutToRequestFocusFromTabTraversal(ShelfButton* button,
                                                         bool reverse) override;
+  void ButtonPressed(views::Button* sender,
+                     const ui::Event& event,
+                     views::InkDrop* ink_drop) override;
 
   // ContextMenuController:
   void ShowContextMenuForViewImpl(views::View* source,
                                   const gfx::Point& point,
                                   ui::MenuSourceType source_type) override;
 
-  void ButtonPressed(views::Button* sender,
-                     const ui::Event& event,
-                     views::InkDrop* ink_drop) override;
-
   // Overridden from ShellObserver:
   void OnShelfAlignmentChanged(aura::Window* root_window) override;
 
diff --git a/ash/shelf/shelf_app_button.cc b/ash/shelf/shelf_app_button.cc
index 65c3b6a..93db255 100644
--- a/ash/shelf/shelf_app_button.cc
+++ b/ash/shelf/shelf_app_button.cc
@@ -296,8 +296,9 @@
 // static
 const char ShelfAppButton::kViewClassName[] = "ash/ShelfAppButton";
 
-ShelfAppButton::ShelfAppButton(ShelfView* shelf_view)
-    : ShelfButton(shelf_view->shelf(), shelf_view),
+ShelfAppButton::ShelfAppButton(ShelfView* shelf_view,
+                               ShelfButtonDelegate* shelf_button_delegate)
+    : ShelfButton(shelf_view->shelf(), shelf_button_delegate),
       icon_view_(new views::ImageView()),
       shelf_view_(shelf_view),
       indicator_(new AppStatusIndicatorView()),
diff --git a/ash/shelf/shelf_app_button.h b/ash/shelf/shelf_app_button.h
index 07fda7e..02acbef 100644
--- a/ash/shelf/shelf_app_button.h
+++ b/ash/shelf/shelf_app_button.h
@@ -45,7 +45,8 @@
     STATE_ACTIVE = 1 << 6,
   };
 
-  explicit ShelfAppButton(ShelfView* shelf_view);
+  ShelfAppButton(ShelfView* shelf_view,
+                 ShelfButtonDelegate* shelf_button_delegate);
   ~ShelfAppButton() override;
 
   // Sets the image to display for this entry.
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 48011aa..ba0f83b 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -301,13 +301,15 @@
 
 ShelfView::ShelfView(ShelfModel* model,
                      Shelf* shelf,
-                     ApplicationDragAndDropHost* drag_and_drop_host)
+                     ApplicationDragAndDropHost* drag_and_drop_host,
+                     ShelfButtonDelegate* shelf_button_delegate)
     : model_(model),
       shelf_(shelf),
       view_model_(std::make_unique<views::ViewModel>()),
       bounds_animator_(std::make_unique<views::BoundsAnimator>(this)),
       focus_search_(std::make_unique<ShelfFocusSearch>(this)),
-      drag_and_drop_host_(drag_and_drop_host) {
+      drag_and_drop_host_(drag_and_drop_host),
+      shelf_button_delegate_(shelf_button_delegate) {
   DCHECK(model_);
   DCHECK(shelf_);
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
@@ -525,7 +527,8 @@
     overflow_bubble_.reset(new OverflowBubble(shelf_));
 
   ShelfView* overflow_view =
-      new ShelfView(model_, shelf_, /*drag_and_drop_host=*/nullptr);
+      new ShelfView(model_, shelf_, /*drag_and_drop_host=*/nullptr,
+                    /*shelf_button_delegate=*/nullptr);
   overflow_view->overflow_mode_ = true;
   overflow_view->Init();
   overflow_view->set_owner_overflow_bubble(overflow_bubble_.get());
@@ -1154,7 +1157,8 @@
     case TYPE_BROWSER_SHORTCUT:
     case TYPE_APP:
     case TYPE_DIALOG: {
-      ShelfAppButton* button = new ShelfAppButton(this);
+      ShelfAppButton* button = new ShelfAppButton(
+          this, shelf_button_delegate_ ? shelf_button_delegate_ : this);
       button->SetImage(item.image);
       button->ReflectItemStatus(item);
       view = button;
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 4271f2b..381268a 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -120,7 +120,8 @@
  public:
   ShelfView(ShelfModel* model,
             Shelf* shelf,
-            ApplicationDragAndDropHost* drag_and_drop_host);
+            ApplicationDragAndDropHost* drag_and_drop_host,
+            ShelfButtonDelegate* delegate);
   ~ShelfView() override;
 
   Shelf* shelf() const { return shelf_; }
@@ -733,6 +734,10 @@
   // ScrollableShelfView.
   ApplicationDragAndDropHost* drag_and_drop_host_ = nullptr;
 
+  // When the scrollable shelf is enabled, |shelf_button_delegate_| should
+  // be ScrollableShelfView.
+  ShelfButtonDelegate* shelf_button_delegate_ = nullptr;
+
   base::WeakPtrFactory<ShelfView> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ShelfView);
diff --git a/ash/system/message_center/unified_message_center_bubble.cc b/ash/system/message_center/unified_message_center_bubble.cc
index 2062e43..2b8f537 100644
--- a/ash/system/message_center/unified_message_center_bubble.cc
+++ b/ash/system/message_center/unified_message_center_bubble.cc
@@ -55,10 +55,6 @@
   tray->bubble()->unified_view()->AddObserver(this);
 
   UpdatePosition();
-
-  if (!message_center_view_->GetVisible()) {
-    bubble_widget_->Hide();
-  }
 }
 
 UnifiedMessageCenterBubble::~UnifiedMessageCenterBubble() {
@@ -132,15 +128,6 @@
   return bubble_widget_;
 }
 
-void UnifiedMessageCenterBubble::OnViewVisibilityChanged(
-    views::View* observed_view,
-    views::View* starting_view) {
-  if (message_center_view_->GetVisible())
-    bubble_widget_->Show();
-  else
-    bubble_widget_->Hide();
-}
-
 void UnifiedMessageCenterBubble::OnViewPreferredSizeChanged(
     views::View* observed_view) {
   UpdatePosition();
diff --git a/ash/system/message_center/unified_message_center_bubble.h b/ash/system/message_center/unified_message_center_bubble.h
index 265d2176..0ce1082 100644
--- a/ash/system/message_center/unified_message_center_bubble.h
+++ b/ash/system/message_center/unified_message_center_bubble.h
@@ -59,8 +59,6 @@
   views::Widget* GetBubbleWidget() const override;
 
   // views::ViewObserver:
-  void OnViewVisibilityChanged(views::View* observed_view,
-                               views::View* starting_view) override;
   void OnViewPreferredSizeChanged(views::View* observed_view) override;
 
   // views::WidgetObserver:
diff --git a/ash/system/message_center/unified_message_center_bubble_unittest.cc b/ash/system/message_center/unified_message_center_bubble_unittest.cc
index a3b210f..e5f8dd34 100644
--- a/ash/system/message_center/unified_message_center_bubble_unittest.cc
+++ b/ash/system/message_center/unified_message_center_bubble_unittest.cc
@@ -129,20 +129,6 @@
   DISALLOW_COPY_AND_ASSIGN(UnifiedMessageCenterBubbleTest);
 };
 
-TEST_F(UnifiedMessageCenterBubbleTest, VisibileOnlyWithNotifications) {
-  EnableMessageCenterRefactor();
-  GetPrimaryUnifiedSystemTray()->ShowBubble(true);
-  // UnifiedMessageCenterBubble should not be visible when there are no
-  // notifications.
-  EXPECT_FALSE(GetMessageCenterBubble()->GetBubbleWidget()->IsVisible());
-
-  AddNotification();
-
-  // UnifiedMessageCenterBubble should become visible after a notification is
-  // added.
-  EXPECT_TRUE(GetMessageCenterBubble()->GetBubbleWidget()->IsVisible());
-}
-
 TEST_F(UnifiedMessageCenterBubbleTest, PositionedAboveSystemTray) {
   const int total_notifications = 5;
   EnableMessageCenterRefactor();
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 2897bfe2..63beac28 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -565,6 +565,7 @@
       ignore_property_change_(false),
       current_state_(new DefaultState(ToWindowStateType(GetShowState()))) {
   window_->AddObserver(this);
+  UpdateWindowPropertiesFromStateType();
   OnPrePipStateChange(WindowStateType::kDefault);
 }
 
diff --git a/base/command_line.cc b/base/command_line.cc
index 4782f8a..4bb290d 100644
--- a/base/command_line.cc
+++ b/base/command_line.cc
@@ -28,8 +28,9 @@
 
 namespace {
 
-const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
-const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("=");
+constexpr CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
+constexpr CommandLine::CharType kSwitchValueSeparator[] =
+    FILE_PATH_LITERAL("=");
 
 // Since we use a lazy match, make sure that longer versions (like "--") are
 // listed before shorter versions (like "-") of similar prefixes.
@@ -37,11 +38,10 @@
 // By putting slash last, we can control whether it is treaded as a switch
 // value by changing the value of switch_prefix_count to be one less than
 // the array size.
-const CommandLine::CharType* const kSwitchPrefixes[] = {
-    FILE_PATH_LITERAL("--"), FILE_PATH_LITERAL("-"), FILE_PATH_LITERAL("/")};
+constexpr CommandLine::StringPieceType kSwitchPrefixes[] = {L"--", L"-", L"/"};
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 // Unixes don't use slash as a switch.
-const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
+constexpr CommandLine::StringPieceType kSwitchPrefixes[] = {"--", "-"};
 #endif
 size_t switch_prefix_count = size(kSwitchPrefixes);
 
@@ -102,8 +102,7 @@
     parse_switches &= (arg != kSwitchTerminator);
     if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
 #if defined(OS_WIN)
-      command_line->AppendSwitchNative(UTF16ToASCII(switch_string),
-                                       switch_value);
+      command_line->AppendSwitchNative(WideToUTF8(switch_string), switch_value);
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
       command_line->AppendSwitchNative(switch_string, switch_value);
 #else
@@ -117,21 +116,21 @@
 
 #if defined(OS_WIN)
 // Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*.
-string16 QuoteForCommandLineToArgvW(const string16& arg,
-                                    bool quote_placeholders) {
+std::wstring QuoteForCommandLineToArgvW(const std::wstring& arg,
+                                        bool quote_placeholders) {
   // We follow the quoting rules of CommandLineToArgvW.
   // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
-  string16 quotable_chars(FILE_PATH_LITERAL(" \\\""));
+  std::wstring quotable_chars(L" \\\"");
   // We may also be required to quote '%', which is commonly used in a command
   // line as a placeholder. (It may be substituted for a string with spaces.)
   if (quote_placeholders)
     quotable_chars.push_back('%');
-  if (arg.find_first_of(quotable_chars) == string16::npos) {
+  if (arg.find_first_of(quotable_chars) == std::wstring::npos) {
     // No quoting necessary.
     return arg;
   }
 
-  string16 out;
+  std::wstring out;
   out.push_back('"');
   for (size_t i = 0; i < arg.size(); ++i) {
     if (arg[i] == '\\') {
@@ -200,9 +199,8 @@
 // static
 void CommandLine::set_slash_is_not_a_switch() {
   // The last switch prefix should be slash, so adjust the size to skip it.
-  // Leverage StringPiece16::operator== to do a deep comparison.
-  DCHECK_EQ(*std::rbegin(kSwitchPrefixes),
-            StringPiece16(FILE_PATH_LITERAL("/")));
+  static_assert(base::make_span(kSwitchPrefixes).back() == L"/",
+                "Error: Last switch prefix is not a slash.");
   switch_prefix_count = size(kSwitchPrefixes) - 1;
 }
 
@@ -210,10 +208,10 @@
 void CommandLine::InitUsingArgvForTesting(int argc, const char* const* argv) {
   DCHECK(!current_process_commandline_);
   current_process_commandline_ = new CommandLine(NO_PROGRAM);
-  // On Windows we need to convert the command line arguments to string16.
+  // On Windows we need to convert the command line arguments to std::wstring.
   CommandLine::StringVector argv_vector;
   for (int i = 0; i < argc; ++i)
-    argv_vector.push_back(UTF8ToUTF16(argv[i]));
+    argv_vector.push_back(UTF8ToWide(argv[i]));
   current_process_commandline_->InitFromArgv(argv_vector);
 }
 #endif
@@ -229,8 +227,7 @@
 
   current_process_commandline_ = new CommandLine(NO_PROGRAM);
 #if defined(OS_WIN)
-  current_process_commandline_->ParseFromString(
-      as_u16cstr(::GetCommandLineW()));
+  current_process_commandline_->ParseFromString(::GetCommandLineW());
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   current_process_commandline_->InitFromArgv(argc, argv);
 #else
@@ -260,7 +257,7 @@
 
 #if defined(OS_WIN)
 // static
-CommandLine CommandLine::FromString(StringPiece16 command_line) {
+CommandLine CommandLine::FromString(StringPieceType command_line) {
   CommandLine cmd(NO_PROGRAM);
   cmd.ParseFromString(command_line);
   return cmd;
@@ -309,12 +306,16 @@
 std::string CommandLine::GetSwitchValueASCII(
     const StringPiece& switch_string) const {
   StringType value = GetSwitchValueNative(switch_string);
+#if defined(OS_WIN)
+  if (!IsStringASCII(base::AsStringPiece16(value))) {
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   if (!IsStringASCII(value)) {
+#endif
     DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII.";
     return std::string();
   }
 #if defined(OS_WIN)
-  return UTF16ToASCII(value);
+  return WideToUTF8(value);
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   return value;
 #endif
@@ -345,7 +346,7 @@
                                      const CommandLine::StringType& value) {
 #if defined(OS_WIN)
   const std::string switch_key = ToLowerASCII(switch_string);
-  StringType combined_switch_string(ASCIIToUTF16(switch_key));
+  StringType combined_switch_string(UTF8ToWide(switch_key));
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   const std::string& switch_key = switch_string;
   StringType combined_switch_string(switch_key);
@@ -356,8 +357,10 @@
   if (!insertion.second)
     insertion.first->second = value;
   // Preserve existing switch prefixes in |argv_|; only append one if necessary.
-  if (prefix_length == 0)
-    combined_switch_string = kSwitchPrefixes[0] + combined_switch_string;
+  if (prefix_length == 0) {
+    combined_switch_string.insert(0, kSwitchPrefixes[0].data(),
+                                  kSwitchPrefixes[0].size());
+  }
   if (!value.empty())
     combined_switch_string += kSwitchValueSeparator + value;
   // Append the switch and update the switches/arguments divider |begin_args_|.
@@ -367,7 +370,7 @@
 void CommandLine::AppendSwitchASCII(const std::string& switch_string,
                                     const std::string& value_string) {
 #if defined(OS_WIN)
-  AppendSwitchNative(switch_string, ASCIIToUTF16(value_string));
+  AppendSwitchNative(switch_string, UTF8ToWide(value_string));
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   AppendSwitchNative(switch_string, value_string);
 #else
@@ -377,7 +380,7 @@
 
 void CommandLine::RemoveSwitch(base::StringPiece switch_key_without_prefix) {
 #if defined(OS_WIN)
-  StringType switch_key_native = base::ASCIIToUTF16(switch_key_without_prefix);
+  StringType switch_key_native = UTF8ToWide(switch_key_without_prefix);
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   StringType switch_key_native = switch_key_without_prefix.as_string();
 #endif
@@ -432,7 +435,7 @@
 void CommandLine::AppendArg(const std::string& value) {
 #if defined(OS_WIN)
   DCHECK(IsStringUTF8(value));
-  AppendArgNative(UTF8ToUTF16(value));
+  AppendArgNative(UTF8ToWide(value));
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   AppendArgNative(value);
 #else
@@ -473,7 +476,7 @@
 }
 
 #if defined(OS_WIN)
-void CommandLine::ParseFromString(StringPiece16 command_line) {
+void CommandLine::ParseFromString(StringPieceType command_line) {
   command_line = TrimWhitespace(command_line, TRIM_ALL);
   if (command_line.empty())
     return;
@@ -490,19 +493,17 @@
         reinterpret_cast<decltype(::CommandLineToArgvW)*>(
             ::GetProcAddress(downlevel_shell32_dll, "CommandLineToArgvW"));
     if (command_line_to_argv_w_proc)
-      args = command_line_to_argv_w_proc(as_wcstr(command_line), &num_args);
+      args = command_line_to_argv_w_proc(command_line.data(), &num_args);
     ::FreeLibrary(downlevel_shell32_dll);
   } else {
     // Since the apiset is not available, allow the delayload of shell32.dll
     // to take place.
-    args = ::CommandLineToArgvW(as_wcstr(command_line), &num_args);
+    args = ::CommandLineToArgvW(command_line.data(), &num_args);
   }
 
   DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: "
-                         << UTF16ToUTF8(command_line);
-  StringVector argv;
-  for (auto* arg : make_span(args, num_args))
-    argv.push_back(WideToUTF16(arg));
+                         << command_line;
+  StringVector argv(args, args + num_args);
   InitFromArgv(argv);
   LocalFree(args);
 }
@@ -516,7 +517,7 @@
 #endif
   StringType params(GetArgumentsStringInternal(quote_placeholders));
   if (!params.empty()) {
-    string.append(StringType(FILE_PATH_LITERAL(" ")));
+    string.append(FILE_PATH_LITERAL(" "));
     string.append(params);
   }
   return string;
@@ -533,7 +534,7 @@
     StringType switch_value;
     parse_switches &= arg != kSwitchTerminator;
     if (i > 1)
-      params.append(StringType(FILE_PATH_LITERAL(" ")));
+      params.append(FILE_PATH_LITERAL(" "));
     if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
       params.append(switch_string);
       if (!switch_value.empty()) {
diff --git a/base/command_line.h b/base/command_line.h
index cd32efb..41d54c8b 100644
--- a/base/command_line.h
+++ b/base/command_line.h
@@ -34,13 +34,12 @@
  public:
 #if defined(OS_WIN)
   // The native command line string type.
-  using StringType = string16;
-  using StringPieceType = base::StringPiece16;
+  using StringType = std::wstring;
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   using StringType = std::string;
-  using StringPieceType = base::StringPiece;
 #endif
 
+  using StringPieceType = base::BasicStringPiece<StringType>;
   using CharType = StringType::value_type;
   using StringVector = std::vector<StringType>;
   using SwitchMap = std::map<std::string, StringType, std::less<>>;
@@ -104,7 +103,7 @@
   static bool InitializedForCurrentProcess();
 
 #if defined(OS_WIN)
-  static CommandLine FromString(StringPiece16 command_line);
+  static CommandLine FromString(StringPieceType command_line);
 #endif
 
   // Initialize from an argv vector.
@@ -217,7 +216,7 @@
 #if defined(OS_WIN)
   // Initialize by parsing the given command line string.
   // The program name is assumed to be the first item in the string.
-  void ParseFromString(StringPiece16 command_line);
+  void ParseFromString(StringPieceType command_line);
 #endif
 
  private:
diff --git a/base/command_line_unittest.cc b/base/command_line_unittest.cc
index 28d1901..a4107b5 100644
--- a/base/command_line_unittest.cc
+++ b/base/command_line_unittest.cc
@@ -108,14 +108,14 @@
 
 TEST(CommandLineTest, CommandLineFromString) {
 #if defined(OS_WIN)
-  CommandLine cl = CommandLine::FromString(StrCat(
-      {STRING16_LITERAL("program --foo= -bAr  /Spaetzel=pierogi /Baz flim "),
-       STRING16_LITERAL("--other-switches=\"--dog=canine --cat=feline\" "),
-       STRING16_LITERAL("-spaetzle=Crepe   -=loosevalue  FLAN "),
-       STRING16_LITERAL("--input-translation=\"45\"--output-rotation "),
-       STRING16_LITERAL("--quotes="), kTrickyQuoted, STRING16_LITERAL(" "),
-       STRING16_LITERAL("-- -- --not-a-switch "),
-       STRING16_LITERAL("\"in the time of submarines...\"")}));
+  CommandLine cl = CommandLine::FromString(
+      L"program --foo= -bAr  /Spaetzel=pierogi /Baz flim "
+      L"--other-switches=\"--dog=canine --cat=feline\" "
+      L"-spaetzle=Crepe   -=loosevalue  FLAN "
+      L"--input-translation=\"45\"--output-rotation "
+      L"--quotes=" +
+      kTrickyQuoted +
+      L" -- -- --not-a-switch \"in the time of submarines...\"");
 
   EXPECT_FALSE(cl.GetCommandLineString().empty());
   EXPECT_FALSE(cl.HasSwitch("cruller"));
@@ -172,7 +172,7 @@
 // Tests behavior with an empty input string.
 TEST(CommandLineTest, EmptyString) {
 #if defined(OS_WIN)
-  CommandLine cl_from_string = CommandLine::FromString(string16());
+  CommandLine cl_from_string = CommandLine::FromString(std::wstring());
   EXPECT_TRUE(cl_from_string.GetCommandLineString().empty());
   EXPECT_TRUE(cl_from_string.GetProgram().empty());
   EXPECT_EQ(1U, cl_from_string.argv().size());
@@ -205,11 +205,11 @@
   cl.AppendArg(kFifthArgName);
 
 #if defined(OS_WIN)
-  CommandLine::StringType expected_first_arg(UTF8ToUTF16(kFirstArgName));
-  CommandLine::StringType expected_second_arg(UTF8ToUTF16(kSecondArgName));
-  CommandLine::StringType expected_third_arg(UTF8ToUTF16(kThirdArgName));
-  CommandLine::StringType expected_fourth_arg(UTF8ToUTF16(kFourthArgName));
-  CommandLine::StringType expected_fifth_arg(UTF8ToUTF16(kFifthArgName));
+  CommandLine::StringType expected_first_arg(UTF8ToWide(kFirstArgName));
+  CommandLine::StringType expected_second_arg(UTF8ToWide(kSecondArgName));
+  CommandLine::StringType expected_third_arg(UTF8ToWide(kThirdArgName));
+  CommandLine::StringType expected_fourth_arg(UTF8ToWide(kFourthArgName));
+  CommandLine::StringType expected_fifth_arg(UTF8ToWide(kFifthArgName));
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
   CommandLine::StringType expected_first_arg(kFirstArgName);
   CommandLine::StringType expected_second_arg(kSecondArgName);
@@ -293,15 +293,16 @@
 
 #if defined(OS_WIN)
   EXPECT_EQ(
-      StrCat({STRING16_LITERAL("Program "), STRING16_LITERAL("--switch1 "),
-              STRING16_LITERAL("--switch2=value "),
-              STRING16_LITERAL("--switch3=\"a value with spaces\" "),
-              STRING16_LITERAL("--switch4=\"\\\"a value with quotes\\\"\" "),
-              // Even though the switches are unique, appending can add
-              // repeat switches to argv.
-              STRING16_LITERAL("--quotes=\"\\\"a value with quotes\\\"\" "),
-              STRING16_LITERAL("--quotes=\""), kTrickyQuoted,
-              STRING16_LITERAL("\"")}),
+      L"Program "
+      L"--switch1 "
+      L"--switch2=value "
+      L"--switch3=\"a value with spaces\" "
+      L"--switch4=\"\\\"a value with quotes\\\"\" "
+      // Even though the switches are unique, appending can add repeat
+      // switches to argv.
+      L"--quotes=\"\\\"a value with quotes\\\"\" "
+      L"--quotes=\"" +
+          kTrickyQuoted + L"\"",
       cl.GetCommandLineString());
 #endif
 }
@@ -358,12 +359,12 @@
 // against regressions.
 TEST(CommandLineTest, ProgramQuotes) {
   // Check that quotes are not added for paths without spaces.
-  const FilePath kProgram(STRING16_LITERAL("Program"));
+  const FilePath kProgram(L"Program");
   CommandLine cl_program(kProgram);
   EXPECT_EQ(kProgram.value(), cl_program.GetProgram().value());
   EXPECT_EQ(kProgram.value(), cl_program.GetCommandLineString());
 
-  const FilePath kProgramPath(STRING16_LITERAL("Program Path"));
+  const FilePath kProgramPath(L"Program Path");
 
   // Check that quotes are not returned from GetProgram().
   CommandLine cl_program_path(kProgramPath);
@@ -371,13 +372,12 @@
 
   // Check that quotes are added to command line string paths containing spaces.
   CommandLine::StringType cmd_string(cl_program_path.GetCommandLineString());
-  EXPECT_EQ(STRING16_LITERAL("\"Program Path\""), cmd_string);
+  EXPECT_EQ(L"\"Program Path\"", cmd_string);
 
   // Check the optional quoting of placeholders in programs.
-  CommandLine cl_quote_placeholder(FilePath(STRING16_LITERAL("%1")));
-  EXPECT_EQ(STRING16_LITERAL("%1"),
-            cl_quote_placeholder.GetCommandLineString());
-  EXPECT_EQ(STRING16_LITERAL("\"%1\""),
+  CommandLine cl_quote_placeholder(FilePath(L"%1"));
+  EXPECT_EQ(L"%1", cl_quote_placeholder.GetCommandLineString());
+  EXPECT_EQ(L"\"%1\"",
             cl_quote_placeholder.GetCommandLineStringWithPlaceholders());
 }
 #endif
diff --git a/build/android/gyp/allot_native_libraries.py b/build/android/gyp/allot_native_libraries.py
new file mode 100755
index 0000000..4cad72d
--- /dev/null
+++ b/build/android/gyp/allot_native_libraries.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+#
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Allots libraries to modules to be packaged into.
+
+All libraries that are depended on by a single module will be allotted to this
+module. All other libraries will be allotted to base.
+"""
+
+import argparse
+import collections
+import json
+import sys
+
+from util import build_utils
+
+
+def _ModuleLibrariesPair(arg):
+  pos = arg.find(',')
+  assert pos > 0
+  return (arg[:pos], arg[pos + 1:])
+
+
+def main(args):
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--libraries',
+      action='append',
+      type=_ModuleLibrariesPair,
+      required=True,
+      help='A pair of module name and GN list of libraries a module depends '
+      'on. Can be specified multiple times. Must be set at least once for '
+      'base.')
+  parser.add_argument(
+      '--output',
+      required=True,
+      help='A JSON file with a key for each module mapping to a list of '
+      'libraries, which should be packaged into this module.')
+  options = parser.parse_args(build_utils.ExpandFileArgs(args))
+  options.libraries = [(m, build_utils.ParseGnList(l))
+                       for m, l in options.libraries]
+
+  # Parse input to map and count how many modules depend on each library.
+  libraries_map = {}
+  libraries_counter = collections.Counter()  # Modules count for each library.
+  for module, libraries in options.libraries:
+    if module not in libraries_map:
+      libraries_map[module] = set()
+    libraries_map[module].update(libraries)
+    libraries_counter.update(libraries)
+
+  assert 'base' in libraries_map
+
+  # Allot libraries depended on by multiple modules to base.
+  multidep_libraries = {l for l, c in libraries_counter.items() if c > 1}
+  libraries_map = {m: l - multidep_libraries for m, l in libraries_map.items()}
+  libraries_map['base'] |= multidep_libraries
+
+  with open(options.output, 'w') as f:
+    # Write native libraries config and ensure the output is deterministic.
+    json.dump({m: sorted(l)
+               for m, l in libraries_map.items()},
+              f,
+              sort_keys=True,
+              indent=2)
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/build/android/gyp/allot_native_libraries.pydeps b/build/android/gyp/allot_native_libraries.pydeps
new file mode 100644
index 0000000..dddde0c5
--- /dev/null
+++ b/build/android/gyp/allot_native_libraries.pydeps
@@ -0,0 +1,7 @@
+# Generated by running:
+#   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/allot_native_libraries.pydeps build/android/gyp/allot_native_libraries.py
+../../gn_helpers.py
+allot_native_libraries.py
+util/__init__.py
+util/build_utils.py
+util/md5_check.py
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 7ba548f..b1155f0c 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -3614,28 +3614,30 @@
 
 # Create a zip archive corresponding to an application bundle module.
 #
-# Compile all the components of a given android_apk_or_module() target into a zip archive
-# suitable to later create an android_app_bundle() target. This archive's format is very
-# similar to that on an APK, except for a few differences in internal directory
-# layouts, and the fact that resources, as well ass xml files, are compiled using a
-# protocol-buffer based format (instead of the regular binary xml + resources.arsc).
+# Compile all the components of a given android_apk_or_module() target into a
+# zip archive suitable to later create an android_app_bundle() target. This
+# archive's format is very similar to that on an APK, except for a few
+# differences in internal directory layouts, and the fact that resources, as
+# well as xml files, are compiled using a protocol-buffer based format (instead
+# of the regular binary xml + resources.arsc).
 #
 # A final application bundle is built from one or more module bundle modules,
 # plus some configuration file.
 #
 # Variables:
 #   module_zip_path: Output module path.
-#
 #   build_config: Path to build_config of the android_apk_or_module() target.
-#
-#   dex_path: If module is proguarded separately from the base module, dex_path is the
-#     path to its dex file and is passed directly to the creation script.
+#   dex_path: If module is proguarded separately from the base module, dex_path
+#     is the path to its dex file and is passed directly to the creation script.
 #     Otherwise, dex_path is undefined and we retrieve the module's dex file
 #     using its build_config.
-#
+#   module_name: The module's name.
+#   native_libraries_config: Path to file listing native libraries to be
+#     packaged into each module.
 template("create_android_app_bundle_module") {
-  _build_config = invoker.build_config
-  _rebased_build_config = rebase_path(_build_config, root_build_dir)
+  _rebased_build_config = rebase_path(invoker.build_config, root_build_dir)
+  _rebased_native_libraries_config =
+      rebase_path(invoker.native_libraries_config, root_build_dir)
 
   action_with_pydeps(target_name) {
     forward_variables_from(invoker,
@@ -3655,7 +3657,8 @@
     #       .build_config through @FileArg() references (see below) and
     #       will be listed in the generated depfile instead.
     inputs = [
-      _build_config,
+      invoker.build_config,
+      invoker.native_libraries_config,
     ]
     outputs = [
       invoker.module_zip_path,
@@ -3671,18 +3674,24 @@
       "--assets=@FileArg($_rebased_build_config:assets)",
       "--uncompressed-assets=@FileArg(" +
           "$_rebased_build_config:uncompressed_assets)",
-      "--native-libs=@FileArg($_rebased_build_config:native:libraries)",
-      "--native-libs=@FileArg($_rebased_build_config:native:extra_shared_libraries)",
-      "--native-lib-placeholders=@FileArg($_rebased_build_config:native:native_library_placeholders)",
-      "--secondary-native-lib-placeholders=@FileArg($_rebased_build_config:native:secondary_native_library_placeholders)",
+      "--native-libs=@FileArg($_rebased_native_libraries_config" +
+          ":${invoker.module_name})",
+      "--native-lib-placeholders=@FileArg($_rebased_build_config" +
+          ":native:native_library_placeholders)",
+      "--secondary-native-lib-placeholders=@FileArg($_rebased_build_config" +
+          ":native:secondary_native_library_placeholders)",
       "--android-abi=$android_app_abi",
       "--uncompress-shared-libraries=@FileArg(" +
           "$_rebased_build_config:native:uncompress_shared_libraries)",
     ]
     if (defined(android_app_secondary_abi)) {
+      _rebased_secondary_abi_native_libraries_config =
+          rebase_path(invoker.secondary_abi_native_libraries_config,
+                      root_build_dir)
       args += [
-        "--secondary-native-libs=@FileArg(" +
-            "$_rebased_build_config:native:secondary_abi_libraries)",
+        "--secondary-native-libs",
+        "@FileArg($_rebased_secondary_abi_native_libraries_config" +
+            ":${invoker.module_name})",
         "--secondary-android-abi=$android_app_secondary_abi",
       ]
     }
@@ -3752,3 +3761,46 @@
     }
   }
 }
+
+# Allots native libraries depended on by feature modules to the module the
+# libraries should be packaged into. The packaging module may be different from
+# the dependee module in case a library is depended on by multiple modules. In
+# that case the library will be allotted to base.
+#
+# Variables:
+#   modules: List of scopes with the following format:
+#     name: The module's name.
+#     build_config: Path to the module's build config.
+#     build_config_target: Target creating |build_config|.
+#   native_libraries_filearg_keys: Keys to be used in
+#     @FileArg(|build_config|:<keys>) expressions pointing to a list of native
+#     libraries to consider in |build_config|.
+#   output: Path to native libraries config.
+template("allot_native_libraries") {
+  action_with_pydeps(target_name) {
+    script = "//build/android/gyp/allot_native_libraries.py"
+    args = [
+      "--output",
+      rebase_path(invoker.output, root_build_dir),
+    ]
+    outputs = [
+      invoker.output,
+    ]
+    deps = []
+    if (defined(invoker.deps)) {
+      deps += invoker.deps
+    }
+    inputs = []
+    foreach(_module, invoker.modules) {
+      deps += [ _module.build_config_target ]
+      inputs += [ _module.build_config ]
+      _rebased_build_config = rebase_path(_module.build_config, root_out_dir)
+      foreach(_key, invoker.native_libraries_filearg_keys) {
+        args += [
+          "--libraries",
+          "${_module.name},@FileArg($_rebased_build_config:$_key)",
+        ]
+      }
+    }
+  }
+}
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 4a76d67..792b768 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2623,7 +2623,8 @@
       _srcjar_deps += [ ":$_compile_resources_target" ]
     }
 
-    if (_native_libs_deps != [] || _secondary_abi_native_libs_deps != []) {
+    if ((!_is_bundle_module || _is_base_module) &&
+        (_native_libs_deps != [] || _secondary_abi_native_libs_deps != [])) {
       write_native_libraries_java("${_template_name}__native_libraries") {
         forward_variables_from(invoker, [ "enable_chromium_linker_tests" ])
         deps = [
@@ -3468,7 +3469,6 @@
       assert(!defined(invoker.shared_resources_whitelist_target))
       assert(!defined(invoker.shared_resources_whitelist_locales))
       assert(!defined(invoker.build_hooks_android_impl_deps))
-      assert(!defined(invoker.shared_libraries))
       assert(defined(invoker.base_module_target))
       assert(!defined(invoker.bundle_target))
     }
@@ -4350,12 +4350,14 @@
   #   }
   #
   template("android_app_bundle") {
+    _target_name = target_name
+
     _bundle_base_path = "$root_build_dir/apks"
     if (defined(invoker.bundle_base_path)) {
       _bundle_base_path = invoker.bundle_base_path
     }
 
-    _bundle_name = target_name
+    _bundle_name = _target_name
     if (defined(invoker.bundle_name)) {
       _bundle_name = invoker.bundle_name
     }
@@ -4398,7 +4400,7 @@
       if (_uses_static_library) {
         _sync_dex_target_dep = "${invoker.static_library_provider}__dexsplitter"
       } else {
-        _sync_dex_target = "${target_name}__sync_dex"
+        _sync_dex_target = "${_target_name}__sync_dex"
         _sync_dex_target_dep = ":$_sync_dex_target"
       }
       _proguard_mapping_path = "${_bundle_path}.mapping"
@@ -4442,19 +4444,43 @@
       _module_build_configs += [ _module.build_config ]
     }
 
+    # Allot native libraries to modules they should be packaged into. This is
+    # necessary since all libraries that are depended on by multiple modules
+    # have to go into base.
+    _native_libraries_config =
+        "$target_gen_dir/$_target_name.native_libraries_config"
+    allot_native_libraries("${_target_name}__allot_native_libraries") {
+      modules = _modules
+      native_libraries_filearg_keys = [
+        "native:libraries",
+        "native:extra_shared_libraries",
+      ]
+      output = _native_libraries_config
+    }
+    if (defined(android_app_secondary_abi)) {
+      _secondary_abi_native_libraries_config =
+          "$target_gen_dir/$_target_name.secondary_abi_native_libraries_config"
+      allot_native_libraries(
+          "${_target_name}__allot_secondary_abi_native_libraries") {
+        modules = _modules
+        native_libraries_filearg_keys = [ "native:secondary_abi_libraries" ]
+        output = _secondary_abi_native_libraries_config
+      }
+    }
+
     # Used to expose the module Java targets of the bundle.
-    group("${target_name}__java") {
+    group("${_target_name}__java") {
       deps = _module_java_targets
     }
-    group("${target_name}__compile_resources") {
+    group("${_target_name}__compile_resources") {
       deps = [
         "${invoker.base_module_target}__compile_resources",
       ]
     }
 
-    _build_config = "$target_gen_dir/${target_name}.build_config"
+    _build_config = "$target_gen_dir/${_target_name}.build_config"
     _rebased_build_config = rebase_path(_build_config, root_build_dir)
-    _build_config_target = "$target_name$build_config_target_suffix"
+    _build_config_target = "$_target_name$build_config_target_suffix"
     if (defined(invoker.proguard_android_sdk_dep)) {
       proguard_android_sdk_dep_ = invoker.proguard_android_sdk_dep
     } else {
@@ -4466,7 +4492,7 @@
     }
 
     _unsplit_dex_zip =
-        "${target_gen_dir}/${target_name}/${target_name}__unsplit_dex.zip"
+        "${target_gen_dir}/${_target_name}/${_target_name}__unsplit_dex.zip"
 
     write_build_config(_build_config_target) {
       type = "android_app_bundle"
@@ -4496,8 +4522,8 @@
                                  invoker.verify_proguard_flags
         if (_verify_proguard_flags) {
           _proguard_expectations_file =
-              get_label_info(":$target_name", "dir") +
-              "/java/$target_name.proguard_flags.expected"
+              get_label_info(":$_target_name", "dir") +
+              "/java/$_target_name.proguard_flags.expected"
         }
         dex(_sync_dex_target) {
           enable_multidex = _enable_multidex
@@ -4520,7 +4546,7 @@
         }
       }
 
-      _dexsplitter_target = "${target_name}__dexsplitter"
+      _dexsplitter_target = "${_target_name}__dexsplitter"
       dexsplitter(_dexsplitter_target) {
         input_dex_zip = _unsplit_dex_zip
         proguard_mapping = _proguard_mapping_path
@@ -4551,18 +4577,27 @@
       # Important: the bundle tool uses the module's zip filename as
       # the internal module name inside the final bundle, in other words,
       # this file *must* be named ${_module.name}.zip
-      _create_module_target = "${target_name}__${_module.name}__create"
+      _create_module_target = "${_target_name}__${_module.name}__create"
       _module_zip_path = "$target_gen_dir/$target_name/${_module.name}.zip"
 
       create_android_app_bundle_module(_create_module_target) {
+        module_name = _module.name
         build_config = _module_build_config
         module_zip_path = _module_zip_path
+        native_libraries_config = _native_libraries_config
 
         deps = [
+          ":${_target_name}__allot_native_libraries",
           _dex_target_for_module,
           _module_build_config_target,
           _module_target,
         ]
+
+        if (defined(android_app_secondary_abi)) {
+          secondary_abi_native_libraries_config =
+              _secondary_abi_native_libraries_config
+          deps += [ ":${_target_name}__allot_secondary_abi_native_libraries" ]
+        }
       }
 
       _all_create_module_targets += [
@@ -4606,7 +4641,7 @@
       _bundle_keystore_name = _keystore_name
     }
 
-    _bundle_target_name = "${target_name}__bundle"
+    _bundle_target_name = "${_target_name}__bundle"
     action_with_pydeps(_bundle_target_name) {
       script = "//build/android/gyp/create_app_bundle.py"
       inputs = _all_module_zip_paths + _all_module_build_configs
@@ -4667,7 +4702,7 @@
       # Merge all module targets to obtain size info files for all targets.
       _all_module_targets = _module_targets
 
-      _size_info_target = "${target_name}__size_info"
+      _size_info_target = "${_target_name}__size_info"
       create_size_info_files(_size_info_target) {
         name = "$_bundle_name.aab"
         deps = _all_module_targets + [ ":$_build_config_target" ]
@@ -4680,9 +4715,9 @@
 
     _bundle_apks_path = "$_bundle_base_path/$_bundle_name.apks"
     _bundle_wrapper_script_dir = "$root_build_dir/bin"
-    _bundle_wrapper_script_path = "$_bundle_wrapper_script_dir/$target_name"
+    _bundle_wrapper_script_path = "$_bundle_wrapper_script_dir/$_target_name"
 
-    action_with_pydeps("${target_name}__wrapper_script") {
+    action_with_pydeps("${_target_name}__wrapper_script") {
       script = "//build/android/gyp/create_bundle_wrapper_script.py"
       inputs = [
         _base_module_build_config,
@@ -4746,10 +4781,10 @@
       }
     }
 
-    group(target_name) {
+    group(_target_name) {
       public_deps = [
-        ":${target_name}__bundle",
-        ":${target_name}__wrapper_script",
+        ":${_target_name}__bundle",
+        ":${_target_name}__wrapper_script",
       ]
       if (defined(_size_info_target)) {
         public_deps += [ ":$_size_info_target" ]
@@ -4757,7 +4792,7 @@
     }
 
     _apks_path = "$root_build_dir/apks/$_bundle_name.apks"
-    action_with_pydeps("${target_name}_apks") {
+    action_with_pydeps("${_target_name}_apks") {
       script = "//build/android/gyp/create_app_bundle_apks.py"
       inputs = [
         _bundle_path,
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 1822d835..7ee5ea8 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1535,6 +1535,15 @@
           # TODO(https://crbug.com/995993): Clean up and enable.
           "-Wno-implicit-fallthrough",
         ]
+        if (llvm_force_head_revision) {
+          cflags += [
+            # TODO(https://crbug.com/1016947) Clean up, enable.
+            "-Wno-bitwise-conditional-parentheses",
+
+            # TODO(https://crbug.com/1016945) Clean up, enable.
+            "-Wno-builtin-assume-aligned-alignment",
+          ]
+        }
       }
     }
   }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 88ed6b8..77ef9a76 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8898717639165326288
\ No newline at end of file
+8898687329473571088
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index f732be2..3116e43 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8898719885752503984
\ No newline at end of file
+8898687948909005216
\ No newline at end of file
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 5f08a28..4b3cce5 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -282,9 +282,10 @@
     "//chrome/browser/android/thin_webview:java",
     "//chrome/browser/download/android:java",
     "//chrome/browser/image_fetcher:java",
+    "//chrome/browser/share/android:java_resources",
     "//chrome/browser/ui/android/styles:java",
     "//chrome/browser/ui/android/widget:java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//chrome/lib/lifecycle/public/android:java",
     "//components/autofill/android:autofill_java",
     "//components/autofill_assistant/browser:proto_java",
@@ -534,7 +535,7 @@
     "//chrome/android/public/profiles:jni_headers",
     "//chrome/browser/image_fetcher:jni_headers",
     "//chrome/browser/touch_to_fill/android:jni_headers",
-    "//chrome/browser/util/android:jni_headers",
+    "//chrome/browser/util:jni_headers",
   ]
 }
 
@@ -804,7 +805,7 @@
     "//chrome/browser/ui/android/styles:java",
     "//chrome/browser/ui/android/widget:java",
     "//chrome/browser/ui/android/widget:test_support_java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//chrome/test/android:chrome_java_test_support",
     "//chrome/test/android/test_trusted_web_activity:test_trusted_web_activity_java",
     "//components/autofill/android:autofill_java",
@@ -960,7 +961,7 @@
     "//chrome/android:app_hooks_java",
     "//chrome/android:chrome_java",
     "//chrome/android/features/vr:java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//chrome/test/android:chrome_java_test_support",
     "//components/policy/android:policy_java",
     "//content/public/android:content_java",
diff --git a/chrome/android/chrome_common_shared_library.gni b/chrome/android/chrome_common_shared_library.gni
index 783f3fd..cbede69 100644
--- a/chrome/android/chrome_common_shared_library.gni
+++ b/chrome/android/chrome_common_shared_library.gni
@@ -45,8 +45,8 @@
   generate_linker_version_script(_linker_script_target) {
     linker_script = _linker_script
     export_java_symbols = _export_java_symbols
+    export_feature_registrations = true
     if (_generate_partitions) {
-      export_feature_registrations = true
       export_symbol_whitelist_files = []
       foreach(_module_desc, invoker.module_descs) {
         if (defined(_module_desc.native_entrypoints)) {
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 9233b76a..eab4985 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -36,7 +36,7 @@
     "//chrome/android/public/profiles:java",
     "//chrome/browser/image_fetcher:java",
     "//chrome/browser/ui/android/widget:java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//components/policy/android:policy_java",
     "//components/signin/core/browser/android:java",
     "//components/url_formatter/android:url_formatter_java",
diff --git a/chrome/android/features/keyboard_accessory/internal/BUILD.gn b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
index 761c8e9..ef72f7a7 100644
--- a/chrome/android/features/keyboard_accessory/internal/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
@@ -17,7 +17,7 @@
     "//chrome/android/features/keyboard_accessory/public:public_java",
     "//chrome/android/public/profiles:java",
     "//chrome/browser/ui/android/widget:java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//components/autofill/android:autofill_java",
     "//components/feature_engagement/public:public_java",
     "//content/public/android:content_java",
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index d761d60..617590d 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -162,7 +162,7 @@
     "//chrome/app:java_strings_grd",
     "//chrome/browser/ui/android/styles:java",
     "//chrome/browser/ui/android/widget:java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//chrome/lib/lifecycle/public/android:java",
     "//components/embedder_support/android:web_contents_delegate_java",
     "//components/feature_engagement:feature_engagement_java",
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMultiWindowTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMultiWindowTest.java
new file mode 100644
index 0000000..7496849
--- /dev/null
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMultiWindowTest.java
@@ -0,0 +1,207 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import static org.chromium.chrome.browser.multiwindow.MultiWindowTestHelper.moveActivityToFront;
+import static org.chromium.chrome.browser.multiwindow.MultiWindowTestHelper.waitForSecondChromeTabbedActivity;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.contrib.RecyclerViewActions;
+import android.support.test.filters.MediumTest;
+import android.support.v7.widget.RecyclerView;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
+import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.compositor.layouts.Layout;
+import org.chromium.chrome.browser.toolbar.IncognitoToggleTabLayout;
+import org.chromium.chrome.browser.util.FeatureUtilities;
+import org.chromium.chrome.features.start_surface.StartSurfaceLayout;
+import org.chromium.chrome.tab_ui.R;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.chrome.test.util.MenuUtils;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.UiRestriction;
+
+/** Tests for Multi-window related behavior in grid tab switcher. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+// clang-format off
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+                ChromeSwitches.DISABLE_TAB_MERGING_FOR_TESTING})
+@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
+@MinAndroidSdkLevel(Build.VERSION_CODES.N)
+public class TabSwitcherMultiWindowTest {
+    // clang-format on
+    @Rule
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
+    @Rule
+    public TestRule mProcessor = new Features.InstrumentationProcessor();
+
+    @Before
+    public void setUp() {
+        FeatureUtilities.setGridTabSwitcherEnabledForTesting(true);
+        mActivityTestRule.startMainActivityFromLauncher();
+        Layout layout = mActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
+        assertTrue(layout instanceof StartSurfaceLayout);
+        CriteriaHelper.pollUiThread(Criteria.equals(true,
+                mActivityTestRule.getActivity()
+                        .getTabModelSelector()
+                        .getTabModelFilterProvider()
+                        .getCurrentTabModelFilter()::isTabModelRestored));
+    }
+
+    @Test
+    @MediumTest
+    @TargetApi(Build.VERSION_CODES.N)
+    @DisabledTest(message = "crbug.com/1017141 This test fails deterministically on Nexus 5X")
+    public void testMoveTabsAcrossWindow_GTS_WithoutGroup() throws InterruptedException {
+        final ChromeTabbedActivity cta1 = mActivityTestRule.getActivity();
+        // Initially, we have 4 normal tabs and 3 incognito tabs in cta1.
+        initializeTabModel(cta1, false, 4);
+        initializeTabModel(cta1, true, 3);
+        verifyTabModelTabCount(cta1, 4, 3);
+
+        // Enter tab switcher in cta1 in incognito mode.
+        enterTabSwitcher(cta1);
+        assertTrue(cta1.getTabModelSelector().getCurrentModel().isIncognito());
+
+        // Before move, there are 3 incognito tabs in cta1.
+        RecyclerView recyclerView1 = cta1.findViewById(R.id.tab_list_view);
+        CriteriaHelper.pollUiThread(Criteria.equals(3, recyclerView1::getChildCount));
+
+        // Move 2 incognito tabs to cta2.
+        enterFirstTabFromTabSwitcher(cta1);
+        moveTabsToOtherWindow(cta1, 2);
+
+        // After move, there are 1 incognito tab in cta1 and 2 incognito tabs in cta2.
+        final ChromeTabbedActivity cta2 = waitForSecondChromeTabbedActivity();
+        verifyTabModelTabCount(cta1, 4, 1);
+        verifyTabModelTabCount(cta2, 0, 2);
+        enterTabSwitcher(cta1);
+        CriteriaHelper.pollUiThread(Criteria.equals(1, recyclerView1::getChildCount));
+
+        // Enter tab switcher in cta2.
+        moveActivityToFront(cta2);
+        enterTabSwitcher(cta2);
+
+        // There should be two incognito tabs in tab switcher in cta2.
+        RecyclerView recyclerView2 = cta2.findViewById(R.id.tab_list_view);
+        CriteriaHelper.pollUiThread(Criteria.equals(2, recyclerView2::getChildCount));
+
+        // Move 1 incognito tab back to cta1.
+        enterFirstTabFromTabSwitcher(cta2);
+        moveTabsToOtherWindow(cta2, 1);
+
+        // After move, there are 2 incognito tabs in cta1 and 1 incognito tab in cta2.
+        verifyTabModelTabCount(cta1, 4, 2);
+        verifyTabModelTabCount(cta2, 0, 1);
+        enterTabSwitcher(cta2);
+        CriteriaHelper.pollUiThread(Criteria.equals(1, recyclerView2::getChildCount));
+
+        // Enter tab switcher in cta1.
+        moveActivityToFront(cta1);
+
+        // There should be two incognito tabs in tab switcher in cta1.
+        CriteriaHelper.pollUiThread(Criteria.equals(2, recyclerView1::getChildCount));
+
+        // Switch to normal tab list in cta1.
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            IncognitoToggleTabLayout toggleTabLayout =
+                    cta1.findViewById(R.id.incognito_toggle_tabs);
+            ViewGroup toggleButtons = (ViewGroup) toggleTabLayout.getChildAt(0);
+            toggleButtons.getChildAt(0).performClick();
+        });
+        assertEquals(false, cta1.getTabModelSelector().getCurrentModel().isIncognito());
+
+        // Move 3 normal tabs to cta2.
+        enterFirstTabFromTabSwitcher(cta1);
+        moveTabsToOtherWindow(cta1, 3);
+
+        // After move, there are 1 normal tab in cta1 and 3 normal tabs in cta2.
+        verifyTabModelTabCount(cta1, 1, 2);
+        verifyTabModelTabCount(cta2, 3, 1);
+        enterTabSwitcher(cta1);
+        CriteriaHelper.pollUiThread(Criteria.equals(1, recyclerView1::getChildCount));
+
+        // Enter tab switcher in cta2.
+        moveActivityToFront(cta2);
+        enterTabSwitcher(cta2);
+
+        // There should be 3 normal tabs in tab switcher in cta2.
+        CriteriaHelper.pollUiThread(Criteria.equals(3, recyclerView2::getChildCount));
+
+        // Move 2 normal tabs back to cta1.
+        enterFirstTabFromTabSwitcher(cta2);
+        moveTabsToOtherWindow(cta2, 2);
+
+        // After move, there are 3 normal tabs in cta1 and 1 normal tab in cta2.
+        verifyTabModelTabCount(cta1, 3, 2);
+        verifyTabModelTabCount(cta2, 1, 1);
+        enterTabSwitcher(cta2);
+        CriteriaHelper.pollUiThread(Criteria.equals(1, recyclerView2::getChildCount));
+    }
+
+    private void initializeTabModel(ChromeTabbedActivity cta, boolean isIncognito, int tabsCount) {
+        for (int i = 0; i < (isIncognito ? tabsCount : tabsCount - 1); i++) {
+            ChromeTabUtils.newTabFromMenu(
+                    InstrumentationRegistry.getInstrumentation(), cta, isIncognito, true);
+        }
+    }
+
+    private void moveTabsToOtherWindow(ChromeTabbedActivity cta, int number) {
+        for (int i = 0; i < number; i++) {
+            MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(), cta,
+                    org.chromium.chrome.R.id.move_to_other_window_menu_id);
+        }
+    }
+
+    private void enterTabSwitcher(ChromeTabbedActivity cta) {
+        assertFalse(cta.getLayoutManager().overviewVisible());
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { cta.findViewById(R.id.tab_switcher_button).performClick(); });
+        CriteriaHelper.pollUiThread(
+                Criteria.equals(true, () -> cta.getLayoutManager().overviewVisible()));
+    }
+
+    private void enterFirstTabFromTabSwitcher(ChromeTabbedActivity cta) {
+        onView(withId(R.id.tab_list_view))
+                .perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
+        CriteriaHelper.pollInstrumentationThread(() -> !cta.getLayoutManager().overviewVisible());
+    }
+
+    private void verifyTabModelTabCount(
+            ChromeTabbedActivity cta, int normalTabs, int incognitoTabs) {
+        CriteriaHelper.pollUiThread(Criteria.equals(
+                normalTabs, () -> cta.getTabModelSelector().getModel(false).getCount()));
+        CriteriaHelper.pollUiThread(Criteria.equals(
+                incognitoTabs, () -> cta.getTabModelSelector().getModel(true).getCount()));
+    }
+}
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index 10fc3cba..e62ab2c 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -30,6 +30,7 @@
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinderTest.java",
+  "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMultiWindowTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TestRecyclerViewSimpleViewBinder.java",
 ]
 
diff --git a/chrome/android/features/vr/BUILD.gn b/chrome/android/features/vr/BUILD.gn
index feabb434..185a0d6c 100644
--- a/chrome/android/features/vr/BUILD.gn
+++ b/chrome/android/features/vr/BUILD.gn
@@ -119,7 +119,7 @@
     "//base:base_java",
     "//base:jni_java",
     "//chrome/android:chrome_java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//components/policy/android:policy_java",
     "//content/public/android:content_java",
     "//device/vr:java",
diff --git a/chrome/android/features/vr/vr_module.gni b/chrome/android/features/vr/vr_module.gni
index 5548640..87d57532 100644
--- a/chrome/android/features/vr/vr_module.gni
+++ b/chrome/android/features/vr/vr_module.gni
@@ -11,8 +11,6 @@
   name = "vr"
   java_deps = [ "//chrome/android/features/vr:java" ]
   android_manifest = "//chrome/android/features/vr/java/AndroidManifest.xml"
-  if (use_native_partitions) {
-    native_deps = [ "//chrome/browser/vr:vr_ui" ]
-    native_entrypoints = "//chrome/browser/vr/module_exports.lst"
-  }
+  native_deps = [ "//chrome/browser/vr:vr_ui" ]
+  native_entrypoints = "//chrome/browser/vr/module_exports.lst"
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTask.java b/chrome/android/java/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTask.java
index 61612ce..6f07eb8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTask.java
@@ -136,12 +136,12 @@
 
         boolean wasInServiceManagerOnlyMode = isNativeLoadedInServiceManagerOnlyMode();
         mRunningInServiceManagerOnlyMode = supportsServiceManagerOnly();
-        recordMetrics();
 
         final BrowserParts parts = new EmptyBrowserParts() {
             @Override
             public void finishNativeInitialization() {
                 PostTask.postTask(UiThreadTaskTraits.DEFAULT, startWithNativeRunnable);
+                recordMetrics();
             }
             @Override
             public boolean startServiceManagerOnly() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java
index ae3447d5..05fdc5f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/AuthenticatorImpl.java
@@ -40,6 +40,8 @@
             .Callback2<Integer, MakeCredentialAuthenticatorResponse> mMakeCredentialCallback;
     private org.chromium.mojo.bindings.Callbacks
             .Callback2<Integer, GetAssertionAuthenticatorResponse> mGetAssertionCallback;
+    private org.chromium.mojo.bindings.Callbacks
+            .Callback1<Boolean> mIsUserVerifyingPlatformAuthenticatorAvailableCallback;
 
     /**
      * Builds the Authenticator service implementation.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/Fido2ApiHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/Fido2ApiHandler.java
index 2a6670f..c7ba264 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/Fido2ApiHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/Fido2ApiHandler.java
@@ -50,4 +50,7 @@
 
     protected void getAssertion(PublicKeyCredentialRequestOptions options,
             RenderFrameHost frameHost, HandlerResponseCallback callback) {}
+
+    protected void isUserVerifyingPlatformAuthenticatorAvailable(
+            RenderFrameHost frameHost, HandlerResponseCallback callback) {}
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java
index fbd9bb1..f90efbe6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/HandlerResponseCallback.java
@@ -24,6 +24,12 @@
     public void onSignResponse(Integer status, GetAssertionAuthenticatorResponse response){};
 
     /**
+     * Callback for handling response from a request to call
+     * isUserVerifyingPlatformAuthenticatorAvailable.
+     */
+    public void onIsUserVerifyingPlatformAuthenticatorAvailableResponse(boolean isUVPAA){};
+
+    /**
      * Callback for handling any errors from either register or sign requests.
      */
     public void onError(Integer status){};
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index c9f29a1..555a8ab6 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -3989,10 +3989,19 @@
       </message>
 
       <!-- QR Code -->
-      <message name="IDS_QR_CODE_SHARE_ICON_LABEL" desc="Icon label for sharing qith QR Code activity.">
+      <message name="IDS_QR_CODE_SHARE_ICON_LABEL" desc="Icon label for sharing with QR Code activity.">
         QR Code
       </message>
 
+      <message name="IDS_QR_CODE_SHARE_TAB_LABEL" desc="Share tab label for QR Code sharing activity."
+        meaning="Sharing QR code">
+        Share
+      </message>
+
+      <message name="IDS_QR_CODE_SCAN_TAB_LABEL" desc="Scan tab label for QR Code sharing activity.">
+        Scan
+      </message>
+
       <!-- Chime DFM module strings -->
       <message name="IDS_CHIME_MODULE_TITLE" desc="Text shown when the chime module is referenced in install start, success, failure UI (e.g. in IDS_MODULE_INSTALL_START_TEXT, which will expand to 'Installing Google Notifications Platform for Chrome…').">
         Google Notifications Platform
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_QR_CODE_SCAN_TAB_LABEL.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_QR_CODE_SCAN_TAB_LABEL.png.sha1
new file mode 100644
index 0000000..a7b3d1d
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_QR_CODE_SCAN_TAB_LABEL.png.sha1
@@ -0,0 +1 @@
+401b9464b0c27d7269fd41da18be10eacbef5349
\ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_QR_CODE_SHARE_TAB_LABEL.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_QR_CODE_SHARE_TAB_LABEL.png.sha1
new file mode 100644
index 0000000..33429cf7
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_QR_CODE_SHARE_TAB_LABEL.png.sha1
@@ -0,0 +1 @@
+eb8001ee41fe8c85bf9fd3e84e49f9a3747758d9
\ No newline at end of file
diff --git a/chrome/android/modules/chrome_feature_module_tmpl.gni b/chrome/android/modules/chrome_feature_module_tmpl.gni
index b708267..2d29dd92 100644
--- a/chrome/android/modules/chrome_feature_module_tmpl.gni
+++ b/chrome/android/modules/chrome_feature_module_tmpl.gni
@@ -43,14 +43,11 @@
       _module_desc.native_deps != []) {
     _arch = ""
     _toolchain = ""
-    _root_out_dir = root_out_dir
     if (android_64bit_target_cpu && _is_monochrome_or_trichrome) {
       if (_is_64_bit_browser) {
         _arch = "_64"
       } else {
         _toolchain = "($android_secondary_abi_toolchain)"
-        _root_out_dir = get_label_info(":foo($android_secondary_abi_toolchain)",
-                                       "root_out_dir")
       }
     }
     if (_is_monochrome_or_trichrome) {
@@ -61,13 +58,6 @@
     _shared_libraries += [
       "//chrome/android:${_base_target_name}_${_module_desc.name}${_toolchain}",
     ]
-    _native_library = "${_root_out_dir}/${_base_target_name}_partitions/lib${_module_desc.name}.so"
-
-    # Pass the correct library as both the 32 and 64-bit options. Underlying
-    # logic will choose from the correct variable, and supply a dummy library
-    # for the other architecture if required.
-    _loadable_modules_32_bit += [ _native_library ]
-    _loadable_modules_64_bit += [ _native_library ]
   } else {
     not_needed([ "_is_monochrome_or_trichrome" ])
   }
@@ -103,16 +93,17 @@
     package_name = _module_desc.name
 
     # Specify native libraries and placeholders.
-    if (_loadable_modules_32_bit != [] || _loadable_modules_64_bit != []) {
+    if (_loadable_modules_32_bit != [] || _loadable_modules_64_bit != [] ||
+        _shared_libraries != []) {
       # Decision logic: Assign decision variables:
       #   _loadable_modules_to_use: Either |_loadable_modules_64_bit| or
       #     |_loadable_modules_32_bit|.
-      #   _native_is_primary: Whether |_loadable_modules_to_use| should be
-      #     assigned as primary ABI or secondary ABI.
+      #   _native_is_primary: Whether |_loadable_modules_to_use| and/or
+      #     |_shared_libraries| should be assigned as primary ABI or
+      #     secondary ABI.
       #   _native_need_placeholder: Whether a placeholder is needed for the
       #     complementary ABI to the library.
       if (_is_64_bit_browser) {
-        assert(_loadable_modules_64_bit != [])
         not_needed([ "_loadable_modules_32_bit" ])
         _loadable_modules_to_use = _loadable_modules_64_bit
         _native_is_primary =
@@ -121,7 +112,6 @@
             build_apk_secondary_abi && _include_32_bit_webview
         not_needed([ "_include_32_bit_webview" ])
       } else {
-        assert(_loadable_modules_32_bit != [])
         not_needed([
                      "_loadable_modules_64_bit",
                      "_include_32_bit_webview",
@@ -136,11 +126,13 @@
       # Realization logic: Assign libraries and placeholders.
       if (_native_is_primary) {
         loadable_modules = _loadable_modules_to_use
+        shared_libraries = _shared_libraries
         if (_native_need_placeholder) {
           secondary_native_lib_placeholders = [ "libdummy.so" ]
         }
       } else {
         secondary_abi_loadable_modules = _loadable_modules_to_use
+        secondary_abi_shared_libraries = _shared_libraries
         if (_native_need_placeholder) {
           native_lib_placeholders = [ "libdummy.so" ]
         }
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index 226e396..1fdb6d6 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 111
+current_shell_apk_version = 112
diff --git a/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_1_icon.png b/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_1_icon.png
new file mode 100644
index 0000000..7608646
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_1_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_2_icon.png b/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_2_icon.png
new file mode 100644
index 0000000..15e0198
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_2_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_3_icon.png b/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_3_icon.png
new file mode 100644
index 0000000..689a745
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_3_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_4_icon.png b/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_4_icon.png
new file mode 100644
index 0000000..f6a19799
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-hdpi/shortcut_4_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_1_icon.png b/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_1_icon.png
new file mode 100644
index 0000000..6d21cae
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_1_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_2_icon.png b/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_2_icon.png
new file mode 100644
index 0000000..be79d2a
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_2_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_3_icon.png b/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_3_icon.png
new file mode 100644
index 0000000..bf52776
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_3_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_4_icon.png b/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_4_icon.png
new file mode 100644
index 0000000..291d9126
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-mdpi/shortcut_4_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_1_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_1_icon.png
new file mode 100644
index 0000000..922045d
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_1_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_2_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_2_icon.png
new file mode 100644
index 0000000..17a74a23
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_2_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_3_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_3_icon.png
new file mode 100644
index 0000000..aa584c1f
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_3_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_4_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_4_icon.png
new file mode 100644
index 0000000..f9fd43f
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/shortcut_4_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_1_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_1_icon.png
new file mode 100644
index 0000000..646c39c
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_1_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_2_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_2_icon.png
new file mode 100644
index 0000000..43013665
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_2_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_3_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_3_icon.png
new file mode 100644
index 0000000..f71722c
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_3_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_4_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_4_icon.png
new file mode 100644
index 0000000..ca93a2f
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/shortcut_4_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_1_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_1_icon.png
new file mode 100644
index 0000000..109b450c
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_1_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_2_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_2_icon.png
new file mode 100644
index 0000000..518aef67
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_2_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_3_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_3_icon.png
new file mode 100644
index 0000000..60e5d3b
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_3_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_4_icon.png b/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_4_icon.png
new file mode 100644
index 0000000..d99c80b2
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/shortcut_4_icon.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/xml/shortcuts.xml b/chrome/android/webapk/shell_apk/res/xml/shortcuts.xml
index f153bd1..ab013704 100644
--- a/chrome/android/webapk/shell_apk/res/xml/shortcuts.xml
+++ b/chrome/android/webapk/shell_apk/res/xml/shortcuts.xml
@@ -1,3 +1,58 @@
 <shortcuts xmlns:android='http://schemas.android.com/apk/res/android'>
-    {{{shortcuts}}}
+
+  {{#shortcut_1}}
+  <shortcut
+    android:shortcutId='shortcut_1'
+    android:icon='@drawable/shortcut_1_icon'
+    android:shortcutShortLabel='@string/shortcut_1_short_name'
+    android:shortcutLongLabel='@string/shortcut_1_name'>
+      <intent
+          android:action='android.intent.action.MAIN'
+          android:targetPackage='{{{manifest_package}}}'
+          android:targetClass='{{{target_class}}}'
+          android:data='{{{launch_url}}}' />
+  </shortcut>
+  {{/shortcut_1}}
+
+  {{#shortcut_2}}
+  <shortcut
+    android:shortcutId='shortcut_2'
+    android:icon='@drawable/shortcut_2_icon'
+    android:shortcutShortLabel='@string/shortcut_2_short_name'
+    android:shortcutLongLabel='@string/shortcut_2_name'>
+      <intent
+          android:action='android.intent.action.MAIN'
+          android:targetPackage='{{{manifest_package}}}'
+          android:targetClass='{{{target_class}}}'
+          android:data='{{{launch_url}}}' />
+  </shortcut>
+  {{/shortcut_2}}
+
+  {{#shortcut_3}}
+  <shortcut
+    android:shortcutId='shortcut_3'
+    android:icon='@drawable/shortcut_3_icon'
+    android:shortcutShortLabel='@string/shortcut_3_short_name'
+    android:shortcutLongLabel='@string/shortcut_3_name'>
+      <intent
+          android:action='android.intent.action.MAIN'
+          android:targetPackage='{{{manifest_package}}}'
+          android:targetClass='{{{target_class}}}'
+          android:data='{{{launch_url}}}' />
+  </shortcut>
+  {{/shortcut_3}}
+
+  {{#shortcut_4}}
+  <shortcut
+    android:shortcutId='shortcut_4'
+    android:icon='@drawable/shortcut_4_icon'
+    android:shortcutShortLabel='@string/shortcut_4_short_name'
+    android:shortcutLongLabel='@string/shortcut_4_name'>
+      <intent
+          android:action='android.intent.action.MAIN'
+          android:targetPackage='{{{manifest_package}}}'
+          android:targetClass='{{{target_class}}}'
+          android:data='{{{launch_url}}}' />
+  </shortcut>
+  {{/shortcut_4}}
 </shortcuts>
diff --git a/chrome/android/webapk/strings/android_webapk_strings.grd b/chrome/android/webapk/strings/android_webapk_strings.grd
index ba03347..e8339f8 100644
--- a/chrome/android/webapk/strings/android_webapk_strings.grd
+++ b/chrome/android/webapk/strings/android_webapk_strings.grd
@@ -139,6 +139,30 @@
       <message name="IDS_NOTIFICATION_CHANNEL_NAME" desc="The user visible name of the default notification channel of the WebAPK.">
         General
       </message>
+      <message name="IDS_SHORTCUT_1_NAME" desc="The name of the first shortcut." translateable="false">
+        Shortcut 1
+      </message>
+      <message name="IDS_SHORTCUT_1_SHORT_NAME" desc="The short name of the first shortcut." translateable="false">
+        Shortcut 1
+      </message>
+      <message name="IDS_SHORTCUT_2_NAME" desc="The name of the second shortcut." translateable="false">
+        Shortcut 2
+      </message>
+      <message name="IDS_SHORTCUT_2_SHORT_NAME" desc="The short name of the second shortcut." translateable="false">
+        Shortcut 2
+      </message>
+      <message name="IDS_SHORTCUT_3_NAME" desc="The name of the third shortcut." translateable="false">
+        Shortcut 3
+      </message>
+      <message name="IDS_SHORTCUT_3_SHORT_NAME" desc="The short name of the third shortcut." translateable="false">
+        Shortcut 3
+      </message>
+      <message name="IDS_SHORTCUT_4_NAME" desc="The name of the fourth shortcut." translateable="false">
+        Shortcut 4
+      </message>
+      <message name="IDS_SHORTCUT_4_SHORT_NAME" desc="The short name of the fourth shortcut." translateable="false">
+        Shortcut 4
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 743c221..e84f2376 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7457,7 +7457,7 @@
           Cancel
         </message>
         <message name="IDS_EXTERNAL_PROTOCOL_CHECKBOX_TEXT" desc="Checkbox to ask the user whether they would like their decision remembered.">
-          Always open these types of links in the associated app
+          Always open links of this type in the associated app
         </message>
       </if>
       <if expr="chromeos">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index c703f46..1285b6b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2812,6 +2812,11 @@
      flag_descriptions::kOmniboxLocalEntitySuggestionsDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(omnibox::kOmniboxLocalEntitySuggestions)},
 
+    {"omnibox-experimental-suggest-scoring",
+     flag_descriptions::kOmniboxExperimentalSuggestScoringName,
+     flag_descriptions::kOmniboxExperimentalSuggestScoringDescription, kOsAll,
+     FEATURE_VALUE_TYPE(omnibox::kOmniboxExperimentalSuggestScoring)},
+
 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
     {"omnibox-experimental-keyword-mode",
      flag_descriptions::kOmniboxExperimentalKeywordModeName,
diff --git a/chrome/browser/android/chrome_context_util.cc b/chrome/browser/android/chrome_context_util.cc
index f9d6713..893aa94 100644
--- a/chrome/browser/android/chrome_context_util.cc
+++ b/chrome/browser/android/chrome_context_util.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/android/chrome_context_util.h"
 
 #include "base/android/jni_android.h"
-#include "chrome/browser/util/android/jni_headers/ChromeContextUtil_jni.h"
+#include "chrome/browser/util/jni_headers/ChromeContextUtil_jni.h"
 
 namespace chrome {
 namespace android {
diff --git a/chrome/browser/android/preferences/OWNERS b/chrome/browser/android/preferences/OWNERS
index 886fc2e..5b27c2e 100644
--- a/chrome/browser/android/preferences/OWNERS
+++ b/chrome/browser/android/preferences/OWNERS
@@ -1,3 +1,4 @@
 twellington@chromium.org
+chouinard@chromium.org
 
 # COMPONENT: UI>Browser>Mobile>Settings
diff --git a/chrome/browser/android/util/url_utilities.cc b/chrome/browser/android/util/url_utilities.cc
index 5de57b4a..9cb80f1 100644
--- a/chrome/browser/android/util/url_utilities.cc
+++ b/chrome/browser/android/util/url_utilities.cc
@@ -7,7 +7,7 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/strings/string_util.h"
-#include "chrome/browser/util/android/jni_headers/UrlUtilities_jni.h"
+#include "chrome/browser/util/jni_headers/UrlUtilities_jni.h"
 #include "components/google/core/common/google_util.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/captive_portal/captive_portal_tab_reloader_unittest.cc b/chrome/browser/captive_portal/captive_portal_tab_reloader_unittest.cc
index 33df92b..4e207c3f 100644
--- a/chrome/browser/captive_portal/captive_portal_tab_reloader_unittest.cc
+++ b/chrome/browser/captive_portal/captive_portal_tab_reloader_unittest.cc
@@ -538,7 +538,7 @@
   // is created after the TabReloader is notified.
   EXPECT_CALL(tab_reloader(), CheckForCaptivePortal());
   net::SSLInfo ssl_info;
-  ssl_info.SetCertError(net::CERT_STATUS_COMMON_NAME_INVALID);
+  ssl_info.cert_status |= net::CERT_STATUS_COMMON_NAME_INVALID;
   tab_reloader().OnSSLCertError(ssl_info);
   EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
             tab_reloader().state());
diff --git a/chrome/browser/chromeos/arc/arc_optin_uma.cc b/chrome/browser/chromeos/arc/arc_optin_uma.cc
index 140506c..d994f449 100644
--- a/chrome/browser/chromeos/arc/arc_optin_uma.cc
+++ b/chrome/browser/chromeos/arc/arc_optin_uma.cc
@@ -146,6 +146,16 @@
       static_cast<int>(mojom::AccountCheckStatus::CHECK_FAILED) + 1);
 }
 
+void UpdateMainAccountResolutionStatus(
+    const Profile* profile,
+    mojom::MainAccountResolutionStatus status) {
+  DCHECK(mojom::IsKnownEnumValue(status));
+  base::UmaHistogramEnumeration(
+      GetHistogramNameByUserType("ArcAuth.MainAccountResolutionStatus",
+                                 profile),
+      status);
+}
+
 void UpdateSilentAuthCodeUMA(OptInSilentAuthCode state) {
   base::UmaHistogramSparse("Arc.OptInSilentAuthCode", static_cast<int>(state));
 }
diff --git a/chrome/browser/chromeos/arc/arc_optin_uma.h b/chrome/browser/chromeos/arc/arc_optin_uma.h
index 991e32ea..db4281f3 100644
--- a/chrome/browser/chromeos/arc/arc_optin_uma.h
+++ b/chrome/browser/chromeos/arc/arc_optin_uma.h
@@ -248,6 +248,9 @@
 void UpdateAuthTiming(const char* histogram_name, base::TimeDelta elapsed_time);
 void UpdateAuthCheckinAttempts(int32_t num_attempts);
 void UpdateAuthAccountCheckStatus(mojom::AccountCheckStatus status);
+void UpdateMainAccountResolutionStatus(
+    const Profile* profile,
+    mojom::MainAccountResolutionStatus status);
 
 // Outputs the stringified |result| to |os|. This is only for logging purposes.
 std::ostream& operator<<(std::ostream& os, const ProvisioningResult& result);
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service.cc b/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
index 340c263..192ba855 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service.cc
@@ -287,6 +287,19 @@
 
   if (pending_get_arc_accounts_callback_)
     DispatchAccountsInArc(std::move(pending_get_arc_accounts_callback_));
+
+  // Report main account resolution status for provisioned devices.
+  if (!IsArcProvisioned(profile_))
+    return;
+
+  auto* instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->auth(),
+                                               GetMainAccountResolutionStatus);
+  if (!instance)
+    return;
+
+  instance->GetMainAccountResolutionStatus(
+      base::BindOnce(&ArcAuthService::OnMainAccountResolutionStatus,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ArcAuthService::OnConnectionClosed() {
@@ -801,4 +814,9 @@
   instance->GetGoogleAccounts(std::move(callback));
 }
 
+void ArcAuthService::OnMainAccountResolutionStatus(
+    mojom::MainAccountResolutionStatus status) {
+  UpdateMainAccountResolutionStatus(profile_, status);
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service.h b/chrome/browser/chromeos/arc/auth/arc_auth_service.h
index f7f8adb..982f2b8 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service.h
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service.h
@@ -190,6 +190,9 @@
   // Google accounts in ARC.
   void DispatchAccountsInArc(GetGoogleAccountsInArcCallback callback);
 
+  // Response for |mojom::GetMainAccountResolutionStatus|.
+  void OnMainAccountResolutionStatus(mojom::MainAccountResolutionStatus status);
+
   // Non-owning pointers.
   Profile* const profile_;
   signin::IdentityManager* const identity_manager_;
diff --git a/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc b/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
index c23b7becf..3a36ae8 100644
--- a/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_auth_service_browsertest.cc
@@ -152,6 +152,12 @@
     std::move(callback).Run(std::move(accounts));
   }
 
+  void GetMainAccountResolutionStatus(
+      GetMainAccountResolutionStatusCallback callback) override {
+    std::move(callback).Run(
+        mojom::MainAccountResolutionStatus::HASH_CODE_MATCH_SINGLE_ACCOUNT);
+  }
+
   mojom::AccountInfo* account_info() { return account_info_.get(); }
 
   mojom::ArcSignInStatus sign_in_status() const { return status_; }
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 333f83e0..0075785 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -2453,7 +2453,16 @@
 }
 
 void UserSessionManager::MaybeShowReleaseNotesNotification(Profile* profile) {
-  return;
+  if (!base::FeatureList::IsEnabled(features::kReleaseNotes))
+    return;
+  if (!ProfileHelper::IsPrimaryProfile(profile))
+    return;
+  if (!release_notes_notification_) {
+    release_notes_notification_ =
+        std::make_unique<ReleaseNotesNotification>(profile);
+    if (chrome::GetChannel() == version_info::Channel::STABLE)
+      release_notes_notification_->MaybeShowReleaseNotes();
+  }
 }
 
 void UserSessionManager::CreateTokenUtilIfMissing() {
diff --git a/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc b/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc
index 84a9b29..fdf6f57 100644
--- a/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc
+++ b/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc
@@ -77,7 +77,7 @@
 
 // TODO(crbug.com/961017): Fix memory leaks in tests and re-enable on LSAN.
 // Also flaky-failing on slow (debug) bots: https://crbug.com/1017305
-#if defined(LEAK_SANITIZER) || !defined(NDEBUG)
+#if defined(LEAK_SANITIZER) || !defined(NDEBUG) || defined(ADDRESS_SANITIZER)
 #define MAYBE_SaveAsMHTML DISABLED_SaveAsMHTML
 #else
 #define MAYBE_SaveAsMHTML SaveAsMHTML
diff --git a/chrome/browser/extensions/api/proxy/proxy_api_helpers_unittest.cc b/chrome/browser/extensions/api/proxy/proxy_api_helpers_unittest.cc
index 93eee864..979493e 100644
--- a/chrome/browser/extensions/api/proxy/proxy_api_helpers_unittest.cc
+++ b/chrome/browser/extensions/api/proxy/proxy_api_helpers_unittest.cc
@@ -67,9 +67,11 @@
   EXPECT_EQ(kSamplePacScript, out);
 
   // Check that we don't require a mime-type.
+  out.clear();
   ASSERT_TRUE(CreatePACScriptFromDataURL(kSamplePacScriptAsDataUrl2, &out));
   EXPECT_EQ(kSamplePacScript, out);
 
+  out.clear();
   EXPECT_FALSE(CreatePACScriptFromDataURL("http://www.google.com", &out));
 }
 
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index e2e9440..2bca11b4 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -2995,4 +2995,57 @@
       0);
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, AppCacheRequests) {
+  embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
+  ASSERT_TRUE(StartEmbeddedTestServer());
+
+  GURL main_url = embedded_test_server()->GetURL(
+      "/appcache/simple_page_with_manifest.html");
+
+  base::string16 expected_title = base::ASCIIToUTF16("AppCache updated");
+
+  // Load the main page first to make sure it is cached. After the first
+  // navigation, load the extension, then navigate again.
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
+  content::TitleWatcher title_watcher(
+      browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
+  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(R"({
+        "name": "Web Request Appcache Test",
+        "manifest_version": 2,
+        "version": "0.1",
+        "background": { "scripts": ["background.js"] },
+        "permissions": ["<all_urls>", "webRequest"]
+      })");
+  test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), R"(
+        window.numMainResourceRequests = 0;
+        window.numSubresourceRequests = 0;
+        chrome.webRequest.onBeforeRequest.addListener(function(details) {
+          if (details.url.includes('logo.png'))
+            window.numSubresourceRequests++;
+          else if (details.url.includes('simple_page_with_manifest.html'))
+            window.numMainResourceRequests++;
+        }, {urls: ['<all_urls>']}, []);
+
+        chrome.test.sendMessage('ready');
+      )");
+
+  ExtensionTestMessageListener listener("ready", false);
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+  EXPECT_TRUE(listener.WaitUntilSatisfied());
+
+  // This navigation should go through appcache.
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
+
+  EXPECT_EQ(GetCountFromBackgroundPage(extension, profile(),
+                                       "window.numSubresourceRequests"),
+            1);
+  EXPECT_EQ(GetCountFromBackgroundPage(extension, profile(),
+                                       "window.numMainResourceRequests"),
+            1);
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_management.cc b/chrome/browser/extensions/extension_management.cc
index ea14707..3e74afdc 100644
--- a/chrome/browser/extensions/extension_management.cc
+++ b/chrome/browser/extensions/extension_management.cc
@@ -72,6 +72,8 @@
   pref_change_registrar_.Add(pref_names::kAllowedTypes, pref_change_callback);
   pref_change_registrar_.Add(pref_names::kExtensionManagement,
                              pref_change_callback);
+  pref_change_registrar_.Add(prefs::kCloudExtensionRequestEnabled,
+                             pref_change_callback);
 #if !defined(OS_CHROMEOS)
   pref_change_registrar_.Add(prefs::kCloudReportingEnabled,
                              pref_change_callback);
@@ -357,6 +359,8 @@
           LoadPreference(pref_names::kExtensionManagement,
                          true,
                          base::Value::Type::DICTIONARY));
+  const base::Value* extension_request_pref = LoadPreference(
+      prefs::kCloudExtensionRequestEnabled, false, base::Value::Type::BOOLEAN);
 
   // Reset all settings.
   global_settings_.reset(new internal::GlobalSettings());
@@ -365,8 +369,9 @@
 
   // Parse default settings.
   const base::Value wildcard("*");
-  if (denied_list_pref &&
-      denied_list_pref->Find(wildcard) != denied_list_pref->end()) {
+  if ((denied_list_pref &&
+       denied_list_pref->Find(wildcard) != denied_list_pref->end()) ||
+      (extension_request_pref && extension_request_pref->GetBool())) {
     default_settings_->installation_mode = INSTALLATION_BLOCKED;
   }
 
diff --git a/chrome/browser/extensions/extension_management_unittest.cc b/chrome/browser/extensions/extension_management_unittest.cc
index 0f586cb..cb7258a4 100644
--- a/chrome/browser/extensions/extension_management_unittest.cc
+++ b/chrome/browser/extensions/extension_management_unittest.cc
@@ -1005,6 +1005,25 @@
 }
 #endif
 
+TEST_F(ExtensionManagementServiceTest,
+       ExtensionsAreBlockedByDefaultForExtensionRequest) {
+  // When extension request policy is set to true, all extensions are blocked by
+  // default.
+  SetPref(true, prefs::kCloudExtensionRequestEnabled,
+          std::make_unique<base::Value>(true));
+  EXPECT_TRUE(extension_management_->BlacklistedByDefault());
+  EXPECT_EQ(ExtensionManagement::INSTALLATION_BLOCKED,
+            GetInstallationModeById(kTargetExtension));
+  // However, it will be overridden by ExtensionSettings
+  SetExampleDictPref(R"({
+    "*": {
+      "installation_mode": "removed",
+    }
+  })");
+  EXPECT_EQ(ExtensionManagement::INSTALLATION_REMOVED,
+            GetInstallationModeById(kTargetExtension));
+}
+
 // Tests the flag value indicating that extensions are blacklisted by default.
 TEST_F(ExtensionAdminPolicyTest, BlacklistedByDefault) {
   EXPECT_FALSE(BlacklistedByDefault(NULL));
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 480bcc01..32d6940 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2696,6 +2696,11 @@
     "expiry_milestone": 80
   },
   {
+    "name": "omnibox-experimental-suggest-scoring",
+    "owners": [ "jdonnelly", "chrome-omnibox-team@google.com" ],
+    "expiry_milestone": 85
+  },
+  {
     "name": "omnibox-group-suggestions-by-search-vs-url",
     "owners": [ "krb", "chrome-omnibox-team@google.com" ],
     "expiry_milestone": 78
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 4c1c883..244858b 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1404,6 +1404,13 @@
 const char kOmniboxDisableInstantExtendedLimitDescription[] =
     "Effectively doubles the max number of Google-provided search suggestions "
     "on Android by disabling the 'Instant Extended' check.";
+
+const char kOmniboxExperimentalSuggestScoringName[] =
+    "Omnibox Experimental Suggest Scoring";
+const char kOmniboxExperimentalSuggestScoringDescription[] =
+    "Enables an experimental scoring mode for suggestions when Google is the "
+    "default search engine.";
+
 const char kOmniboxGroupSuggestionsBySearchVsUrlName[] =
     "Omnibox Group Suggestions By Search vs URL";
 const char kOmniboxGroupSuggestionsBySearchVsUrlDescription[] =
@@ -2959,7 +2966,7 @@
     "Omnibox Experimental Keyword Mode";
 const char kOmniboxExperimentalKeywordModeDescription[] =
     "Enables various experimental features related to keyword mode, its "
-    "suggestions and layout";
+    "suggestions and layout.";
 
 const char kOmniboxPedalSuggestionsName[] = "Omnibox Pedal suggestions";
 const char kOmniboxPedalSuggestionsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 1f29cee..f4514d4 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -846,6 +846,9 @@
 extern const char kOmniboxDisplayTitleForCurrentUrlName[];
 extern const char kOmniboxDisplayTitleForCurrentUrlDescription[];
 
+extern const char kOmniboxExperimentalSuggestScoringName[];
+extern const char kOmniboxExperimentalSuggestScoringDescription[];
+
 extern const char kOmniboxGroupSuggestionsBySearchVsUrlName[];
 extern const char kOmniboxGroupSuggestionsBySearchVsUrlDescription[];
 
diff --git a/chrome/browser/image_fetcher/BUILD.gn b/chrome/browser/image_fetcher/BUILD.gn
index 9aabc0f..bf16ee4 100644
--- a/chrome/browser/image_fetcher/BUILD.gn
+++ b/chrome/browser/image_fetcher/BUILD.gn
@@ -9,7 +9,7 @@
     "//base:base_java",
     "//base:jni_java",
     "//chrome/android/public/profiles:java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//content/public/android:content_java",
     "//third_party/gif_player:gif_player_java",
   ]
diff --git a/chrome/browser/importer/in_process_importer_bridge.cc b/chrome/browser/importer/in_process_importer_bridge.cc
index 28f9e9a..bf1731f 100644
--- a/chrome/browser/importer/in_process_importer_bridge.cc
+++ b/chrome/browser/importer/in_process_importer_bridge.cc
@@ -65,26 +65,16 @@
 
 // FirefoxURLParameterFilter is used to remove parameter mentioning Firefox from
 // the search URL when importing search engines.
-class FirefoxURLParameterFilter : public TemplateURLParser::ParameterFilter {
- public:
-  FirefoxURLParameterFilter() {}
-  ~FirefoxURLParameterFilter() override {}
-
-  // TemplateURLParser::ParameterFilter method.
-  bool KeepParameter(const std::string& key,
-                     const std::string& value) override {
-    std::string low_value = base::ToLowerASCII(value);
-    if (low_value.find("mozilla") != std::string::npos ||
-        low_value.find("firefox") != std::string::npos ||
-        low_value.find("moz:") != std::string::npos) {
-      return false;
-    }
-    return true;
+bool FirefoxURLParameterFilter(const std::string& key,
+                               const std::string& value) {
+  std::string low_value = base::ToLowerASCII(value);
+  if (low_value.find("mozilla") != std::string::npos ||
+      low_value.find("firefox") != std::string::npos ||
+      low_value.find("moz:") != std::string::npos) {
+    return false;
   }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FirefoxURLParameterFilter);
-};
+  return true;
+}
 
 // Attempts to create a TemplateURL from the provided data. |title| is optional.
 // If TemplateURL creation fails, returns null.
@@ -110,15 +100,14 @@
   DCHECK(search_engines);
 
   std::map<std::string, std::unique_ptr<TemplateURL>> search_engine_for_url;
-  FirefoxURLParameterFilter param_filter;
   // The first XML file represents the default search engine in Firefox 3, so we
   // need to keep it on top of the list.
   auto default_turl = search_engine_for_url.end();
   for (auto xml_iter = xml_data.begin(); xml_iter != xml_data.end();
        ++xml_iter) {
-    std::unique_ptr<TemplateURL> template_url =
-        TemplateURLParser::Parse(UIThreadSearchTermsData(), xml_iter->data(),
-                                 xml_iter->length(), &param_filter);
+    std::unique_ptr<TemplateURL> template_url = TemplateURLParser::Parse(
+        UIThreadSearchTermsData(), xml_iter->data(), xml_iter->length(),
+        base::BindRepeating(&FirefoxURLParameterFilter));
     if (template_url) {
       auto iter = search_engine_for_url.find(template_url->url());
       if (iter == search_engine_for_url.end()) {
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h
index ce099fe..c9c0e96f 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.h
@@ -12,7 +12,6 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/engagement/site_engagement_details.mojom.h"
 #include "chrome/browser/lookalikes/lookalike_url_interstitial_page.h"
-#include "chrome/browser/reputation/reputation_service.h"
 #include "components/url_formatter/url_formatter.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
diff --git a/chrome/browser/platform_util_android.cc b/chrome/browser/platform_util_android.cc
index ddf3da4..aae50e7d 100644
--- a/chrome/browser/platform_util_android.cc
+++ b/chrome/browser/platform_util_android.cc
@@ -8,7 +8,7 @@
 #include "base/android/jni_string.h"
 #include "base/logging.h"
 #include "chrome/browser/platform_util.h"
-#include "chrome/browser/util/android/jni_headers/PlatformUtil_jni.h"
+#include "chrome/browser/util/jni_headers/PlatformUtil_jni.h"
 #include "ui/android/view_android.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index b33706fd..3eb28f68 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -63,7 +63,6 @@
 #include "content/public/common/content_switches.h"
 #include "extensions/buildflags/buildflags.h"
 #include "net/ssl/client_cert_store.h"
-#include "net/url_request/url_request.h"
 #include "services/network/ignore_errors_cert_verifier.h"
 #include "services/network/network_service.h"
 #include "services/network/public/cpp/features.h"
@@ -340,6 +339,12 @@
 bool ProfileIOData::IsHandledProtocol(const std::string& scheme) {
   DCHECK_EQ(scheme, base::ToLowerASCII(scheme));
   static const char* const kProtocolList[] = {
+    url::kHttpScheme,
+    url::kHttpsScheme,
+#if BUILDFLAG(ENABLE_WEBSOCKETS)
+    url::kWsScheme,
+    url::kWssScheme,
+#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
     url::kFileScheme,
     content::kChromeDevToolsScheme,
     dom_distiller::kDomDistillerScheme,
@@ -362,11 +367,11 @@
     url::kFileSystemScheme,
     chrome::kChromeSearchScheme,
   };
-  for (size_t i = 0; i < base::size(kProtocolList); ++i) {
-    if (scheme == kProtocolList[i])
+  for (const char* supported_protocol : kProtocolList) {
+    if (scheme == supported_protocol)
       return true;
   }
-  return net::URLRequest::IsHandledProtocol(scheme);
+  return false;
 }
 
 // static
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index 47f0790..3426c9a 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -153,6 +153,24 @@
   max-width: 584px;
 }
 
+.google-g-icon {
+  background-image:
+      url(../../../../ui/webui/resources/images/200-logo_googleg.png);
+  background-position: center;
+  background-repeat: no-repeat;
+  background-size: 14px;
+  bottom: 0;
+  left: 16px;
+  position: absolute;
+  top: 0;
+  width: 24px;
+}
+
+[dir=rtl] .google-g-icon {
+  left: auto;
+  right: 16px;
+}
+
 #fakebox {
   cursor: text;
 }
@@ -195,7 +213,7 @@
 
 #realbox-matches {
   background: white;
-  border-radius: 8px;
+  border-radius: 16px;
   display: none;
   left: 0;
   overflow: hidden;
@@ -245,6 +263,7 @@
 .search-icon {
   -webkit-mask-position: center;
   -webkit-mask-repeat: no-repeat;
+  -webkit-mask-size: 16px;
   background-color: rgb(117, 117, 117);
   bottom: 0;
   left: 16px;
@@ -255,13 +274,11 @@
 
 .clock-icon {
   -webkit-mask-image: url(icons/clock.svg);
-  -webkit-mask-size: 16px;
 }
 
 .search-icon {
   -webkit-mask-image:
       url(../../../../ui/webui/resources/images/icon_search.svg);
-  -webkit-mask-size: 20px;
 }
 
 html[dir=rtl] :-webkit-any(.clock-icon, .search-icon) {
@@ -404,7 +421,7 @@
   right: auto;
 }
 
-#realbox-input-wrapper :-webkit-any(.search-icon, .microphone-icon) {
+#realbox-input-wrapper :-webkit-any(.google-g-icon, .microphone-icon) {
   z-index: 3;
 }
 
diff --git a/chrome/browser/resources/local_ntp/local_ntp.html b/chrome/browser/resources/local_ntp/local_ntp.html
index d5522ba..8fbc6a7 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.html
+++ b/chrome/browser/resources/local_ntp/local_ntp.html
@@ -78,7 +78,7 @@
 
     <div id="realbox-container" $i18n{hiddenIfRealboxDisabled}>
       <div id="realbox-input-wrapper">
-        <div class="search-icon"></div>
+        <div class="google-g-icon"></div>
         <input id="realbox" type="search" autocomplete="off" spellcheck="false"
             autofocus>
         <button id="realbox-microphone" class="microphone-icon" hidden></button>
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index 97364ef..18a23da9 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -1496,10 +1496,13 @@
       const icon = document.createElement('button');
       icon.title = configData.translatedStrings.removeSuggestion;
       icon.classList.add(CLASSES.REMOVE_ICON);
+      icon.onmousedown = e => {
+        e.preventDefault();  // Stops default browser action (focus)
+      };
       icon.onclick = e => {
         matchElBeingDeleted = matchEl;
         window.chrome.embeddedSearch.searchBox.deleteAutocompleteMatch(i);
-        e.preventDefault();
+        e.preventDefault();  // Stops default browser action (navigation)
       };
 
       const remove = document.createElement('div');
diff --git a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js
index 4322f79..c4b2d4f 100644
--- a/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js
+++ b/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js
@@ -42,7 +42,6 @@
     /** @private */
     showOnLeft_: {
       type: Boolean,
-      computed: 'computeShowOnLeft_(isPrintPreview)',
       reflectToAttribute: true,
     },
   },
@@ -89,15 +88,6 @@
   },
 
   /**
-   * @return {boolean} Whether to show the zoom toolbar on the left side of the
-   *     viewport.
-   * @private
-   */
-  computeShowOnLeft_: function() {
-    return isRTL() !== this.isPrintPreview;
-  },
-
-  /**
    * Change button tooltips to match any changes to localized strings.
    * @param {!{tooltipFitToPage: string,
    *           tooltipFitToWidth: string,
@@ -109,6 +99,7 @@
         [strings.tooltipFitToPage, strings.tooltipFitToWidth];
     this.$['zoom-in-button'].tooltips = [strings.tooltipZoomIn];
     this.$['zoom-out-button'].tooltips = [strings.tooltipZoomOut];
+    this.showOnLeft_ = isRTL() !== this.isPrintPreview;
   },
 
   /** Handle clicks of the fit-button. */
diff --git a/chrome/browser/resources/tab_strip/alert_indicator.js b/chrome/browser/resources/tab_strip/alert_indicator.js
index cff53942..b9a1558 100644
--- a/chrome/browser/resources/tab_strip/alert_indicator.js
+++ b/chrome/browser/resources/tab_strip/alert_indicator.js
@@ -2,12 +2,52 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import './strings.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+
 import {CustomElement} from './custom_element.js';
 import {TabAlertState} from './tabs_api_proxy.js';
 
 /** @const {string} */
 const MAX_WIDTH = '16px';
 
+/**
+ * @param {!TabAlertState} alertState
+ * @return {string}
+ */
+function getAriaLabel(alertState) {
+  // The existing labels for alert states currently expects to format itself
+  // using the title of the tab (eg. "Website - Audio is playing"). The WebUI
+  // tab strip will provide the title of the tab elsewhere outside of this
+  // element, so just provide an empty string as the title here. This also
+  // allows for multiple labels for the same title (eg. "Website - Audio is
+  // playing - VR is presenting").
+  switch (alertState) {
+    case TabAlertState.MEDIA_RECORDING:
+      return loadTimeData.getStringF('mediaRecording', '');
+    case TabAlertState.TAB_CAPTURING:
+      return loadTimeData.getStringF('tabCapturing', '');
+    case TabAlertState.AUDIO_PLAYING:
+      return loadTimeData.getStringF('audioPlaying', '');
+    case TabAlertState.AUDIO_MUTING:
+      return loadTimeData.getStringF('audioMuting', '');
+    case TabAlertState.BLUETOOTH_CONNECTED:
+      return loadTimeData.getStringF('bluetoothConnected', '');
+    case TabAlertState.USB_CONNECTED:
+      return loadTimeData.getStringF('usbConnected', '');
+    case TabAlertState.SERIAL_CONNECTED:
+      return loadTimeData.getStringF('seriaLConnected', '');
+    case TabAlertState.PIP_PLAYING:
+      return loadTimeData.getStringF('pipPlaying', '');
+    case TabAlertState.DESKTOP_CAPTURING:
+      return loadTimeData.getStringF('desktopCapturing', '');
+    case TabAlertState.VR_PRESENTING_IN_HEADSET:
+      return loadTimeData.getStringF('vrPresenting', '');
+    default:
+      return '';
+  }
+}
+
 export class AlertIndicatorElement extends CustomElement {
   static get template() {
     return `{__html_template__}`;
@@ -50,6 +90,7 @@
   /** @param {!TabAlertState} alertState */
   set alertState(alertState) {
     this.setAttribute('alert-state_', alertState);
+    this.setAttribute('aria-label', getAriaLabel(alertState));
     this.alertState_ = alertState;
   }
 
diff --git a/chrome/browser/resources/tab_strip/tab.html b/chrome/browser/resources/tab_strip/tab.html
index 08627d7..30c8de8 100644
--- a/chrome/browser/resources/tab_strip/tab.html
+++ b/chrome/browser/resources/tab_strip/tab.html
@@ -9,7 +9,7 @@
     width: var(--tabstrip-tab-width);
   }
 
-  #dragImage {
+  #tab {
     background: var(--tabstrip-tab-background-color);
     border-radius: var(--tabstrip-tab-border-radius);
     box-shadow: 0 0 0 1px var(--tabstrip-tab-separator-color);
@@ -21,7 +21,7 @@
     width: 100%;
   }
 
-  :host([active]) #dragImage {
+  :host([active]) #tab {
     box-shadow: 0 0 0 2px var(--tabstrip-tab-active-border-color);
     outline: none;
   }
@@ -258,7 +258,7 @@
 
   /* When being dragged, the contents of the drag image needs to be off-screen
    * with nothing else on top or below obscuring it. */
-  :host([dragging_]) #dragImage {
+  :host([dragging_]) #tab {
     box-shadow: none;
     position: absolute;
     top: -999px;
@@ -267,16 +267,17 @@
 
 <div id="dragPlaceholder"></div>
 
-<div id="dragImage">
+<div id="tab" role="tab" tabindex="0"
+    aria-labelledby="titleText alertIndicators">
   <header id="title">
-    <div id="faviconContainer">
+    <div id="faviconContainer" aria-hidden="true">
       <div id="progressSpinner"></div>
       <div id="favicon"></div>
       <div id="crashedIcon"></div>
       <div id="blocked"></div>
     </div>
     <h2 id="titleText"></h2>
-    <tabstrip-alert-indicators></tabstrip-alert-indicators>
+    <tabstrip-alert-indicators id="alertIndicators"></tabstrip-alert-indicators>
     <button id="close">
       <span id="closeIcon"></span>
     </button>
diff --git a/chrome/browser/resources/tab_strip/tab.js b/chrome/browser/resources/tab_strip/tab.js
index a9e4bc0..cafce9b 100644
--- a/chrome/browser/resources/tab_strip/tab.js
+++ b/chrome/browser/resources/tab_strip/tab.js
@@ -2,8 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import './strings.m.js';
+
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {getFavicon} from 'chrome://resources/js/icon.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {AlertIndicatorsElement} from './alert_indicators.js';
 import {CustomElement} from './custom_element.js';
@@ -13,6 +16,24 @@
 
 export const DEFAULT_ANIMATION_DURATION = 125;
 
+/**
+ * @param {!TabData} tab
+ * @return {string}
+ */
+function getAccessibleTitle(tab) {
+  const tabTitle = tab.title;
+
+  if (tab.crashed) {
+    return loadTimeData.getStringF('tabCrashed', tabTitle);
+  }
+
+  if (tab.networkState === TabNetworkState.ERROR) {
+    return loadTimeData.getStringF('tabNetworkError', tabTitle);
+  }
+
+  return tabTitle;
+}
+
 export class TabElement extends CustomElement {
   static get template() {
     return `{__html_template__}`;
@@ -31,11 +52,12 @@
     /** @private {!HTMLElement} */
     this.closeButtonEl_ =
         /** @type {!HTMLElement} */ (this.shadowRoot.querySelector('#close'));
+    this.closeButtonEl_.setAttribute(
+        'aria-label', loadTimeData.getString('closeTab'));
 
     /** @private {!HTMLElement} */
-    this.dragImage_ =
-        /** @type {!HTMLElement} */ (
-            this.shadowRoot.querySelector('#dragImage'));
+    this.tabEl_ =
+        /** @type {!HTMLElement} */ (this.shadowRoot.querySelector('#tab'));
 
     /** @private {!HTMLElement} */
     this.faviconEl_ =
@@ -63,8 +85,8 @@
     this.titleTextEl_ = /** @type {!HTMLElement} */ (
         this.shadowRoot.querySelector('#titleText'));
 
-    this.addEventListener('click', this.onClick_.bind(this));
-    this.addEventListener('contextmenu', this.onContextMenu_.bind(this));
+    this.tabEl_.addEventListener('click', this.onClick_.bind(this));
+    this.tabEl_.addEventListener('contextmenu', this.onContextMenu_.bind(this));
     this.closeButtonEl_.addEventListener('click', this.onClose_.bind(this));
   }
 
@@ -77,6 +99,7 @@
   set tab(tab) {
     assert(this.tab_ !== tab);
     this.toggleAttribute('active', tab.active);
+    this.tabEl_.setAttribute('aria-selected', tab.active.toString());
     this.toggleAttribute('hide-icon_', !tab.showIcon);
     this.toggleAttribute(
         'waiting_',
@@ -94,6 +117,7 @@
     if (!this.tab_ || this.tab_.title !== tab.title) {
       this.titleTextEl_.textContent = tab.title;
     }
+    this.titleTextEl_.setAttribute('aria-label', getAccessibleTitle(tab));
 
     if (tab.networkState === TabNetworkState.WAITING ||
         (tab.networkState === TabNetworkState.LOADING &&
@@ -120,11 +144,9 @@
     this.tab_ = Object.freeze(tab);
   }
 
-  /**
-   * @return {!HTMLElement}
-   */
+  /** @return {!HTMLElement} */
   getDragImage() {
-    return this.dragImage_;
+    return this.tabEl_;
   }
 
   /**
@@ -147,7 +169,10 @@
     }
   }
 
-  /** @private */
+  /**
+   * @param {!Event} event
+   * @private
+   */
   onContextMenu_(event) {
     event.preventDefault();
 
diff --git a/chrome/browser/resources/tab_strip/tab_strip.html b/chrome/browser/resources/tab_strip/tab_strip.html
index 20b58a70..08df098 100644
--- a/chrome/browser/resources/tab_strip/tab_strip.html
+++ b/chrome/browser/resources/tab_strip/tab_strip.html
@@ -32,7 +32,7 @@
     </style>
   </head>
   <body>
-    <tabstrip-tab-list></tabstrip-tab-list>
+    <tabstrip-tab-list role="tablist"></tabstrip-tab-list>
     <script src="tab_list.js" type="module"></script>
   </body>
 </html>
diff --git a/chrome/browser/search_engines/template_url_parser_unittest.cc b/chrome/browser/search_engines/template_url_parser_unittest.cc
index 7cfa0e5..41bc92c 100644
--- a/chrome/browser/search_engines/template_url_parser_unittest.cc
+++ b/chrome/browser/search_engines/template_url_parser_unittest.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "components/search_engines/template_url_parser.h"
+
+#include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/path_service.h"
@@ -15,40 +17,16 @@
 
 using base::ASCIIToUTF16;
 
-// ParamFilterImpl ------------------------------------------------------------
-
-// Filters any param which as an occurrence of name_str_ in its name or an
-// occurrence of value_str_ in its value.
-class ParamFilterImpl : public TemplateURLParser::ParameterFilter {
- public:
-  ParamFilterImpl(const std::string& name_str, const std::string& value_str);
-  ~ParamFilterImpl() override;
-
-  bool KeepParameter(const std::string& key, const std::string& value) override;
-
- private:
-  std::string name_str_;
-  std::string value_str_;
-
-  DISALLOW_COPY_AND_ASSIGN(ParamFilterImpl);
-};
-
-ParamFilterImpl::ParamFilterImpl(const std::string& name_str,
-                                 const std::string& value_str)
-    : name_str_(name_str), value_str_(value_str) {}
-
-ParamFilterImpl::~ParamFilterImpl() {
+// Filters any param which as an occurrence of |name_str| in its |key| or an
+// occurrence of |value_str| in its |value|.
+bool TestFilter(const std::string& name_str,
+                const std::string& value_str,
+                const std::string& key,
+                const std::string& value) {
+  return (name_str.empty() || key.find(name_str) == std::string::npos) &&
+         (value_str.empty() || value.find(value_str) == std::string::npos);
 }
 
-bool ParamFilterImpl::KeepParameter(const std::string& key,
-                                    const std::string& value) {
-  return (name_str_.empty() || key.find(name_str_) == std::string::npos) &&
-         (value_str_.empty() || value.find(value_str_) == std::string::npos);
-}
-
-
-// TemplateURLParserTest ------------------------------------------------------
-
 class TemplateURLParserTest : public testing::Test {
  protected:
   TemplateURLParserTest();
@@ -59,7 +37,7 @@
   // Parses the OpenSearch description document at file_name (relative to the
   // data dir). The TemplateURL is placed in |template_url_|.
   void ParseFile(const std::string& file_name,
-                 TemplateURLParser::ParameterFilter* filter);
+                 const TemplateURLParser::ParameterFilter& filter);
 
   // ParseFile parses the results into this template_url.
   std::unique_ptr<TemplateURL> template_url_;
@@ -82,7 +60,7 @@
 
 void TemplateURLParserTest::ParseFile(
     const std::string& file_name,
-    TemplateURLParser::ParameterFilter* filter) {
+    const TemplateURLParser::ParameterFilter& filter) {
   base::FilePath full_path = osdd_dir_.AppendASCII(file_name);
   ASSERT_TRUE(base::PathExists(full_path));
 
@@ -95,22 +73,26 @@
 // Actual tests ---------------------------------------------------------------
 
 TEST_F(TemplateURLParserTest, FailOnBogusURL) {
-  ASSERT_NO_FATAL_FAILURE(ParseFile("bogus.xml", nullptr));
+  ASSERT_NO_FATAL_FAILURE(
+      ParseFile("bogus.xml", TemplateURLParser::ParameterFilter()));
   EXPECT_FALSE(template_url_);
 }
 
 TEST_F(TemplateURLParserTest, PassOnHTTPS) {
-  ASSERT_NO_FATAL_FAILURE(ParseFile("https.xml", nullptr));
+  ASSERT_NO_FATAL_FAILURE(
+      ParseFile("https.xml", TemplateURLParser::ParameterFilter()));
   EXPECT_TRUE(template_url_);
 }
 
 TEST_F(TemplateURLParserTest, FailOnPost) {
-  ASSERT_NO_FATAL_FAILURE(ParseFile("post.xml", nullptr));
+  ASSERT_NO_FATAL_FAILURE(
+      ParseFile("post.xml", TemplateURLParser::ParameterFilter()));
   EXPECT_FALSE(template_url_);
 }
 
 TEST_F(TemplateURLParserTest, TestDictionary) {
-  ASSERT_NO_FATAL_FAILURE(ParseFile("dictionary.xml", nullptr));
+  ASSERT_NO_FATAL_FAILURE(
+      ParseFile("dictionary.xml", TemplateURLParser::ParameterFilter()));
   ASSERT_TRUE(template_url_);
   EXPECT_EQ(ASCIIToUTF16("Dictionary.com"), template_url_->short_name());
   EXPECT_EQ(GURL("http://cache.lexico.com/g/d/favicon.ico"),
@@ -121,7 +103,8 @@
 }
 
 TEST_F(TemplateURLParserTest, TestMSDN) {
-  ASSERT_NO_FATAL_FAILURE(ParseFile("msdn.xml", nullptr));
+  ASSERT_NO_FATAL_FAILURE(
+      ParseFile("msdn.xml", TemplateURLParser::ParameterFilter()));
   ASSERT_TRUE(template_url_);
   EXPECT_EQ(ASCIIToUTF16("Search \" MSDN"), template_url_->short_name());
   EXPECT_EQ(GURL("http://search.msdn.microsoft.com/search/favicon.ico"),
@@ -133,7 +116,8 @@
 }
 
 TEST_F(TemplateURLParserTest, TestWikipedia) {
-  ASSERT_NO_FATAL_FAILURE(ParseFile("wikipedia.xml", nullptr));
+  ASSERT_NO_FATAL_FAILURE(
+      ParseFile("wikipedia.xml", TemplateURLParser::ParameterFilter()));
   ASSERT_TRUE(template_url_);
   EXPECT_EQ(ASCIIToUTF16("Wikipedia (English)"), template_url_->short_name());
   EXPECT_EQ(GURL("http://en.wikipedia.org/favicon.ico"),
@@ -153,14 +137,16 @@
 }
 
 TEST_F(TemplateURLParserTest, NoCrashOnEmptyAttributes) {
-  ASSERT_NO_FATAL_FAILURE(ParseFile("url_with_no_attributes.xml", nullptr));
+  ASSERT_NO_FATAL_FAILURE(ParseFile("url_with_no_attributes.xml",
+                                    TemplateURLParser::ParameterFilter()));
 }
 
 TEST_F(TemplateURLParserTest, TestFirefoxEbay) {
   // This file uses the Parameter extension
   // (see http://www.opensearch.org/Specifications/OpenSearch/Extensions/Parameter/1.0)
-  ParamFilterImpl filter("ebay", "ebay");
-  ASSERT_NO_FATAL_FAILURE(ParseFile("firefox_ebay.xml", &filter));
+  TemplateURLParser::ParameterFilter filter =
+      base::BindRepeating(&TestFilter, "ebay", "ebay");
+  ASSERT_NO_FATAL_FAILURE(ParseFile("firefox_ebay.xml", filter));
   ASSERT_TRUE(template_url_);
   EXPECT_EQ(ASCIIToUTF16("eBay"), template_url_->short_name());
   EXPECT_TRUE(template_url_->url_ref().SupportsReplacement(SearchTermsData()));
@@ -176,8 +162,9 @@
 
 TEST_F(TemplateURLParserTest, TestFirefoxWebster) {
   // This XML file uses a namespace.
-  ParamFilterImpl filter(std::string(), "Mozilla");
-  ASSERT_NO_FATAL_FAILURE(ParseFile("firefox_webster.xml", &filter));
+  TemplateURLParser::ParameterFilter filter =
+      base::BindRepeating(&TestFilter, std::string(), "Mozilla");
+  ASSERT_NO_FATAL_FAILURE(ParseFile("firefox_webster.xml", filter));
   ASSERT_TRUE(template_url_);
   EXPECT_EQ(ASCIIToUTF16("Webster"), template_url_->short_name());
   EXPECT_TRUE(template_url_->url_ref().SupportsReplacement(SearchTermsData()));
@@ -191,8 +178,9 @@
 
 TEST_F(TemplateURLParserTest, TestFirefoxYahoo) {
   // This XML file uses a namespace.
-  ParamFilterImpl filter(std::string(), "Mozilla");
-  ASSERT_NO_FATAL_FAILURE(ParseFile("firefox_yahoo.xml", &filter));
+  TemplateURLParser::ParameterFilter filter =
+      base::BindRepeating(&TestFilter, std::string(), "Mozilla");
+  ASSERT_NO_FATAL_FAILURE(ParseFile("firefox_yahoo.xml", filter));
   ASSERT_TRUE(template_url_);
   EXPECT_EQ(ASCIIToUTF16("Yahoo"), template_url_->short_name());
   EXPECT_TRUE(template_url_->url_ref().SupportsReplacement(SearchTermsData()));
@@ -211,8 +199,9 @@
 // firefox_yahoo.xml, the suggestion method was just changed to POST).
 TEST_F(TemplateURLParserTest, TestPostSuggestion) {
   // This XML file uses a namespace.
-  ParamFilterImpl filter(std::string(), "Mozilla");
-  ASSERT_NO_FATAL_FAILURE(ParseFile("post_suggestion.xml", &filter));
+  TemplateURLParser::ParameterFilter filter =
+      base::BindRepeating(&TestFilter, std::string(), "Mozilla");
+  ASSERT_NO_FATAL_FAILURE(ParseFile("post_suggestion.xml", filter));
   ASSERT_TRUE(template_url_);
   EXPECT_EQ(ASCIIToUTF16("Yahoo"), template_url_->short_name());
   EXPECT_TRUE(template_url_->url_ref().SupportsReplacement(SearchTermsData()));
@@ -227,7 +216,8 @@
 
 // <Alias> tags are parsed and used as keyword for the template URL.
 TEST_F(TemplateURLParserTest, TestKeyword) {
-  ASSERT_NO_FATAL_FAILURE(ParseFile("keyword.xml", nullptr));
+  ASSERT_NO_FATAL_FAILURE(
+      ParseFile("keyword.xml", TemplateURLParser::ParameterFilter()));
   ASSERT_TRUE(template_url_);
   EXPECT_EQ(ASCIIToUTF16("Example"), template_url_->short_name());
   EXPECT_EQ("https://www.example.com/search?q={searchTerms}",
@@ -238,7 +228,8 @@
 // Empty <Alias> tags are ignored and the default keyword is used instead
 // (because empty keywords are not allowed).
 TEST_F(TemplateURLParserTest, TestEmptyKeyword) {
-  ASSERT_NO_FATAL_FAILURE(ParseFile("empty_keyword.xml", nullptr));
+  ASSERT_NO_FATAL_FAILURE(
+      ParseFile("empty_keyword.xml", TemplateURLParser::ParameterFilter()));
   ASSERT_TRUE(template_url_);
   EXPECT_EQ(ASCIIToUTF16("Example"), template_url_->short_name());
   EXPECT_EQ("https://www.example.com/search?q={searchTerms}",
@@ -249,12 +240,8 @@
 // An invalid template URL should not crash the parser.
 // crbug.com/770734
 TEST_F(TemplateURLParserTest, InvalidInput) {
-  struct DumbFilter : TemplateURLParser::ParameterFilter {
-    bool KeepParameter(const std::string& key,
-                       const std::string& value) override {
-      return true;
-    }
-  } filter;
+  TemplateURLParser::ParameterFilter filter = base::BindRepeating(
+      [](const std::string&, const std::string&) -> bool { return true; });
   constexpr char char_data[] = R"(
     <OpenSearchDescription>
     <Url template=")R:RRR?>RRR0" type="application/x-suggestions+json">
@@ -263,5 +250,5 @@
     </OpenSearchDescription>
   )";
   TemplateURLParser::Parse(SearchTermsData(), char_data, base::size(char_data),
-                           &filter);
+                           filter);
 }
diff --git a/chrome/browser/share/android/BUILD.gn b/chrome/browser/share/android/BUILD.gn
new file mode 100644
index 0000000..ba6fd64
--- /dev/null
+++ b/chrome/browser/share/android/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_resources("java_resources") {
+  # TODO(gayane): remove dependency on chrome/android:chrome_app_java_resources
+  # once strings and common styles move out of chrome/android
+  deps = [
+    "//chrome/android:chrome_app_java_resources",
+  ]
+  resource_dirs = [ "java/res" ]
+  custom_package = "org.chromium.chrome.browser.share.qrcode"
+}
diff --git a/chrome/browser/share/android/java/res/layout/qrcode_dialog.xml b/chrome/browser/share/android/java/res/layout/qrcode_dialog.xml
new file mode 100644
index 0000000..3a34924
--- /dev/null
+++ b/chrome/browser/share/android/java/res/layout/qrcode_dialog.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <android.support.design.widget.TabLayout
+        android:id="@+id/tab_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:tabGravity="center"
+        app:tabPaddingEnd="20dp"
+        app:tabPaddingStart="20dp"
+        app:tabIndicatorFullWidth="true"
+        style="@style/TabLayoutStyle" >
+
+        <android.support.design.widget.TabItem
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/qr_code_share_tab_label" />
+
+        <android.support.design.widget.TabItem
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/qr_code_scan_tab_label" />
+    </android.support.design.widget.TabLayout>
+
+    <org.chromium.ui.widget.ChromeImageButton
+        android:id="@+id/close_button"
+        style="@style/ToolbarButton"
+        android:src="@drawable/btn_close"
+        android:contentDescription="@string/close"
+        app:tint="@color/standard_mode_tint" />
+
+    <ImageView
+        android:id="@+id/tab_line"
+        android:layout_width="match_parent"
+        style="@style/TabBarShadow"
+        android:layout_below="@+id/tab_layout"
+        android:importantForAccessibility="no"/>
+
+    <android.support.v4.view.ViewPager
+        android:id="@+id/qrcode_view_pager"
+        android:layout_width="match_parent"
+        android:layout_height="fill_parent"
+        android:layout_below="@id/tab_line"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeCoordinator.java
new file mode 100644
index 0000000..0b527b2
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeCoordinator.java
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.share.qrcode;
+
+import android.content.Context;
+
+/**
+ * Creates the QrCodeDialog.
+ */
+public class QrCodeCoordinator {
+    QrCodeDialog mDialog;
+
+    QrCodeCoordinator(Context context) {
+        mDialog = new QrCodeDialog(context);
+    }
+
+    public void show() {
+        mDialog.show();
+    }
+}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialog.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialog.java
new file mode 100644
index 0000000..654df513
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialog.java
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.share.qrcode;
+
+import android.content.Context;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import org.chromium.chrome.R;
+import org.chromium.ui.widget.ChromeImageButton;
+
+/**
+ * QrCodeDialog is the main view for QR code sharing.
+ */
+public class QrCodeDialog extends AlertDialog {
+    /**
+     * The QrCodeDialog constructor.
+     * @param context The context to use.
+     */
+    public QrCodeDialog(Context context) {
+        super(context, R.style.Theme_Chromium_Fullscreen);
+
+        View dialogView = (View) LayoutInflater.from(context).inflate(
+                org.chromium.chrome.browser.share.qrcode.R.layout.qrcode_dialog, null);
+        setView(dialogView);
+        ChromeImageButton close_button =
+                (ChromeImageButton) dialogView.findViewById(R.id.close_button);
+        close_button.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                cancel();
+            }
+        });
+    }
+}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeShareActivity.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeShareActivity.java
index e3aca2cc..63c88ead 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeShareActivity.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeShareActivity.java
@@ -14,7 +14,8 @@
 public class QrCodeShareActivity extends ShareActivity {
     @Override
     protected void handleShareAction(ChromeActivity triggeringActivity) {
-        // TODO(crbug.com/993920): Open QR code share/scan activity.
+        QrCodeCoordinator qrCodeCoordinator = new QrCodeCoordinator(triggeringActivity);
+        qrCodeCoordinator.show();
     }
 
     public static boolean featureIsAvailable() {
diff --git a/chrome/browser/share/android/java_sources.gni b/chrome/browser/share/android/java_sources.gni
index a42bc950..bf6ca1a 100644
--- a/chrome/browser/share/android/java_sources.gni
+++ b/chrome/browser/share/android/java_sources.gni
@@ -3,4 +3,8 @@
 # found in the LICENSE file.
 
 # TODO(gayane): This should be a separate build target when circular dependencies are removed.
-share_java_sources = [ "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeShareActivity.java" ]
+share_java_sources = [
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeCoordinator.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialog.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeShareActivity.java",
+]
diff --git a/chrome/browser/touch_to_fill/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
index 07c543c..8560924 100644
--- a/chrome/browser/touch_to_fill/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
@@ -12,7 +12,7 @@
     "//base:jni_java",
     "//chrome/android:chrome_java",
     "//chrome/browser/touch_to_fill/android:public_java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//components/url_formatter/android:url_formatter_java",
     "//third_party/android_deps:com_android_support_recyclerview_v7_java",
     "//ui/android:ui_java",
diff --git a/chrome/browser/ui/android/widget/BUILD.gn b/chrome/browser/ui/android/widget/BUILD.gn
index 4e5fafd..0c907c1 100644
--- a/chrome/browser/ui/android/widget/BUILD.gn
+++ b/chrome/browser/ui/android/widget/BUILD.gn
@@ -55,7 +55,7 @@
   deps = [
     ":ui_widget_java_resources",
     "//base:base_java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//third_party/android_deps:android_support_v4_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:com_android_support_design_java",
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index 4f5624d..ca7227c 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -39,6 +39,7 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkShader.h"
+#include "ui/base/cursor/cursor_theme_manager_linux_observer.h"
 #include "ui/base/ime/linux/fake_input_method_context.h"
 #include "ui/base/ime/linux/linux_input_method_context.h"
 #include "ui/base/ime/linux/linux_input_method_context_factory.h"
@@ -435,6 +436,10 @@
                          G_CALLBACK(OnThemeChangedThunk), this);
   g_signal_connect_after(settings, "notify::gtk-application-prefer-dark-theme",
                          G_CALLBACK(OnThemeChangedThunk), this);
+  g_signal_connect_after(settings, "notify::gtk-cursor-theme-name",
+                         G_CALLBACK(OnCursorThemeNameChangedThunk), this);
+  g_signal_connect_after(settings, "notify::gtk-cursor-theme-size",
+                         G_CALLBACK(OnCursorThemeSizeChangedThunk), this);
 
   GdkScreen* screen = gdk_screen_get_default();
   // Listen for DPI changes.
@@ -628,14 +633,14 @@
     bool focus;
     views::Button::ButtonState state;
   } const paintstate[] = {
-      { !kFocus, views::Button::STATE_NORMAL, },
-      { !kFocus, views::Button::STATE_HOVERED, },
-      { !kFocus, views::Button::STATE_PRESSED, },
-      { !kFocus, views::Button::STATE_DISABLED, },
-      { kFocus, views::Button::STATE_NORMAL, },
-      { kFocus, views::Button::STATE_HOVERED, },
-      { kFocus, views::Button::STATE_PRESSED, },
-      { kFocus, views::Button::STATE_DISABLED, },
+      {!kFocus, views::Button::STATE_NORMAL},
+      {!kFocus, views::Button::STATE_HOVERED},
+      {!kFocus, views::Button::STATE_PRESSED},
+      {!kFocus, views::Button::STATE_DISABLED},
+      {kFocus, views::Button::STATE_NORMAL},
+      {kFocus, views::Button::STATE_HOVERED},
+      {kFocus, views::Button::STATE_PRESSED},
+      {kFocus, views::Button::STATE_DISABLED},
   };
 
   for (unsigned i = 0; i < base::size(paintstate); i++) {
@@ -804,6 +809,25 @@
   return layouts->GetFirstAsciiCapableLayout()->GetMap();
 }
 
+std::string GtkUi::GetCursorThemeName() {
+  gchar* theme = nullptr;
+  g_object_get(gtk_settings_get_default(), "gtk-cursor-theme-name", &theme,
+               nullptr);
+  std::string theme_string;
+  if (theme) {
+    theme_string = theme;
+    g_free(theme);
+  }
+  return theme_string;
+}
+
+int GtkUi::GetCursorThemeSize() {
+  gint size = 0;
+  g_object_get(gtk_settings_get_default(), "gtk-cursor-theme-size", &size,
+               nullptr);
+  return size;
+}
+
 bool GtkUi::MatchEvent(const ui::Event& event,
                        std::vector<ui::TextEditCommandAuraLinux>* commands) {
   // Ensure that we have a keyboard handler.
@@ -822,6 +846,20 @@
   native_theme_->NotifyObservers();
 }
 
+void GtkUi::OnCursorThemeNameChanged(GtkSettings* settings,
+                                     GtkParamSpec* param) {
+  std::string cursor_theme_name = GetCursorThemeName();
+  for (auto& observer : cursor_theme_observers())
+    observer.OnCursorThemeNameChanged(cursor_theme_name);
+}
+
+void GtkUi::OnCursorThemeSizeChanged(GtkSettings* settings,
+                                     GtkParamSpec* param) {
+  int cursor_theme_size = GetCursorThemeSize();
+  for (auto& observer : cursor_theme_observers())
+    observer.OnCursorThemeSizeChanged(cursor_theme_size);
+}
+
 void GtkUi::OnDeviceScaleFactorMaybeChanged(void*, GParamSpec*) {
   UpdateDeviceScaleFactor();
 }
@@ -832,7 +870,6 @@
   // we'd regress startup time. Figure out how to do that when we can't access
   // the prefs system from here.
   UpdateDeviceScaleFactor();
-  UpdateCursorTheme();
   UpdateColors();
 }
 
@@ -999,31 +1036,6 @@
   }
 }
 
-void GtkUi::UpdateCursorTheme() {
-#if defined(USE_X11)
-  GtkSettings* settings = gtk_settings_get_default();
-
-  gchar* theme = nullptr;
-  gint size = 0;
-  g_object_get(settings, "gtk-cursor-theme-name", &theme,
-               "gtk-cursor-theme-size", &size, nullptr);
-
-  if (theme)
-    XcursorSetTheme(gfx::GetXDisplay(), theme);
-  if (size)
-    XcursorSetDefaultSize(gfx::GetXDisplay(), size);
-
-  g_free(theme);
-#else
-  // TODO(thomasanderson): GtkUi shouldn't be the class to make X11 or wayland
-  // calls. Instead, this function should be a getter for X11
-  // (XCursorSetTheme/XcursorSetDefaultSize) and Wayland (wl_cursor_theme_load)
-  // cursor code to call into. In addition, cursor theme name/size changes are
-  // not handled, so an observer interface should be added as well.
-  NOTIMPLEMENTED();
-#endif
-}
-
 void GtkUi::UpdateDefaultFont() {
   gfx::SetFontRenderParamsDeviceScaleFactor(device_scale_factor_);
 
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.h b/chrome/browser/ui/libgtkui/gtk_ui.h
index 545a0d3e..d41fe7e 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.h
+++ b/chrome/browser/ui/libgtkui/gtk_ui.h
@@ -104,6 +104,8 @@
   std::unique_ptr<views::NavButtonProvider> CreateNavButtonProvider() override;
 #endif
   base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override;
+  std::string GetCursorThemeName() override;
+  int GetCursorThemeSize() override;
 
   // ui::TextEditKeybindingDelegate:
   bool MatchEvent(const ui::Event& event,
@@ -116,6 +118,18 @@
 
   CHROMEG_CALLBACK_1(GtkUi,
                      void,
+                     OnCursorThemeNameChanged,
+                     GtkSettings*,
+                     GtkParamSpec*);
+
+  CHROMEG_CALLBACK_1(GtkUi,
+                     void,
+                     OnCursorThemeSizeChanged,
+                     GtkSettings*,
+                     GtkParamSpec*);
+
+  CHROMEG_CALLBACK_1(GtkUi,
+                     void,
                      OnDeviceScaleFactorMaybeChanged,
                      void*,
                      GParamSpec*);
@@ -127,9 +141,6 @@
   // ThemeService interface and the colors we send to Blink.
   void UpdateColors();
 
-  // Sets the Xcursor theme and size with the GTK theme and size.
-  void UpdateCursorTheme();
-
   // Updates |default_font_*|.
   void UpdateDefaultFont();
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
index 791c8c1..cdbc1c4e 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
@@ -18,6 +18,8 @@
 #include "chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/view_class_properties.h"
 #include "ui/views/widget/widget.h"
 
 // static
@@ -45,6 +47,10 @@
   page_action_icon_container_view_ =
       AddChildView(std::make_unique<PageActionIconContainerView>(params));
 
+  avatar_->SetProperty(views::kFlexBehaviorKey,
+                       views::FlexSpecification::ForSizeRule(
+                           views::MinimumFlexSizeRule::kScaleToMinimum,
+                           views::MaximumFlexSizeRule::kPreferred));
   AddMainButton(avatar_);
 }
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index f1eb3c95..561cd618 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -656,15 +656,20 @@
   const int default_margin = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
   // TODO(dfried): rename this constant.
   const int location_bar_margin = GetLayoutConstant(TOOLBAR_STANDARD_SPACING);
-  const views::FlexSpecification browser_actions_flex_rule =
-      views::FlexSpecification::ForCustomRule(
-          BrowserActionsContainer::GetFlexRule())
-          .WithOrder(2);
+  const views::FlexSpecification account_container_flex_rule =
+      views::FlexSpecification::ForSizeRule(
+          views::MinimumFlexSizeRule::kScaleToMinimum,
+          views::MaximumFlexSizeRule::kPreferred)
+          .WithOrder(1);
   const views::FlexSpecification location_bar_flex_rule =
       views::FlexSpecification::ForSizeRule(
           views::MinimumFlexSizeRule::kScaleToMinimum,
           views::MaximumFlexSizeRule::kUnbounded)
-          .WithOrder(1);
+          .WithOrder(2);
+  const views::FlexSpecification browser_actions_flex_rule =
+      views::FlexSpecification::ForCustomRule(
+          BrowserActionsContainer::GetFlexRule())
+          .WithOrder(3);
 
   layout_manager_ = SetLayoutManager(std::make_unique<views::FlexLayout>());
 
@@ -685,6 +690,11 @@
                                   gfx::Insets(0, location_bar_margin));
   }
 
+  if (toolbar_account_icon_container_) {
+    toolbar_account_icon_container_->SetProperty(views::kFlexBehaviorKey,
+                                                 account_container_flex_rule);
+  }
+
   LayoutCommon();
 }
 
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
index b0a3a1fb..e3b251a 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
@@ -29,12 +29,15 @@
 #include "chrome/browser/ui/tabs/tab_utils.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
+#include "chrome/browser/ui/webui/localized_string.h"
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_layout.h"
 #include "chrome/browser/ui/webui/theme_handler.h"
 #include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/generated_resources.h"
 #include "chrome/grit/tab_strip_resources.h"
 #include "chrome/grit/tab_strip_resources_map.h"
 #include "components/favicon_base/favicon_url_parser.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/favicon_status.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/url_data_source.h"
@@ -454,6 +457,23 @@
   html_source->AddBoolean(
       "showDemoOptions",
       base::FeatureList::IsEnabled(features::kWebUITabStripDemoOptions));
+
+  static constexpr LocalizedString kStrings[] = {
+      {"closeTab", IDS_ACCNAME_CLOSE},
+      {"tabCrashed", IDS_TAB_AX_LABEL_CRASHED_FORMAT},
+      {"tabNetworkError", IDS_TAB_AX_LABEL_NETWORK_ERROR_FORMAT},
+      {"audioPlaying", IDS_TAB_AX_LABEL_AUDIO_PLAYING_FORMAT},
+      {"usbConnected", IDS_TAB_AX_LABEL_USB_CONNECTED_FORMAT},
+      {"bluetoothConnected", IDS_TAB_AX_LABEL_BLUETOOTH_CONNECTED_FORMAT},
+      {"serialConnected", IDS_TAB_AX_LABEL_SERIAL_CONNECTED_FORMAT},
+      {"mediaRecording", IDS_TAB_AX_LABEL_MEDIA_RECORDING_FORMAT},
+      {"audioMuting", IDS_TAB_AX_LABEL_AUDIO_MUTING_FORMAT},
+      {"tabCapturing", IDS_TAB_AX_LABEL_DESKTOP_CAPTURING_FORMAT},
+      {"pipPlaying", IDS_TAB_AX_LABEL_PIP_PLAYING_FORMAT},
+      {"desktopCapturing", IDS_TAB_AX_LABEL_DESKTOP_CAPTURING_FORMAT},
+      {"vrPresenting", IDS_TAB_AX_LABEL_VR_PRESENTING},
+  };
+  AddLocalizedStringsBulk(html_source, kStrings, base::size(kStrings));
   html_source->UseStringsJs();
 
   content::WebUIDataSource::Add(profile, html_source);
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui_browsertest.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui_browsertest.cc
index 77ca927..95fc7f4f 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui_browsertest.cc
@@ -95,7 +95,8 @@
 // static
 const std::string TabStripUIBrowserTest::tab_query_js(
     "document.querySelector('tabstrip-tab-list')"
-    "    .shadowRoot.querySelector('tabstrip-tab')");
+    "    .shadowRoot.querySelector('tabstrip-tab')"
+    "    .shadowRoot.querySelector('#tab')");
 
 IN_PROC_BROWSER_TEST_F(TabStripUIBrowserTest, ActivatingTabClosesEmbedder) {
   const std::string activate_tab_js = tab_query_js + ".click()";
diff --git a/chrome/browser/util/BUILD.gn b/chrome/browser/util/BUILD.gn
new file mode 100644
index 0000000..8153ce1
--- /dev/null
+++ b/chrome/browser/util/BUILD.gn
@@ -0,0 +1,43 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  java_files = [
+    "android/java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java",
+    "android/java/src/org/chromium/chrome/browser/util/AndroidTaskUtils.java",
+    "android/java/src/org/chromium/chrome/browser/util/BitmapCache.java",
+    "android/java/src/org/chromium/chrome/browser/util/ChromeContextUtil.java",
+    "android/java/src/org/chromium/chrome/browser/util/ChromeFileProvider.java",
+    "android/java/src/org/chromium/chrome/browser/util/ColorUtils.java",
+    "android/java/src/org/chromium/chrome/browser/util/ConversionUtils.java",
+    "android/java/src/org/chromium/chrome/browser/util/FileSizeUtil.java",
+    "android/java/src/org/chromium/chrome/browser/util/HashUtil.java",
+    "android/java/src/org/chromium/chrome/browser/util/IntentUtils.java",
+    "android/java/src/org/chromium/chrome/browser/util/KeyNavigationUtil.java",
+    "android/java/src/org/chromium/chrome/browser/util/MathUtils.java",
+    "android/java/src/org/chromium/chrome/browser/util/PlatformUtil.java",
+    "android/java/src/org/chromium/chrome/browser/util/UrlConstants.java",
+    "android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java",
+    "android/java/src/org/chromium/chrome/browser/util/ViewUtils.java",
+  ]
+  deps = [
+    "//base:base_java",
+    "//base:jni_java",
+    "//content/public/android:content_java",
+    "//third_party/android_deps:com_android_support_collections_java",
+    "//third_party/android_deps:com_android_support_support_compat_java",
+    "//third_party/android_deps:com_android_support_support_core_utils_java",
+  ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+generate_jni("jni_headers") {
+  sources = [
+    "android/java/src/org/chromium/chrome/browser/util/ChromeContextUtil.java",
+    "android/java/src/org/chromium/chrome/browser/util/PlatformUtil.java",
+    "android/java/src/org/chromium/chrome/browser/util/UrlUtilities.java",
+  ]
+}
diff --git a/chrome/browser/util/android/BUILD.gn b/chrome/browser/util/android/BUILD.gn
index f9d7aa9..941d887 100644
--- a/chrome/browser/util/android/BUILD.gn
+++ b/chrome/browser/util/android/BUILD.gn
@@ -4,40 +4,8 @@
 
 import("//build/config/android/rules.gni")
 
-android_library("java") {
-  java_files = [
-    "java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java",
-    "java/src/org/chromium/chrome/browser/util/AndroidTaskUtils.java",
-    "java/src/org/chromium/chrome/browser/util/BitmapCache.java",
-    "java/src/org/chromium/chrome/browser/util/ChromeContextUtil.java",
-    "java/src/org/chromium/chrome/browser/util/ChromeFileProvider.java",
-    "java/src/org/chromium/chrome/browser/util/ColorUtils.java",
-    "java/src/org/chromium/chrome/browser/util/ConversionUtils.java",
-    "java/src/org/chromium/chrome/browser/util/FileSizeUtil.java",
-    "java/src/org/chromium/chrome/browser/util/HashUtil.java",
-    "java/src/org/chromium/chrome/browser/util/IntentUtils.java",
-    "java/src/org/chromium/chrome/browser/util/KeyNavigationUtil.java",
-    "java/src/org/chromium/chrome/browser/util/MathUtils.java",
-    "java/src/org/chromium/chrome/browser/util/PlatformUtil.java",
-    "java/src/org/chromium/chrome/browser/util/UrlConstants.java",
-    "java/src/org/chromium/chrome/browser/util/UrlUtilities.java",
-    "java/src/org/chromium/chrome/browser/util/ViewUtils.java",
-  ]
+java_group("java") {
   deps = [
-    "//base:base_java",
-    "//base:jni_java",
-    "//content/public/android:content_java",
-    "//third_party/android_deps:com_android_support_collections_java",
-    "//third_party/android_deps:com_android_support_support_compat_java",
-    "//third_party/android_deps:com_android_support_support_core_utils_java",
-  ]
-  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
-}
-
-generate_jni("jni_headers") {
-  sources = [
-    "java/src/org/chromium/chrome/browser/util/ChromeContextUtil.java",
-    "java/src/org/chromium/chrome/browser/util/PlatformUtil.java",
-    "java/src/org/chromium/chrome/browser/util/UrlUtilities.java",
+    "//chrome/browser/util:java",
   ]
 }
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index 387034a0..8192a811 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -201,6 +201,10 @@
     cflags = [ "-fsymbol-partition=libvr.so" ]
   }
 
+  if (is_android) {
+    sources += [ "jni_onload.cc" ]
+  }
+
   defines = [ "VR_UI_IMPLEMENTATION" ]
 
   if (use_command_buffer) {
diff --git a/chrome/browser/vr/jni_onload.cc b/chrome/browser/vr/jni_onload.cc
new file mode 100644
index 0000000..817645352
--- /dev/null
+++ b/chrome/browser/vr/jni_onload.cc
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+// This method is required by the module loading backend. And it is supposed to
+// register VR's native JNI methods. However, since VR's Android-specific native
+// code still lives in the base module, VR's JNI registration is invoked
+// manually. Therefore, this function does nothing.
+JNI_GENERATOR_EXPORT bool JNI_OnLoad_vr(JNIEnv* env) {
+  return true;
+}
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 2b7d157..edb5db70 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -617,7 +617,7 @@
     "extension_types": ["extension", "legacy_packaged_app"]
   },
   "printingMetrics": {
-    "channel": "dev",
+    "channel": "stable",
     "platforms": ["chromeos"],
     "extension_types": ["extension"],
     "location": "policy"
diff --git a/chrome/installer/setup/archive_patch_helper.cc b/chrome/installer/setup/archive_patch_helper.cc
index 0fa7dae..fcb9a5f6c 100644
--- a/chrome/installer/setup/archive_patch_helper.cc
+++ b/chrome/installer/setup/archive_patch_helper.cc
@@ -8,6 +8,7 @@
 
 #include "base/files/file_util.h"
 #include "base/logging.h"
+#include "base/optional.h"
 #include "chrome/installer/setup/buildflags.h"
 #include "chrome/installer/util/lzma_util.h"
 #include "components/zucchini/zucchini.h"
@@ -48,12 +49,13 @@
 
   // UnPackArchive takes care of logging.
   base::FilePath output_file;
-  UnPackStatus unpack_status = UNPACK_NO_ERROR;
-  int32_t ntstatus = 0;
-  DWORD lzma_result = UnPackArchive(compressed_archive_, working_directory_,
-                                    &output_file, &unpack_status, &ntstatus);
-  RecordUnPackMetrics(unpack_status, ntstatus, lzma_result, consumer_);
-  if (lzma_result != ERROR_SUCCESS)
+  base::Optional<DWORD> error_code;
+  base::Optional<int32_t> ntstatus;
+  UnPackStatus unpack_status =
+      UnPackArchive(compressed_archive_, working_directory_, &output_file,
+                    &error_code, &ntstatus);
+  RecordUnPackMetrics(unpack_status, ntstatus, error_code, consumer_);
+  if (unpack_status != UNPACK_NO_ERROR)
     return false;
 
   last_uncompressed_file_ = output_file;
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index 2f01efb..f282194 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -26,6 +26,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/persistent_histogram_storage.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/optional.h"
 #include "base/path_service.h"
 #include "base/process/launch.h"
 #include "base/process/memory.h"
@@ -1098,13 +1099,13 @@
   // --- Background ---
   //   ~14s (50%ile) / >3m (99%ile)
   installer_state.SetStage(UNPACKING);
-  UnPackStatus unpack_status = UNPACK_NO_ERROR;
-  int32_t ntstatus = 0;
-  DWORD lzma_result = UnPackArchive(uncompressed_archive, unpack_path, NULL,
-                                    &unpack_status, &ntstatus);
-  RecordUnPackMetrics(unpack_status, ntstatus, lzma_result,
+  base::Optional<DWORD> error_code;
+  base::Optional<int32_t> ntstatus;
+  UnPackStatus unpack_status = UnPackArchive(uncompressed_archive, unpack_path,
+                                             nullptr, &error_code, &ntstatus);
+  RecordUnPackMetrics(unpack_status, ntstatus, error_code,
                       UnPackConsumer::UNCOMPRESSED_CHROME_ARCHIVE);
-  if (lzma_result) {
+  if (unpack_status != UNPACK_NO_ERROR) {
     installer_state.WriteInstallerResult(
         UNPACKING_FAILED,
         IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc
index 85faef7..ff3ab76 100644
--- a/chrome/installer/setup/setup_util.cc
+++ b/chrome/installer/setup/setup_util.cc
@@ -701,8 +701,8 @@
 }
 
 void RecordUnPackMetrics(UnPackStatus unpack_status,
-                         int32_t status,
-                         DWORD lzma_result,
+                         base::Optional<int32_t> ntstatus,
+                         base::Optional<DWORD> error_code,
                          UnPackConsumer consumer) {
   std::string consumer_name = "";
 
@@ -725,11 +725,16 @@
       std::string(std::string(kUnPackStatusMetricsName) + "_" + consumer_name),
       unpack_status, UNPACK_STATUS_COUNT);
 
-  base::UmaHistogramSparse(
-      std::string(kUnPackResultMetricsName) + "_" + consumer_name, lzma_result);
-
-  base::UmaHistogramSparse(
-      std::string(kUnPackNTSTATUSMetricsName) + "_" + consumer_name, status);
+  if (error_code.has_value()) {
+    base::UmaHistogramSparse(
+        std::string(kUnPackResultMetricsName) + "_" + consumer_name,
+        *error_code);
+  }
+  if (ntstatus.has_value()) {
+    base::UmaHistogramSparse(
+        std::string(kUnPackNTSTATUSMetricsName) + "_" + consumer_name,
+        *ntstatus);
+  }
 }
 
 void RegisterEventLogProvider(const base::FilePath& install_directory,
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h
index bfb53eb..d4e33a9 100644
--- a/chrome/installer/setup/setup_util.h
+++ b/chrome/installer/setup/setup_util.h
@@ -123,8 +123,8 @@
 
 // Records UMA metrics for unpack result.
 void RecordUnPackMetrics(UnPackStatus unpack_status,
-                         int32_t status,
-                         DWORD lzma_result,
+                         base::Optional<int32_t> ntstatus,
+                         base::Optional<DWORD> error_code,
                          UnPackConsumer consumer);
 
 // Register Chrome's EventLog message provider dll.
diff --git a/chrome/installer/test/alternate_version_generator.cc b/chrome/installer/test/alternate_version_generator.cc
index 50503f0..0e253d2 100644
--- a/chrome/installer/test/alternate_version_generator.cc
+++ b/chrome/installer/test/alternate_version_generator.cc
@@ -649,7 +649,7 @@
   // Unpack chrome.packed.7z (static build only).
   if (!chrome_packed_7z.empty()) {
     if (UnPackArchive(chrome_packed_7z, work_dir.directory(), &chrome_7z,
-                      nullptr, nullptr) != ERROR_SUCCESS) {
+                      nullptr, nullptr) != UNPACK_NO_ERROR) {
       LOG(DFATAL) << "Failed unpacking \"" << chrome_packed_7z.value() << "\"";
       return false;
     }
@@ -658,7 +658,7 @@
 
   // Unpack chrome.7z
   if (UnPackArchive(chrome_7z, work_dir.directory(), nullptr, nullptr,
-                    nullptr) != ERROR_SUCCESS) {
+                    nullptr) != UNPACK_NO_ERROR) {
     LOG(DFATAL) << "Failed unpacking \"" << chrome_7z.value() << "\"";
     return false;
   }
diff --git a/chrome/installer/util/lzma_util.cc b/chrome/installer/util/lzma_util.cc
index f7c6ed133..d1d28031 100644
--- a/chrome/installer/util/lzma_util.cc
+++ b/chrome/installer/util/lzma_util.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/installer/util/lzma_util.h"
 
+#include <windows.h>
+
 #include <stddef.h>
 
 #include <vector>
@@ -102,71 +104,62 @@
 
 }  // namespace
 
-DWORD UnPackArchive(const base::FilePath& archive,
-                    const base::FilePath& output_dir,
-                    base::FilePath* output_file,
-                    UnPackStatus* unpack_status,
-                    int32_t* ntstatus) {
+UnPackStatus UnPackArchive(const base::FilePath& archive,
+                           const base::FilePath& output_dir,
+                           base::FilePath* output_file,
+                           base::Optional<DWORD>* error_code,
+                           base::Optional<int32_t>* ntstatus) {
   VLOG(1) << "Opening archive " << archive.value();
   LzmaUtilImpl lzma_util;
-  DWORD ret;
-  if ((ret = lzma_util.OpenArchive(archive)) != ERROR_SUCCESS) {
+  UnPackStatus status;
+  if ((status = lzma_util.OpenArchive(archive)) != UNPACK_NO_ERROR) {
     PLOG(ERROR) << "Unable to open install archive: " << archive.value();
   } else {
     VLOG(1) << "Uncompressing archive to path " << output_dir.value();
-    if ((ret = lzma_util.UnPack(output_dir, output_file)) != ERROR_SUCCESS)
+    if ((status = lzma_util.UnPack(output_dir, output_file)) != UNPACK_NO_ERROR)
       PLOG(ERROR) << "Unable to uncompress archive: " << archive.value();
-    lzma_util.CloseArchive();
   }
-  if (unpack_status)
-    *unpack_status = lzma_util.GetUnPackStatus();
+  if (error_code)
+    *error_code = lzma_util.GetErrorCode();
   if (ntstatus)
     *ntstatus = lzma_util.GetNTSTATUSCode();
-  return ret;
+  return status;
 }
 
-LzmaUtilImpl::LzmaUtilImpl() {}
-LzmaUtilImpl::~LzmaUtilImpl() {
-  CloseArchive();
-}
+LzmaUtilImpl::LzmaUtilImpl() = default;
+LzmaUtilImpl::~LzmaUtilImpl() = default;
 
-DWORD LzmaUtilImpl::OpenArchive(const base::FilePath& archivePath) {
+UnPackStatus LzmaUtilImpl::OpenArchive(const base::FilePath& archivePath) {
   // Make sure file is not already open.
   CloseArchive();
 
-  DWORD ret = ERROR_SUCCESS;
-  archive_handle_ =
-      CreateFile(archivePath.value().c_str(), GENERIC_READ, FILE_SHARE_READ,
-                 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-  if (archive_handle_ == INVALID_HANDLE_VALUE) {
-    archive_handle_ = NULL;  // The rest of the code only checks for NULL.
-    ret = GetLastError();
-    if (ret == ERROR_FILE_NOT_FOUND)
-      unpack_status_ = UNPACK_ARCHIVE_NOT_FOUND;
-    else
-      unpack_status_ = UNPACK_ARCHIVE_CANNOT_OPEN;
-  }
-  return ret;
+  archive_file_.Initialize(archivePath, base::File::FLAG_OPEN |
+                                            base::File::FLAG_READ |
+                                            base::File::FLAG_EXCLUSIVE_WRITE);
+  if (archive_file_.IsValid())
+    return UNPACK_NO_ERROR;
+  error_code_ = ::GetLastError();
+  return archive_file_.error_details() == base::File::FILE_ERROR_NOT_FOUND
+             ? UNPACK_ARCHIVE_NOT_FOUND
+             : UNPACK_ARCHIVE_CANNOT_OPEN;
 }
 
-DWORD LzmaUtilImpl::UnPack(const base::FilePath& location) {
+UnPackStatus LzmaUtilImpl::UnPack(const base::FilePath& location) {
   return UnPack(location, NULL);
 }
 
-DWORD LzmaUtilImpl::UnPack(const base::FilePath& location,
-                           base::FilePath* output_file) {
-  if (!archive_handle_)
-    return ERROR_INVALID_HANDLE;
+UnPackStatus LzmaUtilImpl::UnPack(const base::FilePath& location,
+                                  base::FilePath* output_file) {
+  DCHECK(archive_file_.IsValid());
 
   CFileInStream archiveStream;
   CLookToRead lookStream;
   CSzArEx db;
   ISzAlloc allocImp;
   ISzAlloc allocTempImp;
-  DWORD ret = ERROR_SUCCESS;
   SRes sz_res = SZ_OK;
 
-  archiveStream.file.handle = archive_handle_;
+  archiveStream.file.handle = archive_file_.GetPlatformFile();
   archiveStream.s.Read = SzFileReadImp;
   archiveStream.s.Seek = SzFileSeekImp;
   LookToRead_CreateVTable(&lookStream, false);
@@ -179,12 +172,14 @@
 
   CrcGenerateTable();
   SzArEx_Init(&db);
+  ::SetLastError(ERROR_SUCCESS);
   if ((sz_res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp)) !=
       SZ_OK) {
     LOG(ERROR) << L"Error returned by SzArchiveOpen: " << sz_res;
-
-    unpack_status_ = UNPACK_SZAREX_OPEN_ERROR;
-    return ERROR_INVALID_HANDLE;
+    auto error_code = ::GetLastError();
+    if (error_code != ERROR_SUCCESS)
+      error_code_ = error_code;
+    return UNPACK_SZAREX_OPEN_ERROR;
   }
 
   Byte* outBuffer = 0;  // it must be 0 before first call for each new archive
@@ -193,41 +188,40 @@
 
   LzmaFileAllocator fileAllocator(location);
 
-  unpack_status_ = UNPACK_NO_ERROR;
-
+  UnPackStatus status = UNPACK_NO_ERROR;
   for (unsigned int i = 0; i < db.NumFiles; i++) {
     DWORD written;
     size_t offset = 0;
     size_t outSizeProcessed = 0;
 
-    int32_t status = 0;  // STATUS_SUCCESS
+    int32_t ntstatus = 0;  // STATUS_SUCCESS
+    ::SetLastError(ERROR_SUCCESS);
     __try {
       if ((sz_res =
                SzArEx_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer,
                               &outBufferSize, &offset, &outSizeProcessed,
                               &fileAllocator, &allocTempImp)) != SZ_OK) {
         LOG(ERROR) << L"Error returned by SzExtract: " << sz_res;
-
-        ret = ERROR_INVALID_HANDLE;
-        unpack_status_ = UNPACK_EXTRACT_ERROR;
+        auto error_code = ::GetLastError();
+        if (error_code != ERROR_SUCCESS)
+          error_code_ = error_code;
+        status = UNPACK_EXTRACT_ERROR;
       }
     } __except(FilterPageError(fileAllocator, GetExceptionCode(),
-                                GetExceptionInformation(), &status)) {
-      ret = ERROR_IO_DEVICE;
-      ntstatus_ = status;
+                                GetExceptionInformation(), &ntstatus)) {
+      ntstatus_ = ntstatus;
+      status = UNPACK_EXTRACT_EXCEPTION;
       LOG(ERROR) << L"EXCEPTION_IN_PAGE_ERROR while accessing mapped memory; "
                     L"NTSTATUS = "
-                 << ntstatus_;
-      unpack_status_ = UNPACK_EXTRACT_EXCEPTION;
+                 << ntstatus;
     }
-    if (ret != ERROR_SUCCESS)
+    if (status != UNPACK_NO_ERROR)
       break;
 
     size_t file_name_length = SzArEx_GetFileNameUtf16(&db, i, NULL);
     if (file_name_length < 1) {
       LOG(ERROR) << L"Couldn't get file name";
-      ret = ERROR_INVALID_HANDLE;
-      unpack_status_ = UNPACK_NO_FILENAME_ERROR;
+      status = UNPACK_NO_FILENAME_ERROR;
       break;
     }
 
@@ -242,7 +236,11 @@
 
     // If archive entry is directory create it and move on to the next entry.
     if (SzArEx_IsDir(&db, i)) {
-      CreateDirectory(file_path);
+      if (!CreateDirectory(file_path)) {
+        error_code_ = ::GetLastError();
+        status = UNPACK_CREATE_FILE_ERROR;
+        break;
+      }
       continue;
     }
 
@@ -252,9 +250,9 @@
     hFile = CreateFile(file_path.value().c_str(), GENERIC_WRITE, 0, NULL,
                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
     if (hFile == INVALID_HANDLE_VALUE) {
-      ret = GetLastError();
-      LOG(ERROR) << L"Error returned by CreateFile: " << ret;
-      unpack_status_ = UNPACK_CREATE_FILE_ERROR;
+      error_code_ = ::GetLastError();
+      status = UNPACK_CREATE_FILE_ERROR;
+      PLOG(ERROR) << L"CreateFile failed";
       break;
     }
 
@@ -268,57 +266,56 @@
       if ((!WriteFile(hFile, outBuffer + offset + total_written,
                       static_cast<DWORD>(write_amount), &written, nullptr)) ||
           (written != write_amount)) {
-        ret = GetLastError();
+        error_code_ = ::GetLastError();
+        status = UNPACK_WRITE_FILE_ERROR;
         PLOG(ERROR) << L"Error returned by WriteFile";
         CloseHandle(hFile);
-        unpack_status_ = UNPACK_WRITE_FILE_ERROR;
         break;
       }
       total_written += write_amount;
     }
 
     // Break out of the file loop if the write loop failed.
-    if (unpack_status_ != UNPACK_NO_ERROR)
+    if (status != UNPACK_NO_ERROR)
       break;
 
     if (SzBitWithVals_Check(&db.MTime, i)) {
       if (!SetFileTime(hFile, NULL, NULL,
                        (const FILETIME*)(&db.MTime.Vals[i]))) {
-        ret = GetLastError();
+        error_code_ = ::GetLastError();
+        status = UNPACK_SET_FILE_TIME_ERROR;
         PLOG(ERROR) << L"Error returned by SetFileTime";
         CloseHandle(hFile);
-        unpack_status_ = UNPACK_SET_FILE_TIME_ERROR;
         break;
       }
     }
     if (!CloseHandle(hFile)) {
-      ret = GetLastError();
+      error_code_ = ::GetLastError();
+      status = UNPACK_CLOSE_FILE_ERROR;
       PLOG(ERROR) << L"Error returned by CloseHandle";
-      unpack_status_ = UNPACK_CLOSE_FILE_ERROR;
       break;
     }
   }  // for loop
   IAlloc_Free(&fileAllocator, outBuffer);
   SzArEx_Free(&db, &allocImp);
-  DCHECK_EQ(ret == static_cast<DWORD>(ERROR_SUCCESS),
-            unpack_status_ == UNPACK_NO_ERROR);
+  DCHECK(status != UNPACK_NO_ERROR || !error_code_.has_value());
+  DCHECK(status != UNPACK_NO_ERROR || !ntstatus_.has_value());
 
-  return ret;
+  return status;
 }
 
 void LzmaUtilImpl::CloseArchive() {
-  if (archive_handle_) {
-    CloseHandle(archive_handle_);
-    archive_handle_ = NULL;
-  }
+  archive_file_.Close();
+  error_code_ = base::nullopt;
+  ntstatus_ = base::nullopt;
 }
 
 bool LzmaUtilImpl::CreateDirectory(const base::FilePath& dir) {
-  bool ret = true;
-  if (directories_created_.find(dir.value()) == directories_created_.end()) {
-    ret = base::CreateDirectory(dir);
-    if (ret)
-      directories_created_.insert(dir.value());
+  bool result = true;
+  if (directories_created_.find(dir) == directories_created_.end()) {
+    result = base::CreateDirectory(dir);
+    if (result)
+      directories_created_.insert(dir);
   }
-  return ret;
+  return result;
 }
diff --git a/chrome/installer/util/lzma_util.h b/chrome/installer/util/lzma_util.h
index 11e0515..08c2334 100644
--- a/chrome/installer/util/lzma_util.h
+++ b/chrome/installer/util/lzma_util.h
@@ -5,13 +5,13 @@
 #ifndef CHROME_INSTALLER_UTIL_LZMA_UTIL_H_
 #define CHROME_INSTALLER_UTIL_LZMA_UTIL_H_
 
-#include <windows.h>
-
 #include <set>
 
+#include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
-#include "base/strings/string16.h"
+#include "base/optional.h"
+#include "base/win/windows_types.h"
 
 // The error status of LzmaUtil::Unpack which is used to publish metrics. Do not
 // change the order.
@@ -32,14 +32,15 @@
 
 // Unpacks the contents of |archive| into |output_dir|. |output_file|, if not
 // null, is populated with the name of the last (or only) member extracted from
-// the archive. Returns ERROR_SUCCESS on success. Otherwise, returns a Windows
-// error code and populates |unpack_status| (if not null) with a status value
-// indicating the operation that failed.
-DWORD UnPackArchive(const base::FilePath& archive,
-                    const base::FilePath& output_dir,
-                    base::FilePath* output_file,
-                    UnPackStatus* unpack_status,
-                    int32_t* ntstatus);
+// the archive. Returns UNPACK_NO_ERROR on success. Otherwise, returns a status
+// value indicating the operation that failed, populates |error_code| (if not
+// null) with a Windows error code and |ntstatus| with an exception code, if
+// any.
+UnPackStatus UnPackArchive(const base::FilePath& archive,
+                           const base::FilePath& output_dir,
+                           base::FilePath* output_file,
+                           base::Optional<DWORD>* error_code,
+                           base::Optional<int32_t>* ntstatus);
 
 // A utility class that wraps LZMA SDK library. Prefer UnPackArchive over using
 // this class directly.
@@ -48,29 +49,30 @@
   LzmaUtilImpl();
   ~LzmaUtilImpl();
 
-  DWORD OpenArchive(const base::FilePath& archivePath);
+  UnPackStatus OpenArchive(const base::FilePath& archivePath);
 
   // Unpacks the archive to the given location
-  DWORD UnPack(const base::FilePath& location);
-
-  void CloseArchive();
+  UnPackStatus UnPack(const base::FilePath& location);
 
   // Unpacks the archive to the given location and returns the last file
   // extracted from archive.
-  DWORD UnPack(const base::FilePath& location, base::FilePath* output_file);
+  UnPackStatus UnPack(const base::FilePath& location,
+                      base::FilePath* output_file);
 
-  UnPackStatus GetUnPackStatus() { return unpack_status_; }
-  int32_t GetNTSTATUSCode() { return ntstatus_; }
+  void CloseArchive();
+
+  base::Optional<DWORD> GetErrorCode() { return error_code_; }
+  base::Optional<int32_t> GetNTSTATUSCode() { return ntstatus_; }
 
  protected:
   bool CreateDirectory(const base::FilePath& dir);
 
  private:
-  HANDLE archive_handle_ = nullptr;
-  std::set<base::string16> directories_created_;
-  UnPackStatus unpack_status_ = UNPACK_NO_ERROR;
+  base::File archive_file_;
+  std::set<base::FilePath> directories_created_;
+  base::Optional<DWORD> error_code_;
   // Can't include ntstatus.h as it's conflicted with winnt.h
-  int32_t ntstatus_ = 0;  // STATUS_SUCCESS.
+  base::Optional<int32_t> ntstatus_;
 
   DISALLOW_COPY_AND_ASSIGN(LzmaUtilImpl);
 };
diff --git a/chrome/installer/util/lzma_util_unittest.cc b/chrome/installer/util/lzma_util_unittest.cc
index 08fa4ab39..a744d3b3 100644
--- a/chrome/installer/util/lzma_util_unittest.cc
+++ b/chrome/installer/util/lzma_util_unittest.cc
@@ -36,22 +36,21 @@
 TEST_F(LzmaUtilTest, OpenArchiveTest) {
   base::FilePath archive = data_dir_.AppendASCII("archive1.7z");
   LzmaUtilImpl lzma_util;
-  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), lzma_util.OpenArchive(archive));
+  EXPECT_EQ(UNPACK_NO_ERROR, lzma_util.OpenArchive(archive));
 
   // We allow opening another archive (which will automatically close the first
   // archive).
   archive = data_dir_.AppendASCII("archive2.7z");
-  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), lzma_util.OpenArchive(archive));
+  EXPECT_EQ(UNPACK_NO_ERROR, lzma_util.OpenArchive(archive));
 
   // Explicitly close and open the first archive again.
   lzma_util.CloseArchive();
   archive = data_dir_.AppendASCII("archive1.7z");
-  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), lzma_util.OpenArchive(archive));
+  EXPECT_EQ(UNPACK_NO_ERROR, lzma_util.OpenArchive(archive));
 
   // Make sure non-existent archive returns error.
   archive = data_dir_.AppendASCII("archive.non_existent.7z");
-  EXPECT_EQ(static_cast<DWORD>(ERROR_FILE_NOT_FOUND),
-            lzma_util.OpenArchive(archive));
+  EXPECT_EQ(UNPACK_ARCHIVE_NOT_FOUND, lzma_util.OpenArchive(archive));
 }
 
 // Test that we can extract archives successfully.
@@ -64,38 +63,33 @@
 
   base::FilePath archive = data_dir_.AppendASCII("archive1.7z");
   LzmaUtilImpl lzma_util;
-  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), lzma_util.OpenArchive(archive));
+  EXPECT_EQ(UNPACK_NO_ERROR, lzma_util.OpenArchive(archive));
   base::FilePath unpacked_file;
-  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
-            lzma_util.UnPack(extract_dir, &unpacked_file));
-  EXPECT_EQ(lzma_util.GetUnPackStatus(), UNPACK_NO_ERROR);
+  EXPECT_EQ(UNPACK_NO_ERROR, lzma_util.UnPack(extract_dir, &unpacked_file));
+  EXPECT_FALSE(lzma_util.GetErrorCode());
 
   EXPECT_TRUE(base::PathExists(extract_dir.AppendASCII("a.exe")));
   EXPECT_TRUE(unpacked_file == extract_dir.AppendASCII("a.exe"));
 
   archive = data_dir_.AppendASCII("archive2.7z");
-  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), lzma_util.OpenArchive(archive));
-  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
-            lzma_util.UnPack(extract_dir, &unpacked_file));
+  EXPECT_EQ(UNPACK_NO_ERROR, lzma_util.OpenArchive(archive));
+  EXPECT_EQ(UNPACK_NO_ERROR, lzma_util.UnPack(extract_dir, &unpacked_file));
   EXPECT_TRUE(base::PathExists(extract_dir.AppendASCII("b.exe")));
   EXPECT_TRUE(unpacked_file == extract_dir.AppendASCII("b.exe"));
-  EXPECT_EQ(lzma_util.GetUnPackStatus(), UNPACK_NO_ERROR);
+  EXPECT_FALSE(lzma_util.GetErrorCode());
 
   lzma_util.CloseArchive();
   archive = data_dir_.AppendASCII("invalid_archive.7z");
-  EXPECT_EQ(static_cast<DWORD>(ERROR_INVALID_HANDLE),
-            lzma_util.UnPack(extract_dir, &unpacked_file));
-  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), lzma_util.OpenArchive(archive));
-  EXPECT_EQ(static_cast<DWORD>(ERROR_INVALID_HANDLE),
+  EXPECT_EQ(UNPACK_NO_ERROR, lzma_util.OpenArchive(archive));
+  EXPECT_EQ(UNPACK_SZAREX_OPEN_ERROR,
             lzma_util.UnPack(extract_dir, &unpacked_file));
 
-  EXPECT_EQ(static_cast<DWORD>(ERROR_INVALID_HANDLE),
+  EXPECT_EQ(UNPACK_SZAREX_OPEN_ERROR,
             lzma_util.UnPack(extract_dir, &unpacked_file));
 
   archive = data_dir_.AppendASCII("archive3.7z");
-  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), lzma_util.OpenArchive(archive));
-  EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
-            lzma_util.UnPack(extract_dir, &unpacked_file));
+  EXPECT_EQ(UNPACK_NO_ERROR, lzma_util.OpenArchive(archive));
+  EXPECT_EQ(UNPACK_NO_ERROR, lzma_util.UnPack(extract_dir, &unpacked_file));
   EXPECT_TRUE(base::PathExists(extract_dir.AppendASCII("archive\\a.exe")));
   EXPECT_TRUE(base::PathExists(
       extract_dir.AppendASCII("archive\\sub_dir\\text.txt")));
@@ -112,23 +106,21 @@
   base::FilePath archive = data_dir_.AppendASCII("archive1.7z");
   base::FilePath unpacked_file;
 
-  EXPECT_EQ(
-      static_cast<DWORD>(ERROR_SUCCESS),
-      UnPackArchive(archive, extract_dir, &unpacked_file, nullptr, nullptr));
+  EXPECT_EQ(UNPACK_NO_ERROR, UnPackArchive(archive, extract_dir, &unpacked_file,
+                                           nullptr, nullptr));
 
   EXPECT_TRUE(base::PathExists(extract_dir.AppendASCII("a.exe")));
   EXPECT_TRUE(unpacked_file == extract_dir.AppendASCII("a.exe"));
 
   archive = data_dir_.AppendASCII("archive2.7z");
-  EXPECT_EQ(
-      static_cast<DWORD>(ERROR_SUCCESS),
-      UnPackArchive(archive, extract_dir, &unpacked_file, nullptr, nullptr));
+  EXPECT_EQ(UNPACK_NO_ERROR, UnPackArchive(archive, extract_dir, &unpacked_file,
+                                           nullptr, nullptr));
 
   EXPECT_TRUE(base::PathExists(extract_dir.AppendASCII("b.exe")));
   EXPECT_TRUE(unpacked_file == extract_dir.AppendASCII("b.exe"));
 
   archive = data_dir_.AppendASCII("invalid_archive.7z");
   EXPECT_EQ(
-      static_cast<DWORD>(ERROR_INVALID_HANDLE),
+      UNPACK_SZAREX_OPEN_ERROR,
       UnPackArchive(archive, extract_dir, &unpacked_file, nullptr, nullptr));
 }
diff --git a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
index 7890cd1..f6d65d1 100644
--- a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
+++ b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
@@ -32,7 +32,6 @@
 #include "extensions/common/permissions/permission_set.h"
 #include "extensions/common/switches.h"
 #include "extensions/renderer/bindings/api_bindings_system.h"
-#include "extensions/renderer/css_native_handler.h"
 #include "extensions/renderer/dispatcher.h"
 #include "extensions/renderer/lazy_background_page_native_handler.h"
 #include "extensions/renderer/native_extension_bindings_system.h"
@@ -104,9 +103,6 @@
       "lazy_background_page",
       std::unique_ptr<NativeHandler>(
           new extensions::LazyBackgroundPageNativeHandler(context)));
-  module_system->RegisterNativeHandler(
-      "css_natives", std::unique_ptr<NativeHandler>(
-                         new extensions::CssNativeHandler(context)));
 }
 
 void ChromeExtensionsDispatcherDelegate::PopulateSourceMap(
diff --git a/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js b/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js
index 36e3c19..530230fa 100644
--- a/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js
@@ -4,99 +4,30 @@
 
 // Custom binding for the declarativeContent API.
 
-if (!apiBridge) {
-  var utils = require('utils');
-  var validate = require('schemaUtils').validate;
-  var canonicalizeCompoundSelector =
-      requireNative('css_natives').CanonicalizeCompoundSelector;
-}
-
 var setIcon = require('setIcon').setIcon;
 
 apiBridge.registerCustomHook(function(api) {
   var declarativeContent = api.compiledApi;
 
-  if (apiBridge) {
-    // Validation for most types is done in the native C++ with native bindings,
-    // but setIcon is funny (and sadly broken). Ideally, we can move this
-    // validation entirely into the native code, and this whole file can go
-    // away.
-    var nativeSetIcon = declarativeContent.SetIcon;
-    declarativeContent.SetIcon = function(parameters) {
-      // TODO(devlin): This is very, very wrong. setIcon() is potentially
-      // asynchronous (in the case of a path being specified), which means this
-      // becomes an "asynchronous constructor". Errors can be thrown *after* the
-      // `new declarativeContent.SetIcon(...)` call, and in the async cases,
-      // this wouldn't work when we immediately add the action via an API call
-      // (e.g.,
-      //   chrome.declarativeContent.onPageChange.addRules(
-      //       [{conditions: ..., actions: [ new SetIcon(...) ]}]);
-      // ). Some of this is tracked in http://crbug.com/415315.
-      setIcon(parameters, $Function.bind(function(data) {
-        // Fake calling the original function as a constructor.
-        $Object.setPrototypeOf(this, nativeSetIcon.prototype);
-        $Function.apply(nativeSetIcon, this, [data]);
-      }, this));
-    };
-    return;
-  }
-
-  // Returns the schema definition of type |typeId| defined in |namespace|.
-  function getSchema(typeId) {
-    return utils.lookup(api.schema.types,
-                        'id',
-                        'declarativeContent.' + typeId);
-  }
-
-  // Helper function for the constructor of concrete datatypes of the
-  // declarative content API.
-  // Makes sure that |this| contains the union of parameters and
-  // {'instanceType': 'declarativeContent.' + typeId} and validates the
-  // generated union dictionary against the schema for |typeId|.
-  function setupInstance(instance, parameters, typeId) {
-    for (var key in parameters) {
-      if ($Object.hasOwnProperty(parameters, key)) {
-        instance[key] = parameters[key];
-      }
-    }
-    instance.instanceType = 'declarativeContent.' + typeId;
-    var schema = getSchema(typeId);
-    validate([instance], [schema]);
-  }
-
-  function canonicalizeCssSelectors(selectors) {
-    for (var i = 0; i < selectors.length; i++) {
-      var canonicalizedSelector = canonicalizeCompoundSelector(selectors[i]);
-      if (canonicalizedSelector == '') {
-        throw new Error(
-            'Element of \'css\' array must be a ' +
-            'list of valid compound selectors: ' +
-            selectors[i]);
-      }
-      selectors[i] = canonicalizedSelector;
-    }
-  }
-
-  // Setup all data types for the declarative content API.
-  declarativeContent.PageStateMatcher = function(parameters) {
-    setupInstance(this, parameters, 'PageStateMatcher');
-    if ($Object.hasOwnProperty(this, 'css')) {
-      canonicalizeCssSelectors(this.css);
-    }
-  };
-  declarativeContent.ShowAction = function(parameters) {
-    setupInstance(this, parameters, 'ShowAction');
-  };
-  declarativeContent.ShowPageAction = declarativeContent.ShowAction;
-  declarativeContent.RequestContentScript = function(parameters) {
-    setupInstance(this, parameters, 'RequestContentScript');
-  };
-  // TODO(rockot): Do not expose this in M39 stable. Making this restriction
-  // possible will take some extra work. See http://crbug.com/415315
-  // Note: See also the SetIcon wrapper above for more issues.
+  // Validation for most types is done in the native C++ with native bindings,
+  // but setIcon is funny (and sadly broken). Ideally, we can move this
+  // validation entirely into the native code, and this whole file can go
+  // away.
+  var nativeSetIcon = declarativeContent.SetIcon;
   declarativeContent.SetIcon = function(parameters) {
+    // TODO(devlin): This is very, very wrong. setIcon() is potentially
+    // asynchronous (in the case of a path being specified), which means this
+    // becomes an "asynchronous constructor". Errors can be thrown *after* the
+    // `new declarativeContent.SetIcon(...)` call, and in the async cases,
+    // this wouldn't work when we immediately add the action via an API call
+    // (e.g.,
+    //   chrome.declarativeContent.onPageChange.addRules(
+    //       [{conditions: ..., actions: [ new SetIcon(...) ]}]);
+    // ). Some of this is tracked in http://crbug.com/415315.
     setIcon(parameters, $Function.bind(function(data) {
-      setupInstance(this, data, 'SetIcon');
+      // Fake calling the original function as a constructor.
+      $Object.setPrototypeOf(this, nativeSetIcon.prototype);
+      $Function.apply(nativeSetIcon, this, [data]);
     }, this));
   };
 });
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index 13ec18c..917f19f 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -181,7 +181,7 @@
     "//base:base_java_test_support",
     "//chrome/android:chrome_java",
     "//chrome/android/third_party/compositor_animator:compositor_animator_java",
-    "//chrome/browser/util/android:java",
+    "//chrome/browser/util:java",
     "//components/bookmarks/common/android:bookmarks_java",
     "//components/embedder_support/android:web_contents_delegate_java",
     "//components/invalidation/impl:java",
diff --git a/chrome/test/chromedriver/test/run_java_tests.py b/chrome/test/chromedriver/test/run_java_tests.py
index 7e94247..fe178a5 100755
--- a/chrome/test/chromedriver/test/run_java_tests.py
+++ b/chrome/test/chromedriver/test/run_java_tests.py
@@ -7,10 +7,9 @@
 
 import optparse
 import os
-import shutil
+import re
 import stat
 import sys
-import xml.dom.minidom as minidom
 
 _THIS_DIR = os.path.abspath(os.path.dirname(__file__))
 sys.path.insert(1, os.path.join(_THIS_DIR, os.pardir))
@@ -19,48 +18,15 @@
 import test_environment
 import util
 import glob
-import time
 
 if util.IsLinux():
   sys.path.insert(0, os.path.join(chrome_paths.GetSrc(), 'build', 'android'))
   from pylib import constants
 
 
-class TestResult(object):
-  """A result for an attempted single test case."""
-
-  def __init__(self, name, time, failure):
-    """Initializes a test result.
-
-    Args:
-      name: the full name of the test.
-      time: the amount of time the test ran, in seconds.
-      failure: the test error or failure message, or None if the test passed.
-    """
-    self._name = name
-    self._time = time
-    self._failure = failure
-
-  def GetName(self):
-    """Returns the test name."""
-    return self._name
-
-  def GetTime(self):
-    """Returns the time it took to run the test."""
-    return self._time
-
-  def IsPass(self):
-    """Returns whether the test passed."""
-    return self._failure is None
-
-  def GetFailureMessage(self):
-    """Returns the test failure message, or None if the test passed."""
-    return self._failure
-
-
 def _Run(java_tests_src_dir, test_filter, ready_to_run_tests,
          chromedriver_path, chrome_path, log_path, android_package_key,
-         verbose, debug):
+         debug, tests_report_file):
   """Run the WebDriver Java tests and return the test results.
 
   Args:
@@ -73,11 +39,7 @@
     chrome_path: path to Chrome exe.
     log_path: path to server log.
     android_package_key: name of Chrome's Android package.
-    verbose: whether the output should be verbose.
     debug: whether the tests should wait until attached by a debugger.
-
-  Returns:
-    A list of |TestResult|s.
   """
 
   sys_props = ['selenium.browser=chrome',
@@ -132,119 +94,78 @@
     jvm_args += ['-agentlib:jdwp=transport=%s,server=y,suspend=y,'
                  'address=33081' % transport]
 
-  return _RunAntTest(java_tests_src_dir, jvm_args, verbose, sys_props)
+  _RunTest(java_tests_src_dir, jvm_args, sys_props, tests_report_file)
 
-def _RunAntTest(java_tests_src_dir, jvm_args, verbose, sys_props):
-  """Runs a single Ant JUnit test suite and returns the |TestResult|s.
+def _RunTest(java_tests_src_dir, jvm_args, sys_props, tests_report_file):
+  """Runs a single JUnit test suite.
 
   Args:
     java_tests_src_dir: the directory to run the tests in.
     sys_props: Java system properties to set when running the tests.
     jvm_args: Java VM command line args to use.
-    verbose: whether the output should be verbose.
-
-  Returns:
-    A list of |TestResult|s.
   """
-  def _CreateBuildConfig(java_tests_src_dir, jvm_args, sys_props):
-    path_element = []
 
-    for name in glob.glob(java_tests_src_dir + "/jar/*.jar"):
-      path_element.append('\t<pathelement location=\"%s\" />' % name)
-
-    build_xml = '\n'.join([
-    '<project>',
-    ' <property name="test.class.name" value="org.openqa.selenium.chrome.ChromeDriverTests" />',
-    ' <path id="test.classpath">',
-    '\n'.join(path_element),
-    '</path>'])
-
-    def _SystemPropToXml(prop):
-      key, value = prop.split('=')
-      return '<sysproperty key="%s" value="%s"/>' % (key, value)
-    def _JvmArgToXml(arg):
-      return '<jvmarg value="%s"/>' % arg
-    build_xml += '\n'.join([
-      '  <target name="test">',
-      '    <junit %s>' % ' '.join(junit_props),
-      '      <formatter type="xml"/>',
-      '      ' + '\n      '.join(map(_SystemPropToXml, sys_props)),
-      '      ' + '\n      '.join(map(_JvmArgToXml, jvm_args)),
-      '      <test name="%s" outfile="%s"/>' % ("org.openqa.selenium.chrome.ChromeDriverTests", "results"),
-      '      <classpath refid="test.classpath" />',
-      '    </junit>',
-      '  </target>',
-      '</project>'])
-
-    return build_xml
-
-  def _ProcessResults(results_path):
-    doc = minidom.parse(results_path)
-    tests = []
-    for test in doc.getElementsByTagName('testcase'):
-      name = test.getAttribute('classname') + '.' + test.getAttribute('name')
-      time = test.getAttribute('time')
-      failure = None
-      error_nodes = test.getElementsByTagName('error')
-      failure_nodes = test.getElementsByTagName('failure')
-      if error_nodes:
-        failure = error_nodes[0].childNodes[0].nodeValue
-      elif failure_nodes:
-        failure = failure_nodes[0].childNodes[0].nodeValue
-      tests += [TestResult(name, time, failure)]
-    return tests
-
-  junit_props = ['printsummary="yes"',
-                 'fork="yes"',
-                 'haltonfailure="no"',
-                 'haltonerror="no"']
-  if verbose:
-    junit_props += ['showoutput="yes"']
-
-  ant_file = open(os.path.join(java_tests_src_dir, 'build.xml'), 'w')
-  file_contents = _CreateBuildConfig(java_tests_src_dir, jvm_args, sys_props)
-  ant_file.write(file_contents)
-  ant_file.close()
+  classpath = []
+  for name in glob.glob(java_tests_src_dir + "/jar/*.jar"):
+    classpath.append(name)
 
   if util.IsWindows():
-    ant_name = 'ant.bat'
+    separator = ';'
   else:
-    ant_name = 'ant'
-  code = util.RunCommand([ant_name, 'test'], java_tests_src_dir)
+    separator = ':'
+
+  code = util.RunCommand(
+                         ['java'] +
+                         ['-D%s' % sys_prop for sys_prop in sys_props] +
+                         ['-D%s' % jvm_arg for jvm_arg in jvm_args] +
+                         ['-cp', separator.join(classpath),
+                          'org.junit.runner.JUnitCore',
+                          'org.openqa.selenium.chrome.ChromeDriverTests'],
+                         java_tests_src_dir,
+                         tests_report_file)
+
   if code != 0:
-    print 'FAILED to run java tests of ChromeDriverTests through ant'
-    return
-  return _ProcessResults(os.path.join(java_tests_src_dir, 'results.xml'))
+    print 'FAILED to run java tests of ChromeDriverTests'
 
-
-def PrintTestResults(results):
+def _PrintTestResults(results_path):
   """Prints the given results in a format recognized by the buildbot."""
+  with open(results_path, "r") as myfile:
+    contents = myfile.read()
 
-  # If no results that means something went wrong and
-  # we should return non zero value
-  if len(results) == 0:
-    print 'No tests were run'
-    return 1
+  successJunitTestsCount = re.search(r'OK \((\d* tests)', contents)
 
-  failures = []
-  failure_names = []
-  for result in results:
-    if not result.IsPass():
-      failures += [result]
-      failure_names += ['.'.join(result.GetName().split('.')[-2:])]
+  if successJunitTestsCount:
+    testsCount = re.findall(r'INFO: <<< Finished (.*)\)', contents)
+    print("Ran %s tests " % len(testsCount))
+    myfile.close()
+    return 0
 
-  print 'Ran %s tests' % len(results)
-  print 'Failed %s:' % len(failures)
-  util.AddBuildStepText('failed %s/%s' % (len(failures), len(results)))
-  for result in failures:
-    print '=' * 80
-    print '=' * 10, result.GetName(), '(%ss)' % result.GetTime()
-    print result.GetFailureMessage()
-    if len(failures) < 10:
-      util.AddBuildStepText('.'.join(result.GetName().split('.')[-2:]))
-  print 'Rerun failing tests with filter:', ':'.join(failure_names)
-  return len(failures)
+  print("============================")
+  print("FAILURES DETAILS")
+  print("============================")
+  start = 'There w'
+  end = 'FAILURES!!!'
+  print contents[contents.find(start):contents.rfind(end)]
 
+  print("============================")
+  print("SUMMARY")
+  print("============================")
+  testsCount = re.findall(r'INFO: <<< Finished (.*)\)', contents)
+  print("Ran %s tests " % len(testsCount))
+
+  failuresCount = re.search(r'There w.* (.*) failure', contents)
+  if failuresCount:
+    print("Failed %s tests" % failuresCount.group(1))
+  failedTests = re.findall(r'\s\d*\) (.*org.openqa.*)', contents)
+  testsToReRun = []
+  for test in failedTests:
+    testName = test.split('(')[0]
+    testClass = test.split('(')[1].split('.')[-1]
+    testsToReRun.append(testClass[0:-1] + '.' + testName)
+  print 'Rerun failing tests with filter: ' + ':'.join(testsToReRun)
+
+  myfile.close()
+  return failuresCount.group(1)
 
 def main():
   parser = optparse.OptionParser()
@@ -312,6 +233,8 @@
     java_tests_src_dir = os.path.join(chrome_paths.GetSrc(), 'chrome', 'test',
                                       'chromedriver', 'third_party',
                                       'java_tests')
+    tests_report_file = os.path.join(java_tests_src_dir, 'results.txt')
+
     if (not os.path.exists(java_tests_src_dir) or
         not os.listdir(java_tests_src_dir)):
       java_tests_url = ('https://chromium.googlesource.com/chromium/deps'
@@ -324,25 +247,21 @@
              '  $ git clone %s java_tests' % java_tests_url)
       return 1
 
-    results = []
-    for filter in test_filters:
-      results += _Run(
-          java_tests_src_dir=java_tests_src_dir,
-          test_filter=filter,
-          ready_to_run_tests=ready_to_run_tests,
-          chromedriver_path=options.chromedriver,
-          chrome_path=util.GetAbsolutePathOfUserPath(options.chrome),
-          log_path=options.log_path,
-          android_package_key=options.android_package,
-          verbose=options.verbose,
-          debug=options.debug)
-    return PrintTestResults(results)
+    _Run(
+      java_tests_src_dir=java_tests_src_dir,
+      test_filter=test_filter,
+      ready_to_run_tests=ready_to_run_tests,
+      chromedriver_path=options.chromedriver,
+      chrome_path=util.GetAbsolutePathOfUserPath(options.chrome),
+      log_path=options.log_path,
+      android_package_key=options.android_package,
+      debug=options.debug,
+      tests_report_file=tests_report_file)
+    return _PrintTestResults(tests_report_file)
   finally:
     environment.GlobalTearDown()
-    if(os.path.exists(os.path.join(java_tests_src_dir, "build.xml"))):
-      os.remove(os.path.join(java_tests_src_dir, "build.xml"))
-    if(os.path.exists(os.path.join(java_tests_src_dir, "results.xml"))):
-      os.remove(os.path.join(java_tests_src_dir, "results.xml"))
+    if(os.path.exists(tests_report_file)):
+     os.remove(tests_report_file)
     if(os.path.exists(os.path.join(java_tests_src_dir,
                                    "chrome-wrapper-no-sandbox"))):
       os.remove(os.path.join(java_tests_src_dir, "chrome-wrapper-no-sandbox"))
diff --git a/chrome/test/chromedriver/util.py b/chrome/test/chromedriver/util.py
index 9d69eb4..15516ec 100644
--- a/chrome/test/chromedriver/util.py
+++ b/chrome/test/chromedriver/util.py
@@ -166,10 +166,11 @@
     os.kill(pid, signal.SIGTERM)
 
 
-def RunCommand(cmd, cwd=None):
+def RunCommand(cmd, cwd=None, fileName=None):
   """Runs the given command and returns the exit code.
 
   Args:
+    fileName: file name to redirect output
     cmd: list of command arguments.
     cwd: working directory to execute the command, or None if the current
          working directory should be used.
@@ -178,7 +179,11 @@
     The exit code of the command.
   """
   sys.stdout.flush()
-  process = subprocess.Popen(cmd, cwd=cwd)
+  if fileName is not None:
+    with open(fileName,"wb") as out:
+      process = subprocess.Popen(cmd, cwd=cwd,stdout=out,stderr=out)
+  else:
+    process = subprocess.Popen(cmd, cwd=cwd)
   process.wait()
   sys.stdout.flush()
   return process.returncode
diff --git a/chrome/test/data/webui/tab_strip/tab_list_test.js b/chrome/test/data/webui/tab_strip/tab_list_test.js
index ff9b997..c9f7456a 100644
--- a/chrome/test/data/webui/tab_strip/tab_list_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_list_test.js
@@ -147,6 +147,7 @@
 
   test('adds a new tab element when a tab is added in same window', () => {
     const appendedTab = {
+      active: false,
       alertStates: [],
       id: 3,
       index: 3,
@@ -158,6 +159,7 @@
     assertEquals(tabElements[tabs.length].tab, appendedTab);
 
     const prependedTab = {
+      active: false,
       alertStates: [],
       id: 4,
       index: 0,
@@ -211,6 +213,7 @@
 
   test('adds a pinned tab to its designated container', () => {
     webUIListenerCallback('tab-created', {
+      active: false,
       alertStates: [],
       index: 0,
       title: 'New pinned tab',
diff --git a/chrome/test/data/webui/tab_strip/tab_test.js b/chrome/test/data/webui/tab_strip/tab_test.js
index 9241cf9..bfff769 100644
--- a/chrome/test/data/webui/tab_strip/tab_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_test.js
@@ -5,6 +5,7 @@
 import 'chrome://tab-strip/tab.js';
 
 import {getFavicon} from 'chrome://resources/js/icon.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {TabStripEmbedderProxy} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
 import {TabNetworkState, TabsApiProxy} from 'chrome://tab-strip/tabs_api_proxy.js';
 
@@ -17,13 +18,22 @@
   let tabElement;
 
   const tab = {
+    active: false,
     alertStates: [],
     id: 1001,
     networkState: TabNetworkState.NONE,
     title: 'My title',
   };
 
+  const strings = {
+    closeTab: 'Close tab',
+    tabCrashed: '$1 has crashed',
+    tabNetworkError: '$1 has a network error',
+  };
+
   setup(() => {
+    loadTimeData.overrideValues(strings);
+
     document.body.innerHTML = '';
 
     testTabStripEmbedderProxy = new TestTabStripEmbedderProxy();
@@ -65,6 +75,19 @@
     assertFalse(tabElement.hasAttribute('active'));
   });
 
+  test('sets [aria-selected] attribute when active', () => {
+    tabElement.tab = Object.assign({}, tab, {active: true});
+    assertEquals(
+        'true',
+        tabElement.shadowRoot.querySelector('#tab').getAttribute(
+            'aria-selected'));
+    tabElement.tab = Object.assign({}, tab, {active: false});
+    assertEquals(
+        'false',
+        tabElement.shadowRoot.querySelector('#tab').getAttribute(
+            'aria-selected'));
+  });
+
   test('hides entire favicon container when showIcon is false', () => {
     // disable transitions
     tabElement.style.setProperty('--tabstrip-tab-transition-duration', '0ms');
@@ -158,7 +181,7 @@
       });
 
   test('clicking on the element activates the tab', () => {
-    tabElement.click();
+    tabElement.shadowRoot.querySelector('#tab').click();
     return testTabsApiProxy.whenCalled('activateTab', tabId => {
       assertEquals(tabId, tab.id);
     });
@@ -247,15 +270,14 @@
 
   test('getting the drag image grabs the contents', () => {
     assertEquals(
-        tabElement.getDragImage(),
-        tabElement.shadowRoot.querySelector('#dragImage'));
+        tabElement.getDragImage(), tabElement.shadowRoot.querySelector('#tab'));
   });
 
   test('has custom context menu', async () => {
     let event = new Event('contextmenu');
     event.clientX = 1;
     event.clientY = 2;
-    tabElement.dispatchEvent(event);
+    tabElement.shadowRoot.querySelector('#tab').dispatchEvent(event);
 
     const contextMenuArgs =
         await testTabStripEmbedderProxy.whenCalled('showTabContextMenu');
@@ -266,7 +288,28 @@
 
   test('activating closes WebUI container', () => {
     assertEquals(testTabStripEmbedderProxy.getCallCount('closeContainer'), 0);
-    tabElement.click();
+    tabElement.shadowRoot.querySelector('#tab').click();
     assertEquals(testTabStripEmbedderProxy.getCallCount('closeContainer'), 1);
   });
+
+  test('sets an accessible title', () => {
+    const titleTextElement = tabElement.shadowRoot.querySelector('#titleText');
+    assertEquals(titleTextElement.getAttribute('aria-label'), tab.title);
+
+    tabElement.tab = Object.assign({}, tab, {
+      crashed: true,
+      title: 'My tab',
+    });
+    assertEquals(
+        titleTextElement.getAttribute('aria-label'), 'My tab has crashed');
+
+    tabElement.tab = Object.assign({}, tab, {
+      crashed: false,
+      networkState: TabNetworkState.ERROR,
+      title: 'My tab',
+    });
+    assertEquals(
+        titleTextElement.getAttribute('aria-label'),
+        'My tab has a network error');
+  });
 });
diff --git a/chrome/updater/win/installer/installer.cc b/chrome/updater/win/installer/installer.cc
index 787a3da..8c24319 100644
--- a/chrome/updater/win/installer/installer.cc
+++ b/chrome/updater/win/installer/installer.cc
@@ -423,20 +423,18 @@
     return ProcessExitResult(static_cast<DWORD>(installer::TEMP_DIR_FAILED));
 
   // Unpack the compressed archive to extract the uncompressed archive file.
-  UnPackStatus unpack_status = UNPACK_NO_ERROR;
-  int32_t ntstatus = 0;
-  auto lzma_result =
+  UnPackStatus unpack_status =
       UnPackArchive(base::FilePath(compressed_archive.get()), unpack_path,
-                    nullptr, &unpack_status, &ntstatus);
-  if (lzma_result)
+                    nullptr, nullptr, nullptr);
+  if (unpack_status != UNPACK_NO_ERROR)
     return ProcessExitResult(static_cast<DWORD>(installer::UNPACKING_FAILED));
 
   // Unpack the uncompressed archive to extract the updater files.
   base::FilePath uncompressed_archive =
       unpack_path.Append(FILE_PATH_LITERAL("updater.7z"));
-  lzma_result = UnPackArchive(uncompressed_archive, unpack_path, nullptr,
-                              &unpack_status, &ntstatus);
-  if (lzma_result)
+  unpack_status = UnPackArchive(uncompressed_archive, unpack_path, nullptr,
+                                nullptr, nullptr);
+  if (unpack_status != UNPACK_NO_ERROR)
     return ProcessExitResult(static_cast<DWORD>(installer::UNPACKING_FAILED));
 
   // While unpacking the binaries, we paged in a whole bunch of memory that
diff --git a/chromecast/media/audio/external_audio_pipeline_dummy.cc b/chromecast/media/audio/external_audio_pipeline_dummy.cc
index a32c6e29..69a5340 100644
--- a/chromecast/media/audio/external_audio_pipeline_dummy.cc
+++ b/chromecast/media/audio/external_audio_pipeline_dummy.cc
@@ -43,12 +43,12 @@
 }
 
 void ExternalAudioPipelineShlib::AddExternalLoopbackAudioObserver(
-    CastMediaShlib::LoopbackAudioObserver* observer) {
+    LoopbackAudioObserver* observer) {
   NOTREACHED();
 }
 
 void ExternalAudioPipelineShlib::RemoveExternalLoopbackAudioObserver(
-    CastMediaShlib::LoopbackAudioObserver* observer) {
+    LoopbackAudioObserver* observer) {
   NOTREACHED();
 }
 
diff --git a/chromecast/media/audio/external_audio_pipeline_sample.cc b/chromecast/media/audio/external_audio_pipeline_sample.cc
index 9880055..359fd15c 100644
--- a/chromecast/media/audio/external_audio_pipeline_sample.cc
+++ b/chromecast/media/audio/external_audio_pipeline_sample.cc
@@ -34,11 +34,13 @@
     }
   }
 
-  void AddObserver(CastMediaShlib::LoopbackAudioObserver* observer) {
+  void AddObserver(
+      ExternalAudioPipelineShlib::LoopbackAudioObserver* observer) {
     observers_.push_back(observer);
   }
 
-  void RemoveObserver(CastMediaShlib::LoopbackAudioObserver* observer) {
+  void RemoveObserver(
+      ExternalAudioPipelineShlib::LoopbackAudioObserver* observer) {
     auto it = std::find(observers_.begin(), observers_.end(), observer);
     if (it != observers_.end()) {
       observers_.erase(it);
@@ -46,14 +48,14 @@
   }
 
  private:
-  std::vector<CastMediaShlib::LoopbackAudioObserver*> observers_;
+  std::vector<ExternalAudioPipelineShlib::LoopbackAudioObserver*> observers_;
 };
 
 TestLoopBack g_test_loop_back;
 
 class MixerOutputStreamTest : public MixerOutputStream {
  public:
-  MixerOutputStreamTest(TestLoopBack* test_loop_back)
+  explicit MixerOutputStreamTest(TestLoopBack* test_loop_back)
       : stream_(MixerOutputStream::Create()), test_loop_back_(test_loop_back) {
     DCHECK(test_loop_back_);
   }
@@ -104,12 +106,12 @@
 void ExternalAudioPipelineShlib::SetExternalMediaMuted(bool muted) {}
 
 void ExternalAudioPipelineShlib::AddExternalLoopbackAudioObserver(
-    CastMediaShlib::LoopbackAudioObserver* observer) {
+    LoopbackAudioObserver* observer) {
   g_test_loop_back.AddObserver(observer);
 }
 
 void ExternalAudioPipelineShlib::RemoveExternalLoopbackAudioObserver(
-    CastMediaShlib::LoopbackAudioObserver* observer) {
+    LoopbackAudioObserver* observer) {
   g_test_loop_back.RemoveObserver(observer);
 }
 
diff --git a/chromecast/media/audio/fake_external_audio_pipeline.cc b/chromecast/media/audio/fake_external_audio_pipeline.cc
index 5407a83..bd7e993a 100644
--- a/chromecast/media/audio/fake_external_audio_pipeline.cc
+++ b/chromecast/media/audio/fake_external_audio_pipeline.cc
@@ -72,12 +72,12 @@
   }
   // Called from library.
   void AddExternalLoopbackAudioObserver(
-      CastMediaShlib::LoopbackAudioObserver* observer) {
+      ExternalAudioPipelineShlib::LoopbackAudioObserver* observer) {
     observers_.push_back(observer);
   }
 
   void RemoveExternalLoopbackAudioObserver(
-      CastMediaShlib::LoopbackAudioObserver* observer) {
+      ExternalAudioPipelineShlib::LoopbackAudioObserver* observer) {
     auto it = std::find(observers_.begin(), observers_.end(), observer);
     if (it != observers_.end()) {
       observers_.erase(it);
@@ -86,7 +86,7 @@
 
  protected:
   // Used by derived class for FakeExternalAudioPipelineSupport.
-  std::vector<CastMediaShlib::LoopbackAudioObserver*> observers_;
+  std::vector<ExternalAudioPipelineShlib::LoopbackAudioObserver*> observers_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestLoopBack);
@@ -243,12 +243,12 @@
 }
 
 void ExternalAudioPipelineShlib::AddExternalLoopbackAudioObserver(
-    CastMediaShlib::LoopbackAudioObserver* observer) {
+    LoopbackAudioObserver* observer) {
   GetTestMedia()->AddExternalLoopbackAudioObserver(observer);
 }
 
 void ExternalAudioPipelineShlib::RemoveExternalLoopbackAudioObserver(
-    CastMediaShlib::LoopbackAudioObserver* observer) {
+    LoopbackAudioObserver* observer) {
   GetTestMedia()->RemoveExternalLoopbackAudioObserver(observer);
 }
 
diff --git a/chromecast/media/audio/mixer_service/BUILD.gn b/chromecast/media/audio/mixer_service/BUILD.gn
index a7225f2..44cf29f 100644
--- a/chromecast/media/audio/mixer_service/BUILD.gn
+++ b/chromecast/media/audio/mixer_service/BUILD.gn
@@ -82,6 +82,23 @@
   ]
 }
 
+cast_source_set("loopback_connection") {
+  sources = [
+    "loopback_connection.cc",
+    "loopback_connection.h",
+  ]
+
+  deps = [
+    ":common",
+    ":connection",
+    ":proto",
+    "//base",
+    "//chromecast/net:io_buffer_pool",
+    "//chromecast/public/media",
+    "//net",
+  ]
+}
+
 cast_source_set("audio_socket_service") {
   sources = [
     "audio_socket_service.cc",
diff --git a/chromecast/media/audio/mixer_service/conversions.cc b/chromecast/media/audio/mixer_service/conversions.cc
index 0a86faa..61aa17d6 100644
--- a/chromecast/media/audio/mixer_service/conversions.cc
+++ b/chromecast/media/audio/mixer_service/conversions.cc
@@ -30,6 +30,26 @@
   return kSampleFormatS16;
 }
 
+SampleFormat ConvertSampleFormat(media::SampleFormat format) {
+  switch (format) {
+    case kSampleFormatS16:
+      return SAMPLE_FORMAT_INT16_I;
+    case kSampleFormatS32:
+      return SAMPLE_FORMAT_INT32_I;
+    case kSampleFormatF32:
+      return SAMPLE_FORMAT_FLOAT_I;
+    case kSampleFormatPlanarS16:
+      return SAMPLE_FORMAT_INT16_P;
+    case kSampleFormatPlanarF32:
+      return SAMPLE_FORMAT_INT32_P;
+    case kSampleFormatPlanarS32:
+      return SAMPLE_FORMAT_FLOAT_P;
+    default:
+      NOTREACHED() << "Unhandled sample format " << format;
+  }
+  return SAMPLE_FORMAT_INT16_I;
+}
+
 int GetSampleSizeBytes(mixer_service::SampleFormat format) {
   if (format == SAMPLE_FORMAT_INT16_I || format == SAMPLE_FORMAT_INT16_P) {
     return 2;
diff --git a/chromecast/media/audio/mixer_service/conversions.h b/chromecast/media/audio/mixer_service/conversions.h
index 764ec30..5031012 100644
--- a/chromecast/media/audio/mixer_service/conversions.h
+++ b/chromecast/media/audio/mixer_service/conversions.h
@@ -14,6 +14,7 @@
 namespace mixer_service {
 
 media::SampleFormat ConvertSampleFormat(SampleFormat format);
+SampleFormat ConvertSampleFormat(media::SampleFormat format);
 int GetSampleSizeBytes(SampleFormat format);
 
 ContentType ConvertContentType(media::AudioContentType content_type);
diff --git a/chromecast/media/audio/mixer_service/loopback_connection.cc b/chromecast/media/audio/mixer_service/loopback_connection.cc
new file mode 100644
index 0000000..5bd6a71
--- /dev/null
+++ b/chromecast/media/audio/mixer_service/loopback_connection.cc
@@ -0,0 +1,86 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/audio/mixer_service/loopback_connection.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "base/logging.h"
+#include "chromecast/media/audio/mixer_service/conversions.h"
+#include "chromecast/media/audio/mixer_service/mixer_service.pb.h"
+#include "chromecast/net/io_buffer_pool.h"
+#include "net/socket/stream_socket.h"
+
+namespace chromecast {
+namespace media {
+namespace mixer_service {
+
+LoopbackConnection::LoopbackConnection(Delegate* delegate)
+    : LoopbackConnection(delegate, nullptr) {}
+
+LoopbackConnection::LoopbackConnection(
+    Delegate* delegate,
+    std::unique_ptr<MixerSocket> connected_socket_for_test)
+    : delegate_(delegate), socket_(std::move(connected_socket_for_test)) {
+  DCHECK(delegate_);
+}
+
+LoopbackConnection::~LoopbackConnection() = default;
+
+void LoopbackConnection::Connect() {
+  if (socket_) {
+    OnConnected(std::move(socket_));
+    return;
+  }
+  MixerConnection::Connect();
+}
+
+void LoopbackConnection::OnConnected(std::unique_ptr<MixerSocket> socket) {
+  format_ = kUnknownSampleFormat;
+  sample_rate_ = 0;
+  num_channels_ = 0;
+
+  socket_ = std::move(socket);
+  socket_->SetDelegate(this);
+
+  Generic message;
+  message.mutable_loopback_request();
+  socket_->SendProto(message);
+}
+
+void LoopbackConnection::OnConnectionError() {
+  delegate_->OnLoopbackInterrupted();
+  socket_.reset();
+  MixerConnection::Connect();
+}
+
+bool LoopbackConnection::HandleMetadata(const Generic& message) {
+  if (message.has_stream_config()) {
+    format_ = ConvertSampleFormat(message.stream_config().sample_format());
+    sample_rate_ = message.stream_config().sample_rate();
+    num_channels_ = message.stream_config().num_channels();
+  }
+  return true;
+}
+
+bool LoopbackConnection::HandleAudioData(char* data,
+                                         int size,
+                                         int64_t timestamp) {
+  if (size == 0) {
+    delegate_->OnLoopbackInterrupted();
+    return true;
+  }
+
+  if (format_ != kUnknownSampleFormat) {
+    delegate_->OnLoopbackAudio(timestamp, format_, sample_rate_, num_channels_,
+                               reinterpret_cast<uint8_t*>(data), size);
+  }
+  return true;
+}
+
+}  // namespace mixer_service
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/audio/mixer_service/loopback_connection.h b/chromecast/media/audio/mixer_service/loopback_connection.h
new file mode 100644
index 0000000..03e4d3cc4
--- /dev/null
+++ b/chromecast/media/audio/mixer_service/loopback_connection.h
@@ -0,0 +1,85 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_MEDIA_AUDIO_MIXER_SERVICE_LOOPBACK_CONNECTION_H_
+#define CHROMECAST_MEDIA_AUDIO_MIXER_SERVICE_LOOPBACK_CONNECTION_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "chromecast/media/audio/mixer_service/mixer_connection.h"
+#include "chromecast/media/audio/mixer_service/mixer_socket.h"
+#include "chromecast/public/media/decoder_config.h"
+
+namespace chromecast {
+namespace media {
+namespace mixer_service {
+class Generic;
+
+// Connection for observing loopback audio data from the mixer. Must be created
+// and used on an IO thread.
+class LoopbackConnection : public MixerConnection,
+                           public MixerSocket::Delegate {
+ public:
+  // Observer for audio loopback data.
+  class Delegate {
+   public:
+    // Called whenever loopback audio data is available. The |timestamp| is the
+    // estimated time in microseconds (relative to the audio clock) that
+    // the audio will actually be output. |length| is the length of the audio
+    // |data| in bytes. The format of the data is given by |sample_format| and
+    // |num_channels|.
+    virtual void OnLoopbackAudio(int64_t timestamp,
+                                 media::SampleFormat sample_format,
+                                 int sample_rate,
+                                 int num_channels,
+                                 uint8_t* data,
+                                 int length) = 0;
+
+    // Called if the loopback data is not continuous (ie, does not accurately
+    // represent the actual output) for any reason. For example, if there is an
+    // output underflow, or if output is disabled due to no output streams.
+    virtual void OnLoopbackInterrupted() = 0;
+
+   protected:
+    virtual ~Delegate() = default;
+  };
+
+  explicit LoopbackConnection(Delegate* delegate);
+  // For testing only.
+  LoopbackConnection(Delegate* delegate,
+                     std::unique_ptr<MixerSocket> connected_socket_for_test);
+  ~LoopbackConnection() override;
+
+  // Initiates connection to the mixer service. Delegate methods can be called
+  // at any point after Connect() is called, until this is destroyed.
+  void Connect();
+
+ private:
+  // MixerConnection implementation:
+  void OnConnected(std::unique_ptr<MixerSocket> socket) override;
+  void OnConnectionError() override;
+
+  // MixerSocket::Delegate implementation:
+  bool HandleMetadata(const Generic& message) override;
+  bool HandleAudioData(char* data, int size, int64_t timestamp) override;
+
+  Delegate* const delegate_;
+
+  std::unique_ptr<MixerSocket> socket_;
+
+  media::SampleFormat format_ = kUnknownSampleFormat;
+  int sample_rate_ = 0;
+  int num_channels_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(LoopbackConnection);
+};
+
+}  // namespace mixer_service
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_AUDIO_MIXER_SERVICE_LOOPBACK_CONNECTION_H_
diff --git a/chromecast/media/audio/mixer_service/mixer_service.proto b/chromecast/media/audio/mixer_service/mixer_service.proto
index ee5d191e..c1cab82f 100644
--- a/chromecast/media/audio/mixer_service/mixer_service.proto
+++ b/chromecast/media/audio/mixer_service/mixer_service.proto
@@ -103,6 +103,7 @@
   optional SampleFormat sample_format = 1;
   optional int32 sample_rate = 2;
   optional int32 num_channels = 3;
+  optional int32 data_size = 4;  // Expected size of audio data in bytes.
 }
 
 // Sent to indicate that the mixer should stream loopback audio data back over
diff --git a/chromecast/media/audio/mixer_service/mixer_socket.h b/chromecast/media/audio/mixer_service/mixer_socket.h
index 360cd7e..5d9d81d 100644
--- a/chromecast/media/audio/mixer_service/mixer_socket.h
+++ b/chromecast/media/audio/mixer_service/mixer_socket.h
@@ -74,6 +74,13 @@
   explicit MixerSocket(std::unique_ptr<net::StreamSocket> socket);
   ~MixerSocket() override;
 
+  // Used to create local (in-process) connections.
+  MixerSocket();
+  void SetLocalCounterpart(
+      base::WeakPtr<MixerSocket> local_counterpart,
+      scoped_refptr<base::SequencedTaskRunner> counterpart_task_runner);
+  base::WeakPtr<MixerSocket> GetWeakPtr();
+
   // Sets/changes the delegate. Must be called immediately after creation
   // (ie, synchronously on the same sequence).
   void SetDelegate(Delegate* delegate);
@@ -114,16 +121,6 @@
   void ReceiveMoreMessages();
 
  private:
-  friend class Receiver;
-
-  // Used by Receiver to create in-process mixer connections.
-  MixerSocket();
-
-  void SetLocalCounterpart(
-      base::WeakPtr<MixerSocket> local_counterpart,
-      scoped_refptr<base::SequencedTaskRunner> counterpart_task_runner);
-  base::WeakPtr<MixerSocket> GetWeakPtr();
-
   void SendBuffer(scoped_refptr<net::IOBuffer> buffer, int buffer_size);
 
   // SmallMessageSocket::Delegate implementation:
diff --git a/chromecast/media/audio/mixer_service/receiver/receiver.cc b/chromecast/media/audio/mixer_service/receiver/receiver.cc
index 3cf93a6..46c62c8 100644
--- a/chromecast/media/audio/mixer_service/receiver/receiver.cc
+++ b/chromecast/media/audio/mixer_service/receiver/receiver.cc
@@ -141,8 +141,8 @@
 }
 
 std::unique_ptr<MixerSocket> Receiver::LocalConnect() {
-  std::unique_ptr<MixerSocket> receiver_socket(new MixerSocket);
-  std::unique_ptr<MixerSocket> caller_socket(new MixerSocket);
+  auto receiver_socket = std::make_unique<MixerSocket>();
+  auto caller_socket = std::make_unique<MixerSocket>();
 
   receiver_socket->SetLocalCounterpart(caller_socket->GetWeakPtr(),
                                        base::SequencedTaskRunnerHandle::Get());
diff --git a/chromecast/media/audio/mixer_service/receiver/receiver_cma.cc b/chromecast/media/audio/mixer_service/receiver/receiver_cma.cc
index be4e025..d8cbc89 100644
--- a/chromecast/media/audio/mixer_service/receiver/receiver_cma.cc
+++ b/chromecast/media/audio/mixer_service/receiver/receiver_cma.cc
@@ -24,6 +24,27 @@
 constexpr base::TimeDelta kInactivityTimeout = base::TimeDelta::FromSeconds(5);
 }  // namespace
 
+class ReceiverCma::UnusedSocket : public MixerSocket::Delegate {
+ public:
+  UnusedSocket(ReceiverCma* receiver, std::unique_ptr<MixerSocket> socket)
+      : receiver_(receiver), socket_(std::move(socket)) {
+    DCHECK(receiver_);
+    DCHECK(socket_);
+    socket_->SetDelegate(this);
+  }
+
+  ~UnusedSocket() override = default;
+
+ private:
+  // MixerSocket::Delegate implementation:
+  void OnConnectionError() override { receiver_->RemoveUnusedSocket(this); }
+
+  ReceiverCma* const receiver_;
+  const std::unique_ptr<MixerSocket> socket_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnusedSocket);
+};
+
 class ReceiverCma::Stream : public MixerSocket::Delegate,
                             public CmaBackendShim::Delegate {
  public:
@@ -152,6 +173,8 @@
   base::TimeTicks last_receive_time_;
 
   base::WeakPtrFactory<Stream> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(Stream);
 };
 
 ReceiverCma::ReceiverCma(MediaPipelineBackendManager* backend_manager)
@@ -172,22 +195,35 @@
 void ReceiverCma::CreateLoopbackConnection(std::unique_ptr<MixerSocket> socket,
                                            const Generic& message) {
   LOG(INFO) << "Unhandled loopback connection";
+  AddUnusedSocket(std::move(socket));
 }
 
 void ReceiverCma::CreateAudioRedirection(std::unique_ptr<MixerSocket> socket,
                                          const Generic& message) {
   LOG(INFO) << "Unhandled redirection connection";
+  AddUnusedSocket(std::move(socket));
 }
 
 void ReceiverCma::CreateControlConnection(std::unique_ptr<MixerSocket> socket,
                                           const Generic& message) {
   LOG(INFO) << "Unhandled control connection";
+  AddUnusedSocket(std::move(socket));
 }
 
 void ReceiverCma::RemoveStream(Stream* stream) {
   streams_.erase(stream);
 }
 
+void ReceiverCma::AddUnusedSocket(std::unique_ptr<MixerSocket> socket) {
+  auto unused_socket = std::make_unique<UnusedSocket>(this, std::move(socket));
+  UnusedSocket* ptr = unused_socket.get();
+  unused_sockets_[ptr] = std::move(unused_socket);
+}
+
+void ReceiverCma::RemoveUnusedSocket(UnusedSocket* unused_socket) {
+  unused_sockets_.erase(unused_socket);
+}
+
 }  // namespace mixer_service
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/media/audio/mixer_service/receiver/receiver_cma.h b/chromecast/media/audio/mixer_service/receiver/receiver_cma.h
index c123225c..9423fac 100644
--- a/chromecast/media/audio/mixer_service/receiver/receiver_cma.h
+++ b/chromecast/media/audio/mixer_service/receiver/receiver_cma.h
@@ -30,6 +30,7 @@
 
  private:
   class Stream;
+  class UnusedSocket;
 
   // Receiver implementation:
   void CreateOutputStream(std::unique_ptr<MixerSocket> socket,
@@ -43,9 +44,13 @@
 
   void RemoveStream(Stream* stream);
 
+  void AddUnusedSocket(std::unique_ptr<MixerSocket> socket);
+  void RemoveUnusedSocket(UnusedSocket* unused_socket);
+
   MediaPipelineBackendManager* const backend_manager_;
 
   base::flat_map<Stream*, std::unique_ptr<Stream>> streams_;
+  base::flat_map<UnusedSocket*, std::unique_ptr<UnusedSocket>> unused_sockets_;
 
   DISALLOW_COPY_AND_ASSIGN(ReceiverCma);
 };
diff --git a/chromecast/media/cma/backend/cast_media_shlib_mixer_audio.cc b/chromecast/media/cma/backend/cast_media_shlib_mixer_audio.cc
index a191376..3fbd429d4 100644
--- a/chromecast/media/cma/backend/cast_media_shlib_mixer_audio.cc
+++ b/chromecast/media/cma/backend/cast_media_shlib_mixer_audio.cc
@@ -14,15 +14,6 @@
 namespace chromecast {
 namespace media {
 
-void CastMediaShlib::AddLoopbackAudioObserver(LoopbackAudioObserver* observer) {
-  StreamMixer::Get()->AddLoopbackAudioObserver(observer);
-}
-
-void CastMediaShlib::RemoveLoopbackAudioObserver(
-    LoopbackAudioObserver* observer) {
-  StreamMixer::Get()->RemoveLoopbackAudioObserver(observer);
-}
-
 void CastMediaShlib::ResetPostProcessors(CastMediaShlib::ResultCallback cb) {
   StreamMixer::Get()->ResetPostProcessors(std::move(cb));
 }
diff --git a/chromecast/media/cma/backend/mixer/BUILD.gn b/chromecast/media/cma/backend/mixer/BUILD.gn
index ee8e334..c882ec3 100644
--- a/chromecast/media/cma/backend/mixer/BUILD.gn
+++ b/chromecast/media/cma/backend/mixer/BUILD.gn
@@ -29,6 +29,24 @@
   ]
 }
 
+cast_source_set("loopback") {
+  sources = [
+    "loopback_handler.cc",
+    "loopback_handler.h",
+    "mixer_loopback_connection.cc",
+    "mixer_loopback_connection.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chromecast/media/audio:libcast_external_audio_pipeline_1.0",
+    "//chromecast/media/audio/mixer_service:common",
+    "//chromecast/media/audio/mixer_service:proto",
+    "//chromecast/net:io_buffer_pool",
+    "//chromecast/public/media",
+  ]
+}
+
 cast_source_set("mixer") {
   sources = [
     "audio_output_redirector.cc",
@@ -38,8 +56,6 @@
     "direct_mixer_source.h",
     "filter_group.cc",
     "filter_group.h",
-    "loopback_handler.cc",
-    "loopback_handler.h",
     "mixer_input.cc",
     "mixer_input.h",
     "mixer_input_connection.cc",
@@ -60,6 +76,7 @@
   ]
 
   deps = [
+    ":loopback",
     ":mixer_control",
     ":post_processor_paths",
     "//base",
@@ -101,6 +118,21 @@
   }
 }
 
+cast_source_set("loopback_test_support") {
+  testonly = true
+  sources = [
+    "loopback_test_support.cc",
+    "loopback_test_support.h",
+  ]
+
+  deps = [
+    ":loopback",
+    "//base",
+    "//chromecast/media/audio/mixer_service:common",
+    "//chromecast/net:test_support",
+  ]
+}
+
 cast_source_set("unittests") {
   testonly = true
   sources = [
@@ -116,14 +148,19 @@
   ]
 
   deps = [
+    ":loopback",
+    ":loopback_test_support",
     ":mixer",
     "//base",
     "//chromecast/media/audio:fake_external_audio_pipeline",
+    "//chromecast/media/audio/mixer_service:common",
+    "//chromecast/media/audio/mixer_service:loopback_connection",
     "//chromecast/media/cma/backend/mixer/post_processors:unittests",
     "//chromecast/public",
     "//chromecast/public/media",
     "//media",
     "//media:shared_memory_support",
+    "//net",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/chromecast/media/cma/backend/mixer/loopback_handler.cc b/chromecast/media/cma/backend/mixer/loopback_handler.cc
index bc60460..5c8e25a 100644
--- a/chromecast/media/cma/backend/mixer/loopback_handler.cc
+++ b/chromecast/media/cma/backend/mixer/loopback_handler.cc
@@ -4,329 +4,234 @@
 
 #include "chromecast/media/cma/backend/mixer/loopback_handler.h"
 
-#include <algorithm>
+#include <limits>
 #include <utility>
-#include <vector>
 
 #include "base/bind.h"
-#include "base/containers/flat_set.h"
+#include "base/containers/flat_map.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/condition_variable.h"
+#include "base/sequenced_task_runner.h"
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
-#include "base/threading/thread.h"
+#include "chromecast/media/cma/backend/mixer/mixer_loopback_connection.h"
+#include "chromecast/net/io_buffer_pool.h"
 #include "chromecast/public/media/external_audio_pipeline_shlib.h"
 
 namespace chromecast {
 namespace media {
 
-namespace {
-
-const int kDefaultBufferSize = 1024 * 2 * sizeof(float);
-const int kNumBuffers = 16;
-const int kMaxTasks = kNumBuffers + 1;
-
-class LoopbackHandlerImpl : public LoopbackHandler,
-                            public CastMediaShlib::LoopbackAudioObserver {
+class LoopbackHandler::LoopbackIO {
  public:
-  explicit LoopbackHandlerImpl(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-      : external_audio_pipeline_supported_(
-            ExternalAudioPipelineShlib::IsSupported()),
-        task_signal_(&lock_) {
-    CreateBuffersIfNeeded(kDefaultBufferSize);
+  LoopbackIO() = default;
+  ~LoopbackIO() = default;
 
-    if (task_runner) {
-      task_runner_ = std::move(task_runner);
-    } else {
-      thread_ = std::make_unique<base::Thread>("CMA loopback");
-      base::Thread::Options options;
-      options.priority = base::ThreadPriority::REALTIME_AUDIO;
-      thread_->StartWithOptions(options);
-      task_runner_ = thread_->task_runner();
-
-      task_runner_->PostTask(
-          FROM_HERE, base::BindOnce(&LoopbackHandlerImpl::LoopbackTaskLoop,
-                                    base::Unretained(this)));
+  void AddConnection(std::unique_ptr<MixerLoopbackConnection> connection) {
+    MixerLoopbackConnection* ptr = connection.get();
+    connections_[ptr] = std::move(connection);
+    ptr->SetErrorCallback(base::BindOnce(&LoopbackIO::RemoveConnection,
+                                         base::Unretained(this), ptr));
+    if (sample_format_ != kUnknownSampleFormat) {
+      ptr->SetStreamConfig(sample_format_, sample_rate_, num_channels_,
+                           data_size_);
     }
+  }
 
-    if (external_audio_pipeline_supported_) {
-      ExternalAudioPipelineShlib::AddExternalLoopbackAudioObserver(this);
+  void SetStreamConfig(SampleFormat sample_format,
+                       int sample_rate,
+                       int num_channels,
+                       int data_size) {
+    sample_format_ = sample_format;
+    sample_rate_ = sample_rate;
+    num_channels_ = num_channels;
+    data_size_ = data_size;
+
+    for (const auto& c : connections_) {
+      c.second->SetStreamConfig(sample_format_, sample_rate_, num_channels_,
+                                data_size_);
+    }
+  }
+
+  void SendData(scoped_refptr<net::IOBuffer> audio_buffer,
+                int data_size_bytes,
+                int64_t timestamp) {
+    for (const auto& c : connections_) {
+      c.second->SendAudio(audio_buffer, data_size_bytes, timestamp);
     }
   }
 
  private:
-  struct Task {
-    Task(int64_t expected_playback_time,
-         SampleFormat format,
-         int sample_rate,
-         int channels,
-         uint32_t tag,
-         std::unique_ptr<uint8_t[]> data,
-         int length)
-        : expected_playback_time(expected_playback_time),
-          format(format),
-          sample_rate(sample_rate),
-          channels(channels),
-          tag(tag),
-          data(std::move(data)),
-          length(length) {}
+  void RemoveConnection(MixerLoopbackConnection* connection) {
+    connections_.erase(connection);
+  }
 
-    const int64_t expected_playback_time;
-    const SampleFormat format;
-    const int sample_rate;
-    const int channels;
-    const uint32_t tag;
-    std::unique_ptr<uint8_t[]> data;
-    const int length;
-  };
+  base::flat_map<MixerLoopbackConnection*,
+                 std::unique_ptr<MixerLoopbackConnection>>
+      connections_;
 
-  ~LoopbackHandlerImpl() override {
+  SampleFormat sample_format_ = kUnknownSampleFormat;
+  int sample_rate_ = 0;
+  int num_channels_ = 0;
+  int data_size_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(LoopbackIO);
+};
+
+class LoopbackHandler::ExternalLoopbackHandler
+    : public ExternalAudioPipelineShlib::LoopbackAudioObserver {
+ public:
+  explicit ExternalLoopbackHandler(LoopbackHandler* owner) : owner_(owner) {
+    DCHECK(owner_);
+    ExternalAudioPipelineShlib::AddExternalLoopbackAudioObserver(this);
+  }
+
+  void Destroy() {
     {
       base::AutoLock lock(lock_);
-      stop_thread_ = true;
-    }
-    task_signal_.Signal();
-    if (thread_) {
-      thread_->Stop();
-    }
-  }
-
-  // LoopbackHandler implementation:
-  void Destroy() override {
-    if (external_audio_pipeline_supported_) {
-      ExternalAudioPipelineShlib::RemoveExternalLoopbackAudioObserver(this);
-    } else {
-      delete this;
-    }
-  }
-
-  void SetDataSize(int data_size_bytes) override {
-    CreateBuffersIfNeeded(data_size_bytes);
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override {
-    return task_runner_;
-  }
-
-  void AddObserver(CastMediaShlib::LoopbackAudioObserver* observer) override {
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&LoopbackHandlerImpl::AddObserverOnThread,
-                                  base::Unretained(this), observer));
-    task_signal_.Signal();
-  }
-
-  void RemoveObserver(
-      CastMediaShlib::LoopbackAudioObserver* observer) override {
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&LoopbackHandlerImpl::RemoveObserverOnThread,
-                                  base::Unretained(this), observer));
-    task_signal_.Signal();
-  }
-
-  void SendData(int64_t timestamp,
-                int sample_rate,
-                int num_channels,
-                float* data,
-                int frames) override {
-    if (external_audio_pipeline_supported_) {
-      return;
+      destroyed_ = true;
     }
 
-    SendLoopbackData(timestamp, kSampleFormatF32, sample_rate, num_channels,
-                     reinterpret_cast<uint8_t*>(data),
-                     frames * num_channels * sizeof(float));
+    ExternalAudioPipelineShlib::RemoveExternalLoopbackAudioObserver(this);
   }
 
-  void SendInterrupt() override {
-    base::AutoLock lock(lock_);
-    SendInterruptedLocked();
-  }
+ private:
+  ~ExternalLoopbackHandler() override = default;
 
-  // CastMediaShlib::LoopbackAudioObserver implementation:
+  // ExternalAudioPipelineShlib::LoopbackAudioObserver implementation:
   void OnLoopbackAudio(int64_t timestamp,
                        SampleFormat format,
                        int sample_rate,
                        int num_channels,
                        uint8_t* data,
                        int length) override {
-    SendLoopbackData(timestamp, format, sample_rate, num_channels, data,
-                     length);
+    base::AutoLock lock(lock_);
+    if (!destroyed_) {
+      owner_->SendDataInternal(timestamp, format, sample_rate, num_channels,
+                               data, length);
+    }
   }
 
-  void OnLoopbackInterrupted() override { SendInterrupt(); }
+  void OnLoopbackInterrupted() override {
+    base::AutoLock lock(lock_);
+    if (!destroyed_) {
+      owner_->SendInterruptInternal();
+    }
+  }
 
   void OnRemoved() override {
-    // We expect that external pipeline will not invoke any other callbacks
-    // after this one.
     delete this;
-    // No need to pipe this, LoopbackHandlerImpl will let the other observer
-    // know when it's being removed.
   }
 
-  void AddObserverOnThread(CastMediaShlib::LoopbackAudioObserver* observer) {
-    DCHECK(task_runner_->BelongsToCurrentThread());
-    LOG(INFO) << __func__;
-    DCHECK(observer);
-
-    observers_.insert(observer);
-  }
-
-  void RemoveObserverOnThread(CastMediaShlib::LoopbackAudioObserver* observer) {
-    DCHECK(task_runner_->BelongsToCurrentThread());
-    LOG(INFO) << __func__;
-    DCHECK(observer);
-
-    observers_.erase(observer);
-    observer->OnRemoved();
-  }
-
-  void CreateBuffersIfNeeded(int buffer_size_bytes) {
-    if (buffer_size_bytes <= buffer_size_) {
-      return;
-    }
-    LOG(INFO) << "Create new buffers, size = " << buffer_size_bytes;
-
-    base::AutoLock lock(lock_);
-    ++buffer_tag_;
-    buffers_.clear();
-    for (int i = 0; i < kNumBuffers; ++i) {
-      auto buffer = std::make_unique<uint8_t[]>(buffer_size_bytes);
-      std::fill_n(buffer.get(), buffer_size_bytes, 0);
-      buffers_.push_back(std::move(buffer));
-    }
-    buffer_size_ = buffer_size_bytes;
-
-    tasks_.reserve(kMaxTasks);
-    new_tasks_.reserve(kMaxTasks);
-  }
-
-  void SendLoopbackData(int64_t timestamp,
-                        SampleFormat format,
-                        int sample_rate,
-                        int num_channels,
-                        uint8_t* data,
-                        int length) {
-    CreateBuffersIfNeeded(length);
-
-    {
-      base::AutoLock lock(lock_);
-
-      if (buffers_.empty() || tasks_.size() >= kNumBuffers) {
-        LOG(ERROR) << "Can't send loopback data";
-        SendInterruptedLocked();
-        return;
-      }
-
-      std::unique_ptr<uint8_t[]> buffer = std::move(buffers_.back());
-      buffers_.pop_back();
-
-      std::copy(data, data + length, buffer.get());
-      tasks_.emplace_back(timestamp, format, sample_rate, num_channels,
-                          buffer_tag_, std::move(buffer), length);
-    }
-
-    if (thread_) {
-      task_signal_.Signal();
-    } else {
-      HandleLoopbackTask(&tasks_.back());
-      tasks_.pop_back();
-    }
-  }
-
-  void SendInterruptedLocked() {
-    lock_.AssertAcquired();
-
-    if (tasks_.size() == kMaxTasks) {
-      task_signal_.Signal();
-      return;
-    }
-    tasks_.emplace_back(0, kSampleFormatF32, 0, 0, 0, nullptr, 0);
-
-    if (thread_) {
-      task_signal_.Signal();
-    } else {
-      HandleLoopbackTask(&tasks_.back());
-      tasks_.pop_back();
-    }
-  }
-
-  void LoopbackTaskLoop() {
-    DCHECK(task_runner_->BelongsToCurrentThread());
-
-    {
-      base::AutoLock lock(lock_);
-      if (stop_thread_)
-        return;
-      if (tasks_.empty()) {
-        task_signal_.Wait();
-      }
-      new_tasks_.swap(tasks_);
-    }
-
-    for (auto& task : new_tasks_) {
-      HandleLoopbackTask(&task);
-    }
-    new_tasks_.clear();
-
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&LoopbackHandlerImpl::LoopbackTaskLoop,
-                                  base::Unretained(this)));
-  }
-
-  void HandleLoopbackTask(Task* task) {
-    if (!task->data) {
-      for (auto* observer : observers_) {
-        observer->OnLoopbackInterrupted();
-      }
-      return;
-    }
-
-    for (auto* observer : observers_) {
-      observer->OnLoopbackAudio(task->expected_playback_time, task->format,
-                                task->sample_rate, task->channels,
-                                task->data.get(), task->length);
-    }
-
-    base::AutoLock lock(lock_);
-    if (task->tag == buffer_tag_) {
-      // Only return the buffer if the tag matches. Otherwise, the buffer size
-      // may have changed (so we should just delete the buffer).
-      buffers_.push_back(std::move(task->data));
-    }
-  }
-
-  const bool external_audio_pipeline_supported_;
-
-  std::unique_ptr<base::Thread> thread_;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
-  int buffer_size_ = 0;
+  LoopbackHandler* const owner_;
 
   base::Lock lock_;
-  uint32_t buffer_tag_ GUARDED_BY(lock_) = 0;
-  std::vector<std::unique_ptr<uint8_t[]>> buffers_ GUARDED_BY(lock_);
-  bool stop_thread_ GUARDED_BY(lock_) = false;
-  base::ConditionVariable task_signal_;
+  bool destroyed_ GUARDED_BY(lock_) = false;
 
-  std::vector<Task> tasks_;
-  std::vector<Task> new_tasks_;
-
-  base::flat_set<CastMediaShlib::LoopbackAudioObserver*> observers_;
-
-  DISALLOW_COPY_AND_ASSIGN(LoopbackHandlerImpl);
+  DISALLOW_COPY_AND_ASSIGN(ExternalLoopbackHandler);
 };
 
-}  // namespace
+void LoopbackHandler::ExternalDeleter::operator()(
+    ExternalLoopbackHandler* obj) {
+  obj->Destroy();
+}
 
-// static
-std::unique_ptr<LoopbackHandler, LoopbackHandler::Deleter>
-LoopbackHandler::Create(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  return std::unique_ptr<LoopbackHandler, LoopbackHandler::Deleter>(
-      new LoopbackHandlerImpl(std::move(task_runner)));
+LoopbackHandler::LoopbackHandler(
+    scoped_refptr<base::SequencedTaskRunner> io_task_runner)
+    : LoopbackHandler(std::move(io_task_runner),
+                      ExternalAudioPipelineShlib::IsSupported()) {}
+
+LoopbackHandler::LoopbackHandler(
+    scoped_refptr<base::SequencedTaskRunner> io_task_runner,
+    bool use_external_audio_pipeline)
+    : io_(std::move(io_task_runner)) {
+  if (use_external_audio_pipeline) {
+    external_handler_.reset(new ExternalLoopbackHandler(this));
+  }
+}
+
+LoopbackHandler::~LoopbackHandler() = default;
+
+void LoopbackHandler::AddConnection(
+    std::unique_ptr<MixerLoopbackConnection> connection) {
+  io_.Post(FROM_HERE, &LoopbackIO::AddConnection, std::move(connection));
+}
+
+void LoopbackHandler::SetDataSize(int data_size_bytes) {
+  if (external_handler_) {
+    return;
+  }
+
+  if (SetDataSizeInternal(data_size_bytes) && sample_rate_ != 0) {
+    io_.Post(FROM_HERE, &LoopbackIO::SetStreamConfig, format_, sample_rate_,
+             num_channels_, data_size_);
+  }
+}
+
+void LoopbackHandler::SendData(int64_t timestamp,
+                               int sample_rate,
+                               int num_channels,
+                               float* data,
+                               int frames) {
+  if (external_handler_) {
+    return;
+  }
+
+  int data_size_bytes = frames * num_channels * sizeof(float);
+  SendDataInternal(timestamp, kSampleFormatF32, sample_rate, num_channels, data,
+                   data_size_bytes);
+}
+
+void LoopbackHandler::SendInterrupt() {
+  if (external_handler_) {
+    return;
+  }
+  SendInterruptInternal();
+}
+
+bool LoopbackHandler::SetDataSizeInternal(int data_size_bytes) {
+  if (buffer_pool_ && data_size_ >= data_size_bytes) {
+    return false;
+  }
+
+  data_size_ = data_size_bytes;
+  buffer_pool_ = base::MakeRefCounted<IOBufferPool>(
+      data_size_ + mixer_service::MixerSocket::kAudioMessageHeaderSize,
+      std::numeric_limits<size_t>::max(), true /* threadsafe */);
+  buffer_pool_->Preallocate(4);
+  return true;
+}
+
+void LoopbackHandler::SendDataInternal(int64_t timestamp,
+                                       SampleFormat format,
+                                       int sample_rate,
+                                       int num_channels,
+                                       void* data,
+                                       int data_size_bytes) {
+  bool data_size_changed = SetDataSizeInternal(data_size_bytes);
+  if (format != format_ || sample_rate != sample_rate_ ||
+      num_channels != num_channels_ || data_size_changed) {
+    format_ = format;
+    sample_rate_ = sample_rate;
+    num_channels_ = num_channels;
+    io_.Post(FROM_HERE, &LoopbackIO::SetStreamConfig, format_, sample_rate_,
+             num_channels_, data_size_);
+  }
+
+  DCHECK_LE(data_size_bytes, data_size_);
+  DCHECK(buffer_pool_);
+  auto buffer = buffer_pool_->GetBuffer();
+  memcpy(buffer->data() + mixer_service::MixerSocket::kAudioMessageHeaderSize,
+         data, data_size_bytes);
+  io_.Post(FROM_HERE, &LoopbackIO::SendData, std::move(buffer), data_size_bytes,
+           timestamp);
+}
+
+void LoopbackHandler::SendInterruptInternal() {
+  if (!buffer_pool_) {
+    return;
+  }
+
+  io_.Post(FROM_HERE, &LoopbackIO::SendData, buffer_pool_->GetBuffer(), 0, 0);
 }
 
 }  // namespace media
diff --git a/chromecast/media/cma/backend/mixer/loopback_handler.h b/chromecast/media/cma/backend/mixer/loopback_handler.h
index 9206e17..d02a1fa 100644
--- a/chromecast/media/cma/backend/mixer/loopback_handler.h
+++ b/chromecast/media/cma/backend/mixer/loopback_handler.h
@@ -8,45 +8,74 @@
 #include <cstdint>
 #include <memory>
 
+#include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
-#include "chromecast/public/cast_media_shlib.h"
+#include "base/threading/sequence_bound.h"
+#include "chromecast/public/media/decoder_config.h"
 
 namespace base {
-class SingleThreadTaskRunner;
+class SequencedTaskRunner;
 }  // namespace base
 
 namespace chromecast {
-namespace media {
+class IOBufferPool;
 
-// Handles loopback audio from the mixer.
+namespace media {
+class MixerLoopbackConnection;
+
+// Handles loopback audio from the mixer or external audio pipeline.
 class LoopbackHandler {
  public:
-  struct Deleter {
-    void operator()(LoopbackHandler* obj) { obj->Destroy(); }
+  explicit LoopbackHandler(
+      scoped_refptr<base::SequencedTaskRunner> io_task_runner);
+  LoopbackHandler(scoped_refptr<base::SequencedTaskRunner> io_task_runner,
+                  bool use_external_audio_pipeline);
+  ~LoopbackHandler();
+
+  // Adds a new loopback connection.
+  void AddConnection(std::unique_ptr<MixerLoopbackConnection> connection);
+
+  // Sets the expected size of audio data (in bytes) of audio data from the
+  // mixer.
+  void SetDataSize(int data_size_bytes);
+
+  // Passes loopback data from the mixer to any connected observers.
+  void SendData(int64_t timestamp,
+                int sample_rate,
+                int num_channels,
+                float* data,
+                int frames);
+
+  // Sends a 'loopback interrupted' signal to any connected observers.
+  void SendInterrupt();
+
+ private:
+  class ExternalLoopbackHandler;
+  struct ExternalDeleter {
+    void operator()(ExternalLoopbackHandler* obj);
   };
+  class LoopbackIO;
 
-  static std::unique_ptr<LoopbackHandler, Deleter> Create(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-
-  virtual void Destroy() = 0;
-
-  virtual void SetDataSize(int data_size_bytes) = 0;
-
-  virtual scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() = 0;
-
-  virtual void AddObserver(CastMediaShlib::LoopbackAudioObserver* observer) = 0;
-  virtual void RemoveObserver(
-      CastMediaShlib::LoopbackAudioObserver* observer) = 0;
-
-  virtual void SendData(int64_t timestamp,
+  bool SetDataSizeInternal(int data_size_bytes);
+  void SendDataInternal(int64_t timestamp,
+                        SampleFormat format,
                         int sample_rate,
                         int num_channels,
-                        float* data,
-                        int frames) = 0;
-  virtual void SendInterrupt() = 0;
+                        void* data,
+                        int data_size_bytes);
+  void SendInterruptInternal();
 
- protected:
-  virtual ~LoopbackHandler() = default;
+  scoped_refptr<IOBufferPool> buffer_pool_;
+
+  int data_size_ = 0;
+  SampleFormat format_ = kUnknownSampleFormat;
+  int sample_rate_ = 0;
+  int num_channels_ = 0;
+
+  base::SequenceBound<LoopbackIO> io_;
+  std::unique_ptr<ExternalLoopbackHandler, ExternalDeleter> external_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(LoopbackHandler);
 };
 
 }  // namespace media
diff --git a/chromecast/media/cma/backend/mixer/loopback_test_support.cc b/chromecast/media/cma/backend/mixer/loopback_test_support.cc
new file mode 100644
index 0000000..3466d02
--- /dev/null
+++ b/chromecast/media/cma/backend/mixer/loopback_test_support.cc
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/backend/mixer/loopback_test_support.h"
+
+#include <utility>
+
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "chromecast/media/audio/mixer_service/mixer_socket.h"
+#include "chromecast/media/cma/backend/mixer/loopback_handler.h"
+#include "chromecast/media/cma/backend/mixer/mixer_loopback_connection.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+class FakeMixerDelegate : public mixer_service::MixerSocket::Delegate {
+ public:
+  ~FakeMixerDelegate() override = default;
+};
+
+}  // namespace
+
+std::unique_ptr<mixer_service::MixerSocket> CreateLoopbackConnectionForTest(
+    LoopbackHandler* loopback_handler) {
+  auto receiver_socket = std::make_unique<mixer_service::MixerSocket>();
+  auto caller_socket = std::make_unique<mixer_service::MixerSocket>();
+
+  receiver_socket->SetLocalCounterpart(caller_socket->GetWeakPtr(),
+                                       base::SequencedTaskRunnerHandle::Get());
+  caller_socket->SetLocalCounterpart(receiver_socket->GetWeakPtr(),
+                                     base::SequencedTaskRunnerHandle::Get());
+
+  auto mixer_side =
+      std::make_unique<MixerLoopbackConnection>(std::move(receiver_socket));
+  loopback_handler->AddConnection(std::move(mixer_side));
+
+  return caller_socket;
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/mixer/loopback_test_support.h b/chromecast/media/cma/backend/mixer/loopback_test_support.h
new file mode 100644
index 0000000..005ba965
--- /dev/null
+++ b/chromecast/media/cma/backend/mixer/loopback_test_support.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_MEDIA_CMA_BACKEND_MIXER_LOOPBACK_TEST_SUPPORT_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_MIXER_LOOPBACK_TEST_SUPPORT_H_
+
+#include <memory>
+
+namespace chromecast {
+namespace media {
+class LoopbackHandler;
+
+namespace mixer_service {
+class MixerSocket;
+}  // namespace mixer_service
+
+std::unique_ptr<mixer_service::MixerSocket> CreateLoopbackConnectionForTest(
+    LoopbackHandler* loopback_handler);
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_CMA_BACKEND_MIXER_LOOPBACK_TEST_SUPPORT_H_
diff --git a/chromecast/media/cma/backend/mixer/mixer_loopback_connection.cc b/chromecast/media/cma/backend/mixer/mixer_loopback_connection.cc
new file mode 100644
index 0000000..58676c7a
--- /dev/null
+++ b/chromecast/media/cma/backend/mixer/mixer_loopback_connection.cc
@@ -0,0 +1,79 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/backend/mixer/mixer_loopback_connection.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "chromecast/media/audio/mixer_service/conversions.h"
+#include "chromecast/media/audio/mixer_service/mixer_service.pb.h"
+#include "chromecast/net/io_buffer_pool.h"
+
+namespace chromecast {
+namespace media {
+
+MixerLoopbackConnection::MixerLoopbackConnection(
+    std::unique_ptr<mixer_service::MixerSocket> socket)
+    : socket_(std::move(socket)) {
+  DCHECK(socket_);
+  socket_->SetDelegate(this);
+}
+
+MixerLoopbackConnection::~MixerLoopbackConnection() = default;
+
+void MixerLoopbackConnection::SetErrorCallback(base::OnceClosure callback) {
+  error_callback_ = std::move(callback);
+}
+
+void MixerLoopbackConnection::SetStreamConfig(SampleFormat sample_format,
+                                              int sample_rate,
+                                              int num_channels,
+                                              int data_size) {
+  mixer_service::Generic message;
+  mixer_service::StreamConfig* config = message.mutable_stream_config();
+  config->set_sample_format(mixer_service::ConvertSampleFormat(sample_format));
+  config->set_sample_rate(sample_rate);
+  config->set_num_channels(num_channels);
+  config->set_data_size(data_size);
+  socket_->SendProto(message);
+
+  sent_stream_config_ = true;
+}
+
+void MixerLoopbackConnection::SendAudio(
+    scoped_refptr<net::IOBuffer> audio_buffer,
+    int data_size_bytes,
+    int64_t timestamp) {
+  DCHECK(sent_stream_config_);
+  socket_->SendAudioBuffer(std::move(audio_buffer), data_size_bytes, timestamp);
+}
+
+bool MixerLoopbackConnection::HandleMetadata(
+    const mixer_service::Generic& message) {
+  return true;
+}
+
+bool MixerLoopbackConnection::HandleAudioData(char* data,
+                                              int size,
+                                              int64_t timestamp) {
+  return true;
+}
+
+bool MixerLoopbackConnection::HandleAudioBuffer(
+    scoped_refptr<net::IOBuffer> buffer,
+    char* data,
+    int size,
+    int64_t timestamp) {
+  return true;
+}
+
+void MixerLoopbackConnection::OnConnectionError() {
+  if (error_callback_) {
+    std::move(error_callback_).Run();
+  }
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/mixer/mixer_loopback_connection.h b/chromecast/media/cma/backend/mixer/mixer_loopback_connection.h
new file mode 100644
index 0000000..5c2e6052
--- /dev/null
+++ b/chromecast/media/cma/backend/mixer/mixer_loopback_connection.h
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_MEDIA_CMA_BACKEND_MIXER_MIXER_LOOPBACK_CONNECTION_H_
+#define CHROMECAST_MEDIA_CMA_BACKEND_MIXER_MIXER_LOOPBACK_CONNECTION_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "chromecast/media/audio/mixer_service/mixer_socket.h"
+#include "chromecast/public/media/decoder_config.h"
+
+namespace net {
+class IOBuffer;
+}  // namespace net
+
+namespace chromecast {
+namespace media {
+
+namespace mixer_service {
+class Generic;
+}  // namespace mixer_service
+
+class MixerLoopbackConnection : public mixer_service::MixerSocket::Delegate {
+ public:
+  explicit MixerLoopbackConnection(
+      std::unique_ptr<mixer_service::MixerSocket> socket);
+  ~MixerLoopbackConnection() override;
+
+  void SetErrorCallback(base::OnceClosure callback);
+
+  void SetStreamConfig(SampleFormat sample_format,
+                       int sample_rate,
+                       int num_channels,
+                       int data_size);
+
+  void SendAudio(scoped_refptr<net::IOBuffer> audio_buffer,
+                 int data_size_bytes,
+                 int64_t timestamp);
+
+ private:
+  // mixer_service::MixerSocket::Delegate implementation:
+  bool HandleMetadata(const mixer_service::Generic& message) override;
+  bool HandleAudioData(char* data, int size, int64_t timestamp) override;
+  bool HandleAudioBuffer(scoped_refptr<net::IOBuffer> buffer,
+                         char* data,
+                         int size,
+                         int64_t timestamp) override;
+  void OnConnectionError() override;
+
+  const std::unique_ptr<mixer_service::MixerSocket> socket_;
+
+  base::OnceClosure error_callback_;
+
+  bool sent_stream_config_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(MixerLoopbackConnection);
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_CMA_BACKEND_MIXER_MIXER_LOOPBACK_CONNECTION_H_
diff --git a/chromecast/media/cma/backend/mixer/mixer_service_receiver.cc b/chromecast/media/cma/backend/mixer/mixer_service_receiver.cc
index 441cfbe..6a0635d 100644
--- a/chromecast/media/cma/backend/mixer/mixer_service_receiver.cc
+++ b/chromecast/media/cma/backend/mixer/mixer_service_receiver.cc
@@ -11,7 +11,9 @@
 #include "chromecast/media/audio/mixer_service/conversions.h"
 #include "chromecast/media/audio/mixer_service/mixer_service.pb.h"
 #include "chromecast/media/audio/mixer_service/mixer_socket.h"
+#include "chromecast/media/cma/backend/mixer/loopback_handler.h"
 #include "chromecast/media/cma/backend/mixer/mixer_input_connection.h"
+#include "chromecast/media/cma/backend/mixer/mixer_loopback_connection.h"
 #include "chromecast/media/cma/backend/mixer/stream_mixer.h"
 
 namespace chromecast {
@@ -105,8 +107,11 @@
   DISALLOW_COPY_AND_ASSIGN(ControlConnection);
 };
 
-MixerServiceReceiver::MixerServiceReceiver(StreamMixer* mixer) : mixer_(mixer) {
+MixerServiceReceiver::MixerServiceReceiver(StreamMixer* mixer,
+                                           LoopbackHandler* loopback_handler)
+    : mixer_(mixer), loopback_handler_(loopback_handler) {
   DCHECK(mixer_);
+  DCHECK(loopback_handler_);
 }
 
 MixerServiceReceiver::~MixerServiceReceiver() = default;
@@ -133,7 +138,9 @@
 void MixerServiceReceiver::CreateLoopbackConnection(
     std::unique_ptr<mixer_service::MixerSocket> socket,
     const mixer_service::Generic& message) {
-  LOG(INFO) << "Unhandled loopback connection";
+  auto connection =
+      std::make_unique<MixerLoopbackConnection>(std::move(socket));
+  loopback_handler_->AddConnection(std::move(connection));
 }
 
 void MixerServiceReceiver::CreateAudioRedirection(
diff --git a/chromecast/media/cma/backend/mixer/mixer_service_receiver.h b/chromecast/media/cma/backend/mixer/mixer_service_receiver.h
index e506804..70fbf0c 100644
--- a/chromecast/media/cma/backend/mixer/mixer_service_receiver.h
+++ b/chromecast/media/cma/backend/mixer/mixer_service_receiver.h
@@ -13,6 +13,7 @@
 
 namespace chromecast {
 namespace media {
+class LoopbackHandler;
 class StreamMixer;
 
 namespace mixer_service {
@@ -22,7 +23,7 @@
 
 class MixerServiceReceiver : public mixer_service::Receiver {
  public:
-  explicit MixerServiceReceiver(StreamMixer* mixer);
+  MixerServiceReceiver(StreamMixer* mixer, LoopbackHandler* loopback_handler);
   ~MixerServiceReceiver() override;
 
   // Called by the mixer when the active stream count changes.
@@ -47,6 +48,7 @@
   void RemoveControlConnection(ControlConnection* ptr);
 
   StreamMixer* const mixer_;
+  LoopbackHandler* const loopback_handler_;
 
   base::flat_map<ControlConnection*, std::unique_ptr<ControlConnection>>
       control_connections_;
diff --git a/chromecast/media/cma/backend/mixer/stream_mixer.cc b/chromecast/media/cma/backend/mixer/stream_mixer.cc
index e6ca33a..0126eaab 100644
--- a/chromecast/media/cma/backend/mixer/stream_mixer.cc
+++ b/chromecast/media/cma/backend/mixer/stream_mixer.cc
@@ -30,6 +30,7 @@
 #include "chromecast/media/cma/backend/interleaved_channel_mixer.h"
 #include "chromecast/media/cma/backend/mixer/audio_output_redirector.h"
 #include "chromecast/media/cma/backend/mixer/filter_group.h"
+#include "chromecast/media/cma/backend/mixer/loopback_handler.h"
 #include "chromecast/media/cma/backend/mixer/mixer_service_receiver.h"
 #include "chromecast/media/cma/backend/mixer/post_processing_pipeline_impl.h"
 #include "chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.h"
@@ -178,7 +179,6 @@
           std::make_unique<PostProcessingPipelineFactoryImpl>()),
       mixer_thread_(std::move(mixer_thread)),
       mixer_task_runner_(std::move(mixer_task_runner)),
-      loopback_handler_(LoopbackHandler::Create(mixer_task_runner_)),
       enable_dynamic_channel_count_(
           GetSwitchValueBoolean(switches::kMixerEnableDynamicChannelCount,
                                 false)),
@@ -213,19 +213,19 @@
     mixer_task_runner_ = mixer_thread_->task_runner();
     mixer_task_runner_->PostTask(FROM_HERE, base::BindOnce(&UseHighPriority));
 
-    health_checker_ = std::make_unique<ThreadHealthChecker>(
-        mixer_task_runner_, loopback_handler_->GetTaskRunner(),
-        kHealthCheckInterval, kMixerThreadCheckTimeout,
-        base::BindRepeating(&StreamMixer::OnHealthCheckFailed,
-                            base::Unretained(this)));
-    LOG(INFO) << "Mixer health checker started";
-
     io_thread_ = std::make_unique<base::Thread>("MixerIO");
     base::Thread::Options io_options;
     io_options.message_pump_type = base::MessagePumpType::IO;
     io_options.priority = base::ThreadPriority::REALTIME_AUDIO;
     CHECK(io_thread_->StartWithOptions(io_options));
     io_task_runner_ = io_thread_->task_runner();
+
+    health_checker_ = std::make_unique<ThreadHealthChecker>(
+        mixer_task_runner_, io_task_runner_, kHealthCheckInterval,
+        kMixerThreadCheckTimeout,
+        base::BindRepeating(&StreamMixer::OnHealthCheckFailed,
+                            base::Unretained(this)));
+    LOG(INFO) << "Mixer health checker started";
   } else {
     io_task_runner_ = mixer_task_runner_;
   }
@@ -249,7 +249,9 @@
         external_volume_observer_.get());
   }
 
-  receiver_ = base::SequenceBound<MixerServiceReceiver>(io_task_runner_, this);
+  loopback_handler_ = std::make_unique<LoopbackHandler>(io_task_runner_);
+  receiver_ = base::SequenceBound<MixerServiceReceiver>(
+      io_task_runner_, this, loopback_handler_.get());
 }
 
 void StreamMixer::OnHealthCheckFailed() {
@@ -368,6 +370,10 @@
   enable_dynamic_channel_count_ = enable;
 }
 
+LoopbackHandler* StreamMixer::GetLoopbackHandlerForTest() {
+  return loopback_handler_.get();
+}
+
 StreamMixer::~StreamMixer() {
   LOG(INFO) << __func__;
 
@@ -831,16 +837,6 @@
   }
 }
 
-void StreamMixer::AddLoopbackAudioObserver(
-    CastMediaShlib::LoopbackAudioObserver* observer) {
-  loopback_handler_->AddObserver(observer);
-}
-
-void StreamMixer::RemoveLoopbackAudioObserver(
-    CastMediaShlib::LoopbackAudioObserver* observer) {
-  loopback_handler_->RemoveObserver(observer);
-}
-
 void StreamMixer::AddAudioOutputRedirector(
     std::unique_ptr<AudioOutputRedirector> redirector) {
   MAKE_SURE_MIXER_THREAD(AddAudioOutputRedirector, std::move(redirector));
diff --git a/chromecast/media/cma/backend/mixer/stream_mixer.h b/chromecast/media/cma/backend/mixer/stream_mixer.h
index 046148e..41a32e87 100644
--- a/chromecast/media/cma/backend/mixer/stream_mixer.h
+++ b/chromecast/media/cma/backend/mixer/stream_mixer.h
@@ -22,7 +22,6 @@
 #include "base/threading/sequence_bound.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
-#include "chromecast/media/cma/backend/mixer/loopback_handler.h"
 #include "chromecast/media/cma/backend/mixer/mixer_control.h"
 #include "chromecast/media/cma/backend/mixer/mixer_input.h"
 #include "chromecast/media/cma/backend/mixer/mixer_pipeline.h"
@@ -38,6 +37,7 @@
 
 class AudioOutputRedirector;
 class InterleavedChannelMixer;
+class LoopbackHandler;
 class MixerServiceReceiver;
 class MixerOutputStream;
 class PostProcessingPipelineFactory;
@@ -84,13 +84,6 @@
   // called on it; at this point it is safe to delete the input source.
   void RemoveInput(MixerInput::Source* input_source);
 
-  // Adds/removes a loopback audio observer. Observers are passed audio data
-  // just before it is written to output (prior to linearization filters).
-  void AddLoopbackAudioObserver(
-      CastMediaShlib::LoopbackAudioObserver* observer);
-  void RemoveLoopbackAudioObserver(
-      CastMediaShlib::LoopbackAudioObserver* observer);
-
   // Adds/removes an output redirector. Output redirectors take audio from
   // matching inputs and pass them to secondary output instead of the normal
   // mixer output.
@@ -132,6 +125,7 @@
       const std::string& pipeline_json);
   void SetNumOutputChannelsForTest(int num_output_channels);
   void EnableDynamicChannelCountForTest(bool enable);
+  LoopbackHandler* GetLoopbackHandlerForTest();
 
  private:
   class BaseExternalMediaVolumeChangeRequestObserver
@@ -195,7 +189,6 @@
   scoped_refptr<base::SingleThreadTaskRunner> mixer_task_runner_;
   std::unique_ptr<base::Thread> io_thread_;
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
-  std::unique_ptr<LoopbackHandler, LoopbackHandler::Deleter> loopback_handler_;
   std::unique_ptr<ThreadHealthChecker> health_checker_;
 
   std::unique_ptr<InterleavedChannelMixer> loopback_channel_mixer_;
@@ -242,6 +235,7 @@
   std::unique_ptr<BaseExternalMediaVolumeChangeRequestObserver>
       external_volume_observer_;
 
+  std::unique_ptr<LoopbackHandler> loopback_handler_;
   base::SequenceBound<MixerServiceReceiver> receiver_;
 
   base::WeakPtrFactory<StreamMixer> weak_factory_;
diff --git a/chromecast/media/cma/backend/mixer/stream_mixer_external_audio_pipeline_unittest.cc b/chromecast/media/cma/backend/mixer/stream_mixer_external_audio_pipeline_unittest.cc
index 85c8099..8056ea3 100644
--- a/chromecast/media/cma/backend/mixer/stream_mixer_external_audio_pipeline_unittest.cc
+++ b/chromecast/media/cma/backend/mixer/stream_mixer_external_audio_pipeline_unittest.cc
@@ -13,6 +13,9 @@
 #include "base/run_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromecast/media/audio/fake_external_audio_pipeline_support.h"
+#include "chromecast/media/audio/mixer_service/loopback_connection.h"
+#include "chromecast/media/audio/mixer_service/mixer_socket.h"
+#include "chromecast/media/cma/backend/mixer/loopback_test_support.h"
 #include "chromecast/media/cma/backend/mixer/mock_mixer_source.h"
 #include "chromecast/media/cma/backend/mixer/mock_post_processor_factory.h"
 #include "chromecast/media/cma/backend/mixer/stream_mixer.h"
@@ -30,7 +33,8 @@
 using ::testing::AtLeast;
 
 // Mock for saving and checking loopback audio data.
-class MockLoopbackAudioObserver : public CastMediaShlib::LoopbackAudioObserver {
+class MockLoopbackAudioObserver
+    : public mixer_service::LoopbackConnection::Delegate {
  public:
   MockLoopbackAudioObserver() {
     ON_CALL(*this, OnLoopbackAudio(_, _, _, _, _, _))
@@ -46,7 +50,6 @@
                     uint8_t* data,
                     int length));
   MOCK_METHOD0(OnLoopbackInterrupted, void());
-  MOCK_METHOD0(OnRemoved, void());
 
   const std::vector<float>& data() const { return data_; }
 
@@ -96,6 +99,16 @@
     run_loop.Run();
   }
 
+  std::unique_ptr<mixer_service::LoopbackConnection> AddLoopbackObserver(
+      mixer_service::LoopbackConnection::Delegate* observer) {
+    std::unique_ptr<mixer_service::MixerSocket> loopback_socket =
+        CreateLoopbackConnectionForTest(mixer_->GetLoopbackHandlerForTest());
+    auto connection = std::make_unique<mixer_service::LoopbackConnection>(
+        observer, std::move(loopback_socket));
+    connection->Connect();
+    return connection;
+  }
+
  protected:
   std::unique_ptr<StreamMixer> mixer_;
   testing::FakeExternalAudioPipelineSupport* const
@@ -166,13 +179,6 @@
   mixer_->ResetPostProcessorsForTest(
       std::make_unique<MockPostProcessorFactory>(), "{}");
 
-  // CastMediaShlib::LoopbackAudioObserver mock observer.
-  MockLoopbackAudioObserver mock_loopback_observer;
-  EXPECT_CALL(mock_loopback_observer, OnLoopbackAudio(_, _, _, _, _, _))
-      .Times(AtLeast(1));
-  EXPECT_CALL(mock_loopback_observer, OnLoopbackInterrupted())
-      .Times(AtLeast(1));
-  EXPECT_CALL(mock_loopback_observer, OnRemoved()).Times(1);
   // Input.
   MockMixerSource input(48000);
   EXPECT_CALL(input, InitializeAudioPlayback(_, _)).Times(1);
@@ -194,15 +200,18 @@
   auto expected = ::media::AudioBus::Create(kNumChannels, kNumFrames);
   data->CopyTo(expected.get());
 
-  // Start the test. Set loopback observer.
-  mixer_->AddLoopbackAudioObserver(&mock_loopback_observer);
-
   mixer_->AddInput(&input);
+  MockLoopbackAudioObserver mock_loopback_observer;
+  auto connection = AddLoopbackObserver(&mock_loopback_observer);
+  EXPECT_CALL(mock_loopback_observer, OnLoopbackAudio(_, _, _, _, _, _))
+      .Times(AtLeast(1));
   RunLoopForMixer();
 
   // Send data to the stream mixer.
   input.SetData(std::move(data));
   RunLoopForMixer();
+  RunLoopForMixer();
+  RunLoopForMixer();
 
   // Get actual data from our mocked loopback observer.
   ASSERT_GE(mock_loopback_observer.data().size(), kNumFrames);
@@ -215,7 +224,6 @@
   CompareAudioData(*expected, *actual);
 
   // Check OnRemoved.
-  mixer_->RemoveLoopbackAudioObserver(&mock_loopback_observer);
   mixer_->RemoveInput(&input);
 
   RunLoopForMixer();
diff --git a/chromecast/media/cma/backend/mixer/stream_mixer_unittest.cc b/chromecast/media/cma/backend/mixer/stream_mixer_unittest.cc
index 21d81473..49e386e 100644
--- a/chromecast/media/cma/backend/mixer/stream_mixer_unittest.cc
+++ b/chromecast/media/cma/backend/mixer/stream_mixer_unittest.cc
@@ -16,7 +16,11 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
+#include "chromecast/media/audio/mixer_service/loopback_connection.h"
+#include "chromecast/media/audio/mixer_service/mixer_socket.h"
 #include "chromecast/media/cma/backend/mixer/audio_output_redirector.h"
+#include "chromecast/media/cma/backend/mixer/loopback_handler.h"
+#include "chromecast/media/cma/backend/mixer/loopback_test_support.h"
 #include "chromecast/media/cma/backend/mixer/mixer_input.h"
 #include "chromecast/media/cma/backend/mixer/mock_mixer_source.h"
 #include "chromecast/media/cma/backend/mixer/mock_post_processor_factory.h"
@@ -232,7 +236,8 @@
   }
 }
 
-class MockLoopbackAudioObserver : public CastMediaShlib::LoopbackAudioObserver {
+class MockLoopbackAudioObserver
+    : public mixer_service::LoopbackConnection::Delegate {
  public:
   MockLoopbackAudioObserver() = default;
   ~MockLoopbackAudioObserver() override = default;
@@ -240,7 +245,6 @@
   MOCK_METHOD6(OnLoopbackAudio,
                void(int64_t, SampleFormat, int, int, uint8_t*, int));
   MOCK_METHOD0(OnLoopbackInterrupted, void());
-  MOCK_METHOD0(OnRemoved, void());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockLoopbackAudioObserver);
@@ -452,6 +456,16 @@
     CompareAudioData(*expected_redirected_output, *actual_redirected_output);
   }
 
+  std::unique_ptr<mixer_service::LoopbackConnection> AddLoopbackObserver(
+      mixer_service::LoopbackConnection::Delegate* observer) {
+    std::unique_ptr<mixer_service::MixerSocket> loopback_socket =
+        CreateLoopbackConnectionForTest(mixer_->GetLoopbackHandlerForTest());
+    auto connection = std::make_unique<mixer_service::LoopbackConnection>(
+        observer, std::move(loopback_socket));
+    connection->Connect();
+    return connection;
+  }
+
  protected:
   const std::unique_ptr<base::MessageLoop> message_loop_;
   MockMixerOutput* mock_output_;
@@ -1198,7 +1212,7 @@
   MockMixerSource input(kTestSamplesPerSecond);
   testing::StrictMock<MockLoopbackAudioObserver> observer;
   mixer_->AddInput(&input);
-  mixer_->AddLoopbackAudioObserver(&observer);
+  auto connection = AddLoopbackObserver(&observer);
   EXPECT_CALL(observer,
               OnLoopbackAudio(_, kSampleFormatF32, kTestSamplesPerSecond,
                               kNumChannels, _, _))
@@ -1207,10 +1221,7 @@
   WaitForMixer();
   PlaybackOnce();
 
-  EXPECT_CALL(observer, OnRemoved());
-  mixer_->RemoveLoopbackAudioObserver(&observer);
   WaitForMixer();
-
   mixer_.reset();
 }
 
@@ -1221,7 +1232,7 @@
   MockMixerSource input(kTestSamplesPerSecond);
   testing::StrictMock<MockLoopbackAudioObserver> observer;
   mixer_->AddInput(&input);
-  mixer_->AddLoopbackAudioObserver(&observer);
+  auto connection = AddLoopbackObserver(&observer);
 
   EXPECT_CALL(observer,
               OnLoopbackAudio(_, kSampleFormatF32, kTestSamplesPerSecond,
@@ -1231,10 +1242,7 @@
   WaitForMixer();
   PlaybackOnce();
 
-  EXPECT_CALL(observer, OnRemoved());
-  mixer_->RemoveLoopbackAudioObserver(&observer);
   WaitForMixer();
-
   mixer_.reset();
 }
 
diff --git a/chromecast/net/fake_stream_socket.cc b/chromecast/net/fake_stream_socket.cc
index 42bfecee..97ac83f2 100644
--- a/chromecast/net/fake_stream_socket.cc
+++ b/chromecast/net/fake_stream_socket.cc
@@ -73,6 +73,8 @@
   DISALLOW_COPY_AND_ASSIGN(SocketBuffer);
 };
 
+FakeStreamSocket::FakeStreamSocket() : FakeStreamSocket(net::IPEndPoint()) {}
+
 FakeStreamSocket::FakeStreamSocket(const net::IPEndPoint& local_address)
     : local_address_(local_address),
       buffer_(std::make_unique<SocketBuffer>()),
diff --git a/chromecast/net/fake_stream_socket.h b/chromecast/net/fake_stream_socket.h
index d5061b38..4e46c67f5 100644
--- a/chromecast/net/fake_stream_socket.h
+++ b/chromecast/net/fake_stream_socket.h
@@ -21,6 +21,7 @@
 // Fake StreamSocket that communicates with another instance in memory.
 class FakeStreamSocket : public net::StreamSocket {
  public:
+  FakeStreamSocket();
   explicit FakeStreamSocket(const net::IPEndPoint& local_address);
   ~FakeStreamSocket() override;
 
diff --git a/chromecast/public/cast_media_shlib.h b/chromecast/public/cast_media_shlib.h
index 6242076..f63ec84 100644
--- a/chromecast/public/cast_media_shlib.h
+++ b/chromecast/public/cast_media_shlib.h
@@ -43,39 +43,6 @@
  public:
   using ResultCallback =
       std::function<void(bool success, const std::string& message)>;
-  // Observer for audio loopback data.
-  class LoopbackAudioObserver {
-   public:
-    // Called whenever audio data is about to be output. The |timestamp| is the
-    // estimated time in microseconds (relative to CLOCK_MONOTONIC_RAW) that
-    // the audio will actually be output. |length| is the length of the audio
-    // |data| in bytes. The format of the data is given by |sample_format| and
-    // |num_channels|.
-    // This method may be called by any thread, and MUST not block or take very
-    // much time (to avoid audio underruns).
-    virtual void OnLoopbackAudio(int64_t timestamp,
-                                 SampleFormat sample_format,
-                                 int sample_rate,
-                                 int num_channels,
-                                 uint8_t* data,
-                                 int length) = 0;
-
-    // Called if the loopback data is not continuous (ie, does not accurately
-    // represent the actual output) for any reason. For example, if there is an
-    // output underflow, or if output is disabled due to no output streams.
-    // This method could be called from any thread.
-    virtual void OnLoopbackInterrupted() = 0;
-
-    // Called once this observer has been fully removed by a call to
-    // RemoveLoopbackAudioObserver(). After this is called, no more calls to
-    // OnLoopbackAudio() or OnLoopbackInterrupted() will be made for this
-    // observer unless it is added again. This method could be called from any
-    // thread.
-    virtual void OnRemoved() = 0;
-
-   protected:
-    virtual ~LoopbackAudioObserver() {}
-  };
 
   // Initializes platform-specific media systems.  Only called when in an
   // uninitialized state.
@@ -112,25 +79,6 @@
   // Tests if the implementation supports renderer clock rate adjustments.
   static bool SupportsMediaClockRateChange();
 
-  // Adds a loopback audio observer. An observer will not be added more than
-  // once without being removed first.
-  // This function is optional to implement.
-  static void AddLoopbackAudioObserver(LoopbackAudioObserver* observer)
-      __attribute__((__weak__));
-
-  // Removes a loopback audio observer. An observer will not be removed unless
-  // it was previously added, and will not be removed more than once without
-  // being added again first.
-  // Once the observer is fully removed (ie. once it is certain that
-  // OnLoopbackAudio() will not be called again for the observer), the
-  // observer's OnRemoved() method must be called. The OnRemoved() method must
-  // be called once for each time that RemoveLoopbackAudioObserver() is called
-  // for a given observer, even if the observer was not added. The
-  // implementation may call OnRemoved() from any thread.
-  // This function is optional to implement.
-  static void RemoveLoopbackAudioObserver(LoopbackAudioObserver* observer)
-      __attribute__((__weak__));
-
   // Reset the post processing pipeline. |callback| will be called with
   // |success| = |true| if the new config loads without error.
   static void ResetPostProcessors(ResultCallback callback)
diff --git a/chromecast/public/media/external_audio_pipeline_shlib.h b/chromecast/public/media/external_audio_pipeline_shlib.h
index ce71ebe..4dc5673 100644
--- a/chromecast/public/media/external_audio_pipeline_shlib.h
+++ b/chromecast/public/media/external_audio_pipeline_shlib.h
@@ -6,6 +6,7 @@
 #define CHROMECAST_PUBLIC_MEDIA_EXTERNAL_AUDIO_PIPELINE_SHLIB_H_
 
 #include <memory>
+#include <string>
 
 #include "cast_media_shlib.h"
 #include "chromecast_export.h"
@@ -82,6 +83,40 @@
     virtual ~ExternalMediaMetadataChangeObserver() = default;
   };
 
+  // Observer for audio loopback data.
+  class LoopbackAudioObserver {
+   public:
+    // Called whenever audio data is about to be output. The |timestamp| is the
+    // estimated time in microseconds (relative to CLOCK_MONOTONIC_RAW) that
+    // the audio will actually be output. |length| is the length of the audio
+    // |data| in bytes. The format of the data is given by |sample_format| and
+    // |num_channels|.
+    // This method may be called by any thread, and MUST not block or take very
+    // much time (to avoid audio underruns).
+    virtual void OnLoopbackAudio(int64_t timestamp,
+                                 SampleFormat sample_format,
+                                 int sample_rate,
+                                 int num_channels,
+                                 uint8_t* data,
+                                 int length) = 0;
+
+    // Called if the loopback data is not continuous (ie, does not accurately
+    // represent the actual output) for any reason. For example, if there is an
+    // output underflow, or if output is disabled due to no output streams.
+    // This method could be called from any thread.
+    virtual void OnLoopbackInterrupted() = 0;
+
+    // Called once this observer has been fully removed by a call to
+    // RemoveLoopbackAudioObserver(). After this is called, no more calls to
+    // OnLoopbackAudio() or OnLoopbackInterrupted() will be made for this
+    // observer unless it is added again. This method could be called from any
+    // thread.
+    virtual void OnRemoved() = 0;
+
+   protected:
+    virtual ~LoopbackAudioObserver() {}
+  };
+
   // Returns whether this shlib is supported. If this returns true, it indicates
   // that the platform uses an external audio pipeline that needs to be combined
   // with Cast's media pipeline.
@@ -110,8 +145,7 @@
 
   // Adds a loopback audio observer. An observer will not be added more than
   // once without being removed first.
-  static void AddExternalLoopbackAudioObserver(
-      CastMediaShlib::LoopbackAudioObserver* observer);
+  static void AddExternalLoopbackAudioObserver(LoopbackAudioObserver* observer);
 
   // Removes a loopback audio observer. An observer will not be removed unless
   // it was previously added, and will not be removed more than once without
@@ -124,7 +158,7 @@
   // implementation may call OnRemoved() from any thread.
   // This function is optional to implement.
   static void RemoveExternalLoopbackAudioObserver(
-      CastMediaShlib::LoopbackAudioObserver* observer);
+      LoopbackAudioObserver* observer);
 
   // Adds an external media metadata observer.
   static void AddExternalMediaMetadataChangeObserver(
diff --git a/chromecast/renderer/cast_extensions_dispatcher_delegate.cc b/chromecast/renderer/cast_extensions_dispatcher_delegate.cc
index bb07b56..3edde2c 100644
--- a/chromecast/renderer/cast_extensions_dispatcher_delegate.cc
+++ b/chromecast/renderer/cast_extensions_dispatcher_delegate.cc
@@ -24,7 +24,6 @@
 #include "extensions/common/switches.h"
 #include "extensions/renderer/bindings/api_binding_hooks.h"
 #include "extensions/renderer/bindings/api_bindings_system.h"
-#include "extensions/renderer/css_native_handler.h"
 #include "extensions/renderer/dispatcher.h"
 #include "extensions/renderer/lazy_background_page_native_handler.h"
 #include "extensions/renderer/native_extension_bindings_system.h"
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 338e237..b3a44927 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/buildflag_header.gni")
 import("//build/config/chromeos/rules.gni")
 import("//build/config/ui.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
 import("//testing/test.gni")
 import("//third_party/protobuf/proto_library.gni")
 
@@ -266,3 +267,12 @@
     ]
   }
 }
+
+fuzzer_test("ppd_line_reader_fuzzer") {
+  sources = [
+    "printing/ppd_line_reader_fuzzer.cc",
+  ]
+  deps = [
+    ":chromeos",
+  ]
+}
diff --git a/chromeos/printing/ppd_line_reader.cc b/chromeos/printing/ppd_line_reader.cc
index 3110e23..fb220dd 100644
--- a/chromeos/printing/ppd_line_reader.cc
+++ b/chromeos/printing/ppd_line_reader.cc
@@ -180,13 +180,13 @@
 // static
 std::unique_ptr<PpdLineReader> PpdLineReader::Create(
     const std::string& contents,
-    int max_line_length) {
+    size_t max_line_length) {
   return std::make_unique<PpdLineReaderImpl>(contents, max_line_length);
 }
 
 // static
 bool PpdLineReader::ContainsMagicNumber(const std::string& contents,
-                                        int max_line_length) {
+                                        size_t max_line_length) {
   auto line_reader = PpdLineReader::Create(contents, max_line_length);
   std::string line;
   return line_reader->NextLine(&line) &&
diff --git a/chromeos/printing/ppd_line_reader.h b/chromeos/printing/ppd_line_reader.h
index c1f6f22..90553202 100644
--- a/chromeos/printing/ppd_line_reader.h
+++ b/chromeos/printing/ppd_line_reader.h
@@ -23,14 +23,14 @@
   // |contents| may or may not be gzip-compressed, and must remain valid and
   // unchanged while the Created PpdReader exists.
   static std::unique_ptr<PpdLineReader> Create(const std::string& contents,
-                                               int max_line_length);
+                                               size_t max_line_length);
 
   // Checks to see whether the file contents in |contents| contains the magic
   // number which is found at the beginning of every PPD file. To verify this,
   // a line reader is created which simply attempts to read the first line and
   // checks whether it contains the magic number.
   static bool ContainsMagicNumber(const std::string& contents,
-                                  int max_line_length);
+                                  size_t max_line_length);
 
   virtual ~PpdLineReader() = default;
 
diff --git a/chromeos/printing/ppd_line_reader_fuzzer.cc b/chromeos/printing/ppd_line_reader_fuzzer.cc
new file mode 100644
index 0000000..d7154a05
--- /dev/null
+++ b/chromeos/printing/ppd_line_reader_fuzzer.cc
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/printing/ppd_line_reader.h"
+
+#include <memory>
+#include <string>
+
+#include <fuzzer/FuzzedDataProvider.h>
+namespace {
+
+constexpr int kUpperMaxLineLengthBound = 1024;
+
+}  // namespace
+
+namespace chromeos {
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzedDataProvider data_provider(data, size);
+
+  const size_t line_length =
+      data_provider.ConsumeIntegralInRange<size_t>(0, kUpperMaxLineLengthBound);
+  const std::string contents = data_provider.ConsumeRemainingBytesAsString();
+
+  std::unique_ptr<PpdLineReader> ppd_line_reader =
+      PpdLineReader::Create(contents, line_length);
+
+  std::string line;
+  while (ppd_line_reader->NextLine(&line)) {
+    // Call NextLine() until we hit the end of the file or an error.
+  }
+
+  return 0;
+}
+
+}  // namespace chromeos
\ No newline at end of file
diff --git a/components/BUILD.gn b/components/BUILD.gn
index d77f858..798377d 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -206,6 +206,7 @@
       "//components/image_fetcher/ios:unit_tests",
       "//components/language/ios/browser:unit_tests",
       "//components/password_manager/ios:unit_tests",
+      "//components/security_state/ios:unit_tests",
       "//components/signin/ios/browser:unit_tests",
       "//components/translate/ios/browser:unit_tests",
     ]
diff --git a/components/arc/mojom/auth.mojom b/components/arc/mojom/auth.mojom
index 9b6bc51..984164a 100644
--- a/components/arc/mojom/auth.mojom
+++ b/components/arc/mojom/auth.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Next MinVersion: 22
+// Next MinVersion: 23
 
 module arc.mojom;
 
@@ -105,6 +105,45 @@
     // corresponding UMA callsite in Chrome arc::UpdateAuthAccountCheckStatus.
 };
 
+[Extensible]
+enum MainAccountResolutionStatus {
+    // No hash code was set for the main account. System contains no account of
+    // required type.
+    [MinVersion=22] NO_HASH_CODE_NO_ACCOUNT = 0,
+
+    // No hash code was set for the main account. System contains only single
+    // account of required type.
+    [MinVersion=22] NO_HASH_CODE_SINGLE_ACCOUNT = 1,
+
+    // No hash code was set for the main account. System contains multiple
+    // accounts of required type.
+    [MinVersion=22] NO_HASH_CODE_MULTIPLE_ACCOUNTS = 2,
+
+    // Hash code was set but no account currently available.
+    [MinVersion=22] HASH_CODE_NO_ACCOUNT = 3,
+
+    // Hash code was set but it does not match single account of required type.
+    [MinVersion=22] HASH_CODE_DO_NOT_MATCH_SINGLE_ACCOUNT = 4,
+
+    // Hash code was set but it does not match multiple accounts of required
+    // type.
+    [MinVersion=22] HASH_CODE_DO_NOT_MATCH_MULTIPLE_ACCOUNTS = 5,
+
+    // Hash code was set and it matches single account of required type.
+    [MinVersion=22] HASH_CODE_MATCH_SINGLE_ACCOUNT = 6,
+
+    // Hash code was set and it matches one of multiple accounts of required
+    // type.
+    [MinVersion=22] HASH_CODE_MATCH_MULTIPLE_ACCOUNTS = 7,
+
+    // Hash code collides with multiple accounts of required type.
+    [MinVersion=22] HASH_CODE_COLLIDE_FOR_MULTIPLE_ACCOUNTS = 8,
+
+    // NOTE: If you add any entries to this enum, you must also update the
+    // corresponding UMA ArcAuthMainAccountResolutionStatus at
+    // tools/metrics/histograms/enums.xml.
+};
+
 // These values describe the result of ARC attempting to change supervision
 // state after an account type change.
 [Extensible]
@@ -299,7 +338,7 @@
   [MinVersion=20] HandleUpdateCredentialsRequest@17(string account_name);
 };
 
-// Next Method ID: 5
+// Next Method ID: 6
 interface AuthInstance {
   // DEPRECATED: Please use Init@2 instead.
   InitDeprecated@0(AuthHost host_ptr);
@@ -328,4 +367,8 @@
   // Gets the list of Google accounts in ARC. Called during the one-time
   // migration of accounts from ARC to Chrome OS Account Manager.
   [MinVersion=19] GetGoogleAccounts@4() => (array<ArcAccountInfo> accounts);
+
+  // Gets resolution status of main account for statistics reporting.
+  [MinVersion=22] GetMainAccountResolutionStatus@5()
+      => (MainAccountResolutionStatus status);
 };
diff --git a/components/domain_reliability/quic_error_mapping.cc b/components/domain_reliability/quic_error_mapping.cc
index 53789d7..df33039 100644
--- a/components/domain_reliability/quic_error_mapping.cc
+++ b/components/domain_reliability/quic_error_mapping.cc
@@ -316,6 +316,10 @@
     {quic::QUIC_QPACK_ENCODER_STREAM_ERROR, "QUIC.QPACK.ENCODER.STREAM.ERROR"},
     {quic::QUIC_QPACK_DECODER_STREAM_ERROR, "QUIC.QPACK.DECODER.STREAM.ERROR"},
 
+    {quic::QUIC_STREAM_DATA_BEYOND_CLOSE_OFFSET,
+     "quic.stream.data.beyond.close.offset"},
+    {quic::QUIC_STREAM_MULTIPLE_OFFSET, "quic.stream.multiple.offset"},
+
     // QUIC_INVALID_APPLICATION_CLOSE_DATA was code 101. The code has been
     // deprecated, but to keep the assert below happy, there needs to be
     // an entry for it, but the symbol is gone.
diff --git a/components/gwp_asan/client/guarded_page_allocator_win.cc b/components/gwp_asan/client/guarded_page_allocator_win.cc
index 92b72a14..cbad675 100644
--- a/components/gwp_asan/client/guarded_page_allocator_win.cc
+++ b/components/gwp_asan/client/guarded_page_allocator_win.cc
@@ -13,10 +13,13 @@
 namespace internal {
 
 void* GuardedPageAllocator::MapRegion() {
-  if (void* hint = MapRegionHint())
-    if (void* ptr =
-            VirtualAlloc(hint, RegionSize(), MEM_RESERVE, PAGE_NOACCESS))
+  // Number of times to try to map the region in high memory before giving up.
+  constexpr size_t kHintTries = 5;
+  for (size_t i = 0; i < kHintTries; i++) {
+    if (void* ptr = VirtualAlloc(MapRegionHint(), RegionSize(), MEM_RESERVE,
+                                 PAGE_NOACCESS))
       return ptr;
+  }
 
   return VirtualAlloc(nullptr, RegionSize(), MEM_RESERVE, PAGE_NOACCESS);
 }
diff --git a/components/module_installer/android/java/src/org/chromium/components/module_installer/builder/Module.java b/components/module_installer/android/java/src/org/chromium/components/module_installer/builder/Module.java
index c27bd09..33ec630 100644
--- a/components/module_installer/android/java/src/org/chromium/components/module_installer/builder/Module.java
+++ b/components/module_installer/android/java/src/org/chromium/components/module_installer/builder/Module.java
@@ -132,22 +132,18 @@
     private static void loadNative(String name) {
         // Can only initialize native once per lifetime of Chrome.
         if (sInitializedModules.contains(name)) return;
-        // TODO(crbug.com/870055): Use |libraries| instead of whitelist to load
-        // native libraries.
         String[] libraries = loadModuleDescriptor(name).getLibraries();
         // TODO(crbug.com/986960): Automatically determine if module has native
         // resources instead of whitelisting.
-        boolean loadLibrary = false;
         boolean loadResources = false;
         if ("test_dummy".equals(name)) {
-            loadLibrary = true;
             loadResources = true;
         }
         if ("dev_ui".equals(name)) {
             loadResources = true;
         }
-        if (loadLibrary || loadResources) {
-            ModuleJni.get().loadNative(name, loadLibrary, loadResources);
+        if (libraries.length > 0 || loadResources) {
+            ModuleJni.get().loadNative(name, libraries, loadResources);
         }
         sInitializedModules.add(name);
     }
@@ -195,6 +191,6 @@
 
     @NativeMethods
     interface Natives {
-        void loadNative(String name, boolean loadLibrary, boolean loadResources);
+        void loadNative(String name, String[] libraries, boolean loadResources);
     }
 }
diff --git a/components/module_installer/android/module.cc b/components/module_installer/android/module.cc
index 73be75e..b51e91f 100644
--- a/components/module_installer/android/module.cc
+++ b/components/module_installer/android/module.cc
@@ -7,6 +7,7 @@
 
 #include "base/android/bundle_utils.h"
 #include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
@@ -40,8 +41,8 @@
 
 typedef bool JniRegistrationFunction(JNIEnv* env);
 
-void LoadLibrary(JNIEnv* env, const std::string& library_name) {
-  JniRegistrationFunction* registration_function = nullptr;
+void* LoadLibrary(const std::string& library_name) {
+  void* library_handle = nullptr;
 
 #if defined(LOAD_FROM_PARTITIONS)
   // The partition library must be opened via native code (using
@@ -49,25 +50,28 @@
   // operation on the Java side, because JNI registration is done explicitly
   // (hence there is no reason for the Java ClassLoader to be aware of the
   // library, for lazy JNI registration).
-  void* library_handle =
-      BundleUtils::DlOpenModuleLibraryPartition(library_name);
+  library_handle = BundleUtils::DlOpenModuleLibraryPartition(library_name);
 #elif defined(COMPONENT_BUILD)
-  const std::string lib_name = "lib" + library_name + ".cr.so";
-  void* library_handle = dlopen(lib_name.c_str(), RTLD_LOCAL);
+  const std::string lib_name = "lib" + library_name + ".so";
+  library_handle = dlopen(lib_name.c_str(), RTLD_LOCAL);
 #else
 #error "Unsupported configuration."
 #endif  // defined(COMPONENT_BUILD)
   CHECK(library_handle != nullptr)
-      << "Could not open feature library:" << dlerror();
+      << "Could not open feature library: " << dlerror();
 
-  const std::string registration_name = "JNI_OnLoad_" + library_name;
+  return library_handle;
+}
+
+void RegisterJni(JNIEnv* env, void* library_handle, const std::string& name) {
+  const std::string registration_name = "JNI_OnLoad_" + name;
   // Find the module's JNI registration method from the feature library.
   void* symbol = dlsym(library_handle, registration_name.c_str());
-  CHECK(symbol != nullptr) << "Could not find JNI registration method: "
-                           << dlerror();
-  registration_function = reinterpret_cast<JniRegistrationFunction*>(symbol);
-  CHECK(registration_function(env))
-      << "JNI registration failed: " << library_name;
+  CHECK(symbol) << "Could not find JNI registration method '"
+                << registration_name << "' for '" << name << "': " << dlerror();
+  auto* registration_function =
+      reinterpret_cast<JniRegistrationFunction*>(symbol);
+  CHECK(registration_function(env)) << "JNI registration failed: " << name;
 }
 
 void LoadResources(const std::string& name) {
@@ -80,12 +84,22 @@
 static void JNI_Module_LoadNative(
     JNIEnv* env,
     const base::android::JavaParamRef<jstring>& jname,
-    jboolean load_library,
+    const base::android::JavaParamRef<jobjectArray>& jlibraries,
     jboolean load_resources) {
   std::string name;
   base::android::ConvertJavaStringToUTF8(env, jname, &name);
-  if (load_library) {
-    LoadLibrary(env, name);
+  std::vector<std::string> libraries;
+  base::android::AppendJavaStringArrayToStringVector(env, jlibraries,
+                                                     &libraries);
+  if (libraries.size() > 0) {
+    void* library_handle = nullptr;
+    for (const auto& library : libraries) {
+      library_handle = LoadLibrary(library);
+    }
+    // module libraries are ordered such that the root library will be the last
+    // item in the list. We expect this library to provide the JNI registration
+    // function.
+    RegisterJni(env, library_handle, name);
   }
   if (load_resources) {
     LoadResources(name);
diff --git a/components/module_installer/android/module_desc_java.py b/components/module_installer/android/module_desc_java.py
index 4082fc7782..8825b50a 100755
--- a/components/module_installer/android/module_desc_java.py
+++ b/components/module_installer/android/module_desc_java.py
@@ -12,10 +12,10 @@
 
 sys.path.append(
     os.path.join(
-        os.path.dirname(__file__), '..', '..', '..', 'build', 'android', 'gyp'))
+        os.path.dirname(__file__), "..", "..", "..", "build", "android", "gyp"))
 from util import build_utils
 
-_TEMPLATE = '''\
+_TEMPLATE = """\
 // This file is autogenerated by
 //     components/module_installer/android/module_desc_java.py
 // Please do not change its content.
@@ -33,7 +33,7 @@
         return LIBRARIES;
     }}
 }}
-'''
+"""
 
 
 def main():
diff --git a/components/omnibox/browser/autocomplete_input_fuzzer.cc b/components/omnibox/browser/autocomplete_input_fuzzer.cc
index b5f1319..c193f79 100644
--- a/components/omnibox/browser/autocomplete_input_fuzzer.cc
+++ b/components/omnibox/browser/autocomplete_input_fuzzer.cc
@@ -10,6 +10,7 @@
 #include "base/at_exit.h"
 #include "base/i18n/icu_util.h"
 #include "base/strings/string16.h"
+#include "base/strings/utf_string_conversion_utils.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 
@@ -23,10 +24,20 @@
 IcuEnvironment icu_env;
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // Enforce a reasonable bound on what we believe it takes to trigger
+  // an error.
+  if (size > 4096)
+    return 0;
   // This fuzzer creates a random UTF16 string, for testing primarily against
   // AutocompleteInput::Parse().
   base::string16 s(reinterpret_cast<const base::string16::value_type*>(data),
                    size / sizeof(base::string16::value_type));
+  // Some characters are considered illegal and, while our code handles them
+  // fine, fuzzing runs with DCHECKs enabled which will trigger on them.
+  for (auto c : s) {
+    if (!base::IsValidCharacter(c))
+      return 0;
+  }
   AutocompleteInput input(s, metrics::OmniboxEventProto::OTHER,
                           TestSchemeClassifier());
   return 0;
diff --git a/components/omnibox/browser/autocomplete_provider_unittest.cc b/components/omnibox/browser/autocomplete_provider_unittest.cc
index 79ee090..3707100 100644
--- a/components/omnibox/browser/autocomplete_provider_unittest.cc
+++ b/components/omnibox/browser/autocomplete_provider_unittest.cc
@@ -33,11 +33,11 @@
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/search_engines/template_url_service_client.h"
-#include "net/url_request/url_request.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 #include "ui/gfx/image/image_unittest_util.h"
 #include "ui/gfx/image/image_util.h"
+#include "url/url_constants.h"
 
 static std::ostream& operator<<(std::ostream& os,
                                 const AutocompleteResult::const_iterator& it) {
@@ -55,7 +55,8 @@
 
   metrics::OmniboxInputType GetInputTypeForScheme(
       const std::string& scheme) const override {
-    return net::URLRequest::IsHandledProtocol(scheme)
+    DCHECK_EQ(scheme, base::ToLowerASCII(scheme));
+    return (scheme == url::kHttpScheme || scheme == url::kHttpsScheme)
                ? metrics::OmniboxInputType::URL
                : metrics::OmniboxInputType::EMPTY;
   }
diff --git a/components/omnibox/browser/keyword_provider_unittest.cc b/components/omnibox/browser/keyword_provider_unittest.cc
index e54922e..fbd6c092 100644
--- a/components/omnibox/browser/keyword_provider_unittest.cc
+++ b/components/omnibox/browser/keyword_provider_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/command_line.h"
 #include "base/metrics/field_trial.h"
 #include "base/stl_util.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/task_environment.h"
 #include "components/omnibox/browser/autocomplete_match.h"
@@ -24,11 +25,11 @@
 #include "components/search_engines/template_url_service.h"
 #include "components/variations/entropy_provider.h"
 #include "components/variations/variations_associated_data.h"
-#include "net/url_request/url_request.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
 #include "url/gurl.h"
+#include "url/url_constants.h"
 
 using base::ASCIIToUTF16;
 
@@ -38,7 +39,8 @@
  public:
   metrics::OmniboxInputType GetInputTypeForScheme(
       const std::string& scheme) const override {
-    if (net::URLRequest::IsHandledProtocol(scheme))
+    DCHECK_EQ(scheme, base::ToLowerASCII(scheme));
+    if (scheme == url::kHttpScheme || scheme == url::kHttpsScheme)
       return metrics::OmniboxInputType::URL;
     return metrics::OmniboxInputType::EMPTY;
   }
diff --git a/components/omnibox/browser/test_scheme_classifier.cc b/components/omnibox/browser/test_scheme_classifier.cc
index 1d706656..e1a5bfa 100644
--- a/components/omnibox/browser/test_scheme_classifier.cc
+++ b/components/omnibox/browser/test_scheme_classifier.cc
@@ -7,8 +7,8 @@
 #include <string>
 
 #include "base/stl_util.h"
+#include "base/strings/string_util.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
-#include "net/url_request/url_request.h"
 #include "third_party/metrics_proto/omnibox_input_type.pb.h"
 #include "url/url_constants.h"
 
@@ -18,17 +18,20 @@
 
 metrics::OmniboxInputType TestSchemeClassifier::GetInputTypeForScheme(
     const std::string& scheme) const {
+  DCHECK_EQ(scheme, base::ToLowerASCII(scheme));
+
   // This doesn't check the preference but check some chrome-ish schemes.
   const char* kKnownURLSchemes[] = {
-    url::kFileScheme, url::kAboutScheme, url::kFtpScheme, url::kBlobScheme,
-    url::kFileSystemScheme, "view-source", "javascript", "chrome", "chrome-ui",
+      url::kHttpScheme, url::kHttpsScheme, url::kWsScheme,
+      url::kWssScheme,  url::kFileScheme,  url::kAboutScheme,
+      url::kFtpScheme,  url::kBlobScheme,  url::kFileSystemScheme,
+      "view-source",    "javascript",      "chrome",
+      "chrome-ui",
   };
-  for (size_t i = 0; i < base::size(kKnownURLSchemes); ++i) {
-    if (scheme == kKnownURLSchemes[i])
+  for (const char* known_scheme : kKnownURLSchemes) {
+    if (scheme == known_scheme)
       return metrics::OmniboxInputType::URL;
   }
-  if (net::URLRequest::IsHandledProtocol(scheme))
-    return metrics::OmniboxInputType::URL;
 
   return metrics::OmniboxInputType::EMPTY;
 }
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 4ea9be9..6fdd4199 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -309,6 +309,13 @@
 const base::Feature kZeroSuggestionsOnSERP{"OmniboxZeroSuggestionsOnSERP",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
+// If enabled, changes the way Google-provided search suggestions are scored by
+// the backend. Note that this Feature is only used for triggering a server-
+// side experiment config that will send experiment IDs to the backend. It is
+// not referred to in any of the Chromium code.
+const base::Feature kOmniboxExperimentalSuggestScoring{
+    "OmniboxExperimentalSuggestScoring", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If enabled, shows a confirm dialog before removing search suggestions from
 // the omnibox. See ConfirmNtpSuggestionRemovals for the NTP equivalent.
 const base::Feature kConfirmOmniboxSuggestionRemovals{
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index f9c6ed2e..1dcf24d 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -61,6 +61,9 @@
 extern const base::Feature kZeroSuggestionsOnNTPRealbox;
 extern const base::Feature kZeroSuggestionsOnSERP;
 
+// Scoring - these affect how relevance scores are calculated for suggestions.
+extern const base::Feature kOmniboxExperimentalSuggestScoring;
+
 // Suggestions UI - these affect the UI or function of the suggestions popup.
 extern const base::Feature kConfirmOmniboxSuggestionRemovals;
 
diff --git a/components/password_manager/ios/BUILD.gn b/components/password_manager/ios/BUILD.gn
index 78759941..cb9a66d 100644
--- a/components/password_manager/ios/BUILD.gn
+++ b/components/password_manager/ios/BUILD.gn
@@ -14,6 +14,9 @@
     "//components/autofill/ios/form_util",
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser/form_parsing",
+    "//components/password_manager/core/common",
+    "//components/security_state/ios",
+    "//ios/web/common",
     "//ios/web/public",
     "//ios/web/public/deprecated",
     "//ios/web/public/js_messaging",
@@ -23,6 +26,8 @@
   sources = [
     "account_select_fill_data.cc",
     "account_select_fill_data.h",
+    "credential_manager_util.h",
+    "credential_manager_util.mm",
     "js_password_manager.h",
     "js_password_manager.mm",
     "password_form_helper.h",
@@ -52,6 +57,7 @@
   testonly = true
   sources = [
     "account_select_fill_data_unittest.cc",
+    "credential_manager_util_unittest.cc",
     "password_form_helper_unittest.mm",
   ]
   deps = [
diff --git a/components/password_manager/ios/DEPS b/components/password_manager/ios/DEPS
index 67aac39d..729b9e1 100644
--- a/components/password_manager/ios/DEPS
+++ b/components/password_manager/ios/DEPS
@@ -3,5 +3,7 @@
   "+components/autofill/core/common",
   "+components/autofill/ios/browser",
   "+components/autofill/ios/form_util",
+  "+components/security_state/ios",
   "+ios/web/public",
+  "+ios/web/common",
 ]
diff --git a/ios/chrome/browser/passwords/credential_manager_util.h b/components/password_manager/ios/credential_manager_util.h
similarity index 93%
rename from ios/chrome/browser/passwords/credential_manager_util.h
rename to components/password_manager/ios/credential_manager_util.h
index 2ba6b79..6d41722 100644
--- a/ios/chrome/browser/passwords/credential_manager_util.h
+++ b/components/password_manager/ios/credential_manager_util.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_PASSWORDS_CREDENTIAL_MANAGER_UTIL_H_
-#define IOS_CHROME_BROWSER_PASSWORDS_CREDENTIAL_MANAGER_UTIL_H_
+#ifndef COMPONENTS_PASSWORD_MANAGER_IOS_CREDENTIAL_MANAGER_UTIL_H_
+#define COMPONENTS_PASSWORD_MANAGER_IOS_CREDENTIAL_MANAGER_UTIL_H_
 
 #include "base/values.h"
 #include "components/password_manager/core/common/credential_manager_types.h"
@@ -12,6 +12,8 @@
 class WebState;
 }
 
+namespace password_manager {
+
 // Keys for obtaining common Credential's fields from DictionaryValue
 // representing the Credential. Keys below correspond to JavaScript
 // Credential object fields as follows:
@@ -87,4 +89,6 @@
 // ignore API calls from insecure context.
 bool WebStateContentIsSecureHtml(const web::WebState* web_state);
 
-#endif  // IOS_CHROME_BROWSER_PASSWORDS_CREDENTIAL_MANAGER_UTIL_H_
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_IOS_CREDENTIAL_MANAGER_UTIL_H_
diff --git a/ios/chrome/browser/passwords/credential_manager_util.mm b/components/password_manager/ios/credential_manager_util.mm
similarity index 89%
rename from ios/chrome/browser/passwords/credential_manager_util.mm
rename to components/password_manager/ios/credential_manager_util.mm
index ee6108c9..78ce592 100644
--- a/ios/chrome/browser/passwords/credential_manager_util.mm
+++ b/components/password_manager/ios/credential_manager_util.mm
@@ -2,37 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/browser/passwords/credential_manager_util.h"
+#include "components/password_manager/ios/credential_manager_util.h"
 
-#include "components/security_state/core/security_state.h"
-#include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
+#include "components/security_state/ios/security_state_utils.h"
 #import "ios/web/common/origin_util.h"
+#import "ios/web/public/web_state.h"
 #include "url/origin.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-using password_manager::CredentialManagerError;
-using password_manager::CredentialInfo;
-using password_manager::CredentialType;
-using password_manager::CredentialMediationRequirement;
-
-namespace {
-
-security_state::SecurityLevel GetSecurityLevelForWebState(
-    const web::WebState* web_state) {
-  if (!web_state) {
-    return security_state::NONE;
-  }
-  auto* client = IOSSecurityStateTabHelper::FromWebState(web_state);
-  if (!client) {
-    return security_state::NONE;
-  }
-  return client->GetSecurityLevel();
-}
-
-}  // namespace
+namespace password_manager {
 
 const char kCredentialIdKey[] = "_id";
 const char kCredentialTypeKey[] = "_type";
@@ -221,6 +202,8 @@
 
   // If scheme is cryptographic, valid SSL certificate is required.
   security_state::SecurityLevel security_level =
-      GetSecurityLevelForWebState(web_state);
+      security_state::GetSecurityLevelForWebState(web_state);
   return security_state::IsSslCertificateValid(security_level);
 }
+
+}  // namespace password_manager
diff --git a/ios/chrome/browser/passwords/credential_manager_util_unittest.cc b/components/password_manager/ios/credential_manager_util_unittest.cc
similarity index 97%
rename from ios/chrome/browser/passwords/credential_manager_util_unittest.cc
rename to components/password_manager/ios/credential_manager_util_unittest.cc
index afc8728..82ed4d7 100644
--- a/ios/chrome/browser/passwords/credential_manager_util_unittest.cc
+++ b/components/password_manager/ios/credential_manager_util_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/browser/passwords/credential_manager_util.h"
+#include "components/password_manager/ios/credential_manager_util.h"
 
 #include <memory>
 
@@ -13,9 +13,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
-using password_manager::CredentialInfo;
-using password_manager::CredentialMediationRequirement;
-using password_manager::CredentialType;
+namespace password_manager {
 
 namespace {
 
@@ -254,3 +252,5 @@
   json.SetString(kCredentialRequestProvidersKey, kTestWebOrigin);
   EXPECT_FALSE(ParseFederations(json, &federations));
 }
+
+}  // namespace password_manager
diff --git a/components/search_engines/template_url_fetcher.cc b/components/search_engines/template_url_fetcher.cc
index 40b4abb7..79fe7dd 100644
--- a/components/search_engines/template_url_fetcher.cc
+++ b/components/search_engines/template_url_fetcher.cc
@@ -160,7 +160,8 @@
 
   template_url_ = TemplateURLParser::Parse(
       fetcher_->template_url_service_->search_terms_data(),
-      response_body->data(), response_body->length(), nullptr);
+      response_body->data(), response_body->length(),
+      TemplateURLParser::ParameterFilter());
   if (!template_url_ ||
       !template_url_->url_ref().SupportsReplacement(
           fetcher_->template_url_service_->search_terms_data())) {
diff --git a/components/search_engines/template_url_parser.cc b/components/search_engines/template_url_parser.cc
index 08c56f8b..96977a7 100644
--- a/components/search_engines/template_url_parser.cc
+++ b/components/search_engines/template_url_parser.cc
@@ -132,7 +132,7 @@
   typedef std::pair<std::string, std::string> Param;
 
   explicit TemplateURLParsingContext(
-      TemplateURLParser::ParameterFilter* parameter_filter);
+      const TemplateURLParser::ParameterFilter& parameter_filter);
 
   static void StartElementImpl(void* ctx,
                                const xmlChar* name,
@@ -173,7 +173,7 @@
   // Character content for the current element.
   base::string16 string_;
 
-  TemplateURLParser::ParameterFilter* parameter_filter_;
+  const TemplateURLParser::ParameterFilter& parameter_filter_;
 
   // The list of parameters parsed in the Param nodes of a Url node.
   std::vector<Param> extra_params_;
@@ -202,7 +202,7 @@
     TemplateURLParsingContext::kElementNameToElementTypeMap = nullptr;
 
 TemplateURLParsingContext::TemplateURLParsingContext(
-    TemplateURLParser::ParameterFilter* parameter_filter)
+    const TemplateURLParser::ParameterFilter& parameter_filter)
     : image_is_valid_for_favicon_(false),
       parameter_filter_(parameter_filter),
       method_(GET),
@@ -428,12 +428,12 @@
   }
 
   if (!key.empty() &&
-      (!parameter_filter_ || parameter_filter_->KeepParameter(key, value)))
+      (parameter_filter_.is_null() || parameter_filter_.Run(key, value)))
     extra_params_.push_back(Param(key, value));
 }
 
 void TemplateURLParsingContext::ProcessURLParams() {
-  if (!parameter_filter_ && extra_params_.empty())
+  if (parameter_filter_.is_null() && extra_params_.empty())
     return;
 
   GURL url(is_suggest_url_ ? data_.suggestions_url : data_.url());
@@ -444,14 +444,14 @@
   // unwanted parameter.
   std::string new_query;
   bool modified = false;
-  if (parameter_filter_) {
+  if (!parameter_filter_.is_null()) {
     url::Component query = url.parsed_for_possibly_invalid_spec().query;
     url::Component key, value;
     const char* url_spec = url.spec().c_str();
     while (url::ExtractQueryKeyValue(url_spec, &query, &key, &value)) {
       std::string key_str(url_spec, key.begin, key.len);
       std::string value_str(url_spec, value.begin, value.len);
-      if (parameter_filter_->KeepParameter(key_str, value_str)) {
+      if (parameter_filter_.Run(key_str, value_str)) {
         AppendParamToQuery(key_str, value_str, &new_query);
       } else {
         modified = true;
@@ -497,7 +497,7 @@
     const SearchTermsData& search_terms_data,
     const char* data,
     size_t length,
-    TemplateURLParser::ParameterFilter* param_filter) {
+    const TemplateURLParser::ParameterFilter& param_filter) {
   // xmlSubstituteEntitiesDefault(1) makes it so that &amp; isn't mapped to
   // &#38; . Unfortunately xmlSubstituteEntitiesDefault affects global state.
   // If this becomes problematic we'll need to provide our own entity
diff --git a/components/search_engines/template_url_parser.h b/components/search_engines/template_url_parser.h
index c381f7a0..789b0ad 100644
--- a/components/search_engines/template_url_parser.h
+++ b/components/search_engines/template_url_parser.h
@@ -10,6 +10,7 @@
 #include <memory>
 #include <string>
 
+#include "base/callback.h"
 #include "base/macros.h"
 
 class SearchTermsData;
@@ -19,16 +20,12 @@
 // from OpenSearch description documents.
 class TemplateURLParser {
  public:
-  class ParameterFilter {
-   public:
-    // Invoked for each parameter of the template URL while parsing.  If this
-    // methods returns false, the parameter is not included.
-    virtual bool KeepParameter(const std::string& key,
-                               const std::string& value) = 0;
-
-   protected:
-    virtual ~ParameterFilter() {}
-  };
+  // A ParameterFilter is called for every URL paramter encountered during
+  // Parse(). It passes the parameter key as the first argument and the value
+  // as the second. The callback should return true if the parameter should be
+  // kept, and false if it should be discarded.
+  using ParameterFilter =
+      base::RepeatingCallback<bool(const std::string&, const std::string&)>;
 
   // Decodes the chunk of data representing a TemplateURL, creates the
   // TemplateURL, and returns it.  Returns null if the data does not describe a
@@ -42,7 +39,7 @@
       const SearchTermsData& search_terms_data,
       const char* data,
       size_t length,
-      ParameterFilter* parameter_filter);
+      const ParameterFilter& parameter_filter);
 
  private:
   // No one should create one of these.
diff --git a/components/security_state/ios/BUILD.gn b/components/security_state/ios/BUILD.gn
index 2111331..a52dc987 100644
--- a/components/security_state/ios/BUILD.gn
+++ b/components/security_state/ios/BUILD.gn
@@ -2,18 +2,36 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/jumbo.gni")
-
-jumbo_source_set("ios") {
+component("ios") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
+    "insecure_input_tab_helper.h",
+    "insecure_input_tab_helper.mm",
+    "security_state_utils.h",
+    "security_state_utils.mm",
     "ssl_status_input_event_data.h",
     "ssl_status_input_event_data.mm",
   ]
   deps = [
     "//base",
-    "//components/security_state/core/",
-    "//ios/web/public/",
+    "//components/autofill/ios/form_util",
+    "//components/security_state/core",
+    "//ios/web/common",
+    "//ios/web/public",
     "//ios/web/public/security",
   ]
 }
+
+source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "security_state_utils_unittest.mm",
+  ]
+  deps = [
+    ":ios",
+    "//components/security_state/core",
+    "//ios/web/public/security",
+    "//ios/web/public/test",
+  ]
+}
diff --git a/components/security_state/ios/DEPS b/components/security_state/ios/DEPS
index 0eca071..7bbff89 100644
--- a/components/security_state/ios/DEPS
+++ b/components/security_state/ios/DEPS
@@ -1,4 +1,6 @@
 include_rules = [
+  "+components/autofill/ios",
   "+components/security_state/core",
+  "+ios/web/common",
   "+ios/web/public",
 ]
diff --git a/ios/chrome/browser/ssl/insecure_input_tab_helper.h b/components/security_state/ios/insecure_input_tab_helper.h
similarity index 89%
rename from ios/chrome/browser/ssl/insecure_input_tab_helper.h
rename to components/security_state/ios/insecure_input_tab_helper.h
index 83828ad..d20edb9 100644
--- a/ios/chrome/browser/ssl/insecure_input_tab_helper.h
+++ b/components/security_state/ios/insecure_input_tab_helper.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_SSL_INSECURE_INPUT_TAB_HELPER_H_
-#define IOS_CHROME_BROWSER_SSL_INSECURE_INPUT_TAB_HELPER_H_
+#ifndef COMPONENTS_SECURITY_STATE_IOS_INSECURE_INPUT_TAB_HELPER_H_
+#define COMPONENTS_SECURITY_STATE_IOS_INSECURE_INPUT_TAB_HELPER_H_
 
 #include <string>
 
@@ -54,4 +54,4 @@
   DISALLOW_COPY_AND_ASSIGN(InsecureInputTabHelper);
 };
 
-#endif  // IOS_CHROME_BROWSER_SSL_INSECURE_INPUT_TAB_HELPER_H_
+#endif  // COMPONENTS_SECURITY_STATE_IOS_INSECURE_INPUT_TAB_HELPER_H_
diff --git a/ios/chrome/browser/ssl/insecure_input_tab_helper.mm b/components/security_state/ios/insecure_input_tab_helper.mm
similarity index 97%
rename from ios/chrome/browser/ssl/insecure_input_tab_helper.mm
rename to components/security_state/ios/insecure_input_tab_helper.mm
index fd740bcb..d8cb5bb 100644
--- a/ios/chrome/browser/ssl/insecure_input_tab_helper.mm
+++ b/components/security_state/ios/insecure_input_tab_helper.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/ssl/insecure_input_tab_helper.h"
+#import "components/security_state/ios/insecure_input_tab_helper.h"
 
 #import <Foundation/Foundation.h>
 
@@ -18,7 +18,6 @@
 #import "ios/web/public/navigation/navigation_manager.h"
 #import "ios/web/public/web_state.h"
 #import "ios/web/public/web_state_user_data.h"
-#include "ui/base/page_transition_types.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/components/security_state/ios/security_state_utils.h b/components/security_state/ios/security_state_utils.h
new file mode 100644
index 0000000..55b8017
--- /dev/null
+++ b/components/security_state/ios/security_state_utils.h
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SECURITY_STATE_IOS_SECURITY_STATE_UTILS_H_
+#define COMPONENTS_SECURITY_STATE_IOS_SECURITY_STATE_UTILS_H_
+
+#include "components/security_state/core/security_state.h"
+
+namespace web {
+class WebState;
+}  // namespace web
+
+namespace security_state {
+
+// Returns |web_state|'s security_state::VisibleSecurityState.
+std::unique_ptr<security_state::VisibleSecurityState>
+GetVisibleSecurityStateForWebState(const web::WebState* web_state);
+
+// Returns the SecurityLevel for |web_state|.
+security_state::SecurityLevel GetSecurityLevelForWebState(
+    const web::WebState* web_state);
+
+}  // namespace security_state
+
+#endif  // COMPONENTS_SECURITY_STATE_IOS_SECURITY_STATE_UTILS_H_
diff --git a/ios/chrome/browser/ssl/ios_security_state_tab_helper.mm b/components/security_state/ios/security_state_utils.mm
similarity index 60%
rename from ios/chrome/browser/ssl/ios_security_state_tab_helper.mm
rename to components/security_state/ios/security_state_utils.mm
index ae2e22b..7468902 100644
--- a/ios/chrome/browser/ssl/ios_security_state_tab_helper.mm
+++ b/components/security_state/ios/security_state_utils.mm
@@ -1,46 +1,29 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
+#include "components/security_state/ios/security_state_utils.h"
 
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram_macros.h"
+#include <memory>
+
 #include "components/security_state/core/security_state.h"
 #include "components/security_state/ios/ssl_status_input_event_data.h"
 #import "ios/web/common/origin_util.h"
-#include "ios/web/public/browser_state.h"
 #include "ios/web/public/navigation/navigation_item.h"
 #import "ios/web/public/navigation/navigation_manager.h"
 #include "ios/web/public/security/security_style.h"
 #include "ios/web/public/security/ssl_status.h"
 #import "ios/web/public/web_state.h"
-#include "net/cert/x509_certificate.h"
+#include "url/origin.h"
 
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-IOSSecurityStateTabHelper::IOSSecurityStateTabHelper(web::WebState* web_state)
-    : web_state_(web_state) {}
-
-IOSSecurityStateTabHelper::~IOSSecurityStateTabHelper() {}
-
-security_state::SecurityLevel IOSSecurityStateTabHelper::GetSecurityLevel()
-    const {
-  return security_state::GetSecurityLevel(
-      *GetVisibleSecurityState(), false /* used policy installed certificate */,
-      base::BindRepeating(&web::IsOriginSecure));
-}
+namespace security_state {
 
 std::unique_ptr<security_state::VisibleSecurityState>
-IOSSecurityStateTabHelper::GetVisibleSecurityState() const {
+GetVisibleSecurityStateForWebState(const web::WebState* web_state) {
   auto state = std::make_unique<security_state::VisibleSecurityState>();
 
   const web::NavigationItem* item =
-      web_state_->GetNavigationManager()->GetVisibleItem();
+      web_state->GetNavigationManager()->GetVisibleItem();
   if (!item || item->GetSSL().security_style == web::SECURITY_STYLE_UNKNOWN)
     return state;
 
@@ -62,4 +45,15 @@
   return state;
 }
 
-WEB_STATE_USER_DATA_KEY_IMPL(IOSSecurityStateTabHelper)
+security_state::SecurityLevel GetSecurityLevelForWebState(
+    const web::WebState* web_state) {
+  if (!web_state) {
+    return security_state::NONE;
+  }
+  return security_state::GetSecurityLevel(
+      *GetVisibleSecurityStateForWebState(web_state),
+      false /* used policy installed certificate */,
+      base::BindRepeating(&web::IsOriginSecure));
+}
+
+}  // namespace security_state
diff --git a/ios/chrome/browser/ssl/ios_security_state_tab_helper_unittest.mm b/components/security_state/ios/security_state_utils_unittest.mm
similarity index 82%
rename from ios/chrome/browser/ssl/ios_security_state_tab_helper_unittest.mm
rename to components/security_state/ios/security_state_utils_unittest.mm
index 5f8e91f..e640949 100644
--- a/ios/chrome/browser/ssl/ios_security_state_tab_helper_unittest.mm
+++ b/components/security_state/ios/security_state_utils_unittest.mm
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
+#include "components/security_state/ios/security_state_utils.h"
 
 #include "components/security_state/core/security_state.h"
-#import "ios/chrome/browser/ssl/insecure_input_tab_helper.h"
+#import "components/security_state/ios/insecure_input_tab_helper.h"
 #import "ios/web/public/navigation/navigation_item.h"
 #import "ios/web/public/navigation/navigation_manager.h"
 #include "ios/web/public/security/ssl_status.h"
@@ -18,7 +18,7 @@
 // This test fixture creates an IOSSecurityStateTabHelper and an
 // InsecureInputTabHelper for the WebState, then loads a non-secure
 // HTML document.
-class IOSSecurityStateTabHelperTest : public web::WebTestWithWebState {
+class SecurityStateUtilsTest : public web::WebTestWithWebState {
  protected:
   void SetUp() override {
     web::WebTestWithWebState::SetUp();
@@ -26,14 +26,12 @@
     insecure_input_ = InsecureInputTabHelper::FromWebState(web_state());
     ASSERT_TRUE(insecure_input_);
 
-    IOSSecurityStateTabHelper::CreateForWebState(web_state());
     LoadHtml(@"<html><body></body></html>", GURL("http://chromium.test"));
   }
 
   // Returns the InsecureInputEventData for current WebState().
   security_state::InsecureInputEventData GetInsecureInputEventData() const {
-    return IOSSecurityStateTabHelper::FromWebState(web_state())
-        ->GetVisibleSecurityState()
+    return security_state::GetVisibleSecurityStateForWebState(web_state())
         ->insecure_input_events;
   }
 
@@ -45,7 +43,7 @@
 
 // Ensures that |insecure_field_edited| is set only when an editing event has
 // been reported.
-TEST_F(IOSSecurityStateTabHelperTest, SecurityInfoAfterEditing) {
+TEST_F(SecurityStateUtilsTest, SecurityInfoAfterEditing) {
   // Verify |insecure_field_edited| is not set prematurely.
   security_state::InsecureInputEventData events = GetInsecureInputEventData();
   EXPECT_FALSE(events.insecure_field_edited);
@@ -59,7 +57,7 @@
 
 // Ensures that re-navigating to the same page does not keep
 // |insecure_field_set| set.
-TEST_F(IOSSecurityStateTabHelperTest, InsecureInputClearedOnRenavigation) {
+TEST_F(SecurityStateUtilsTest, InsecureInputClearedOnRenavigation) {
   // Simulate an edit and verify |insecure_field_edited| is noted in the
   // insecure_input_events.
   insecure_input()->DidEditFieldInInsecureContext();
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index ea27b59..f21bbd6 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -957,9 +957,8 @@
       DrawRenderPassQuad(RenderPassDrawQuad::MaterialCast(quad), params);
       break;
     case DrawQuad::Material::kSolidColor:
-      // TODO(michaelludwig) - Support solid color quads bypassing a renderpass
-      DCHECK(rpdq_params == nullptr);
-      DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad), params);
+      DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad), rpdq_params,
+                         params);
       break;
     case DrawQuad::Material::kStreamVideoContent:
       // TODO(michaelludwig) - Support video quads bypassing a renderpass
@@ -1276,7 +1275,7 @@
 
 const DrawQuad* SkiaRenderer::CanPassBeDrawnDirectly(const RenderPass* pass) {
   // TODO(michaelludwig) - For now, this only supports opaque, src-over
-  // TileDrawQuads with invertable transforms.
+  // TileDrawQuads and SolidColor quads with invertable transforms.
   // It will be updated to select more quad material types as their
   // corresponding DrawXQuad() functions are updated to accept DrawRPDQParams
   // as well.
@@ -1291,7 +1290,8 @@
   const DrawQuad* quad = *pass->quad_list.BackToFrontBegin();
   // TODO(michaelludwig) - The set of supported material types can be expanded
   // by passing the DrawRPDQParams to the other DrawXQuad() functions.
-  if (quad->material != DrawQuad::Material::kTiledContent)
+  if (quad->material != DrawQuad::Material::kTiledContent &&
+      quad->material != DrawQuad::Material::kSolidColor)
     return nullptr;
 
   // In order to concatenate the bypass'ed quads transform with RP itself, it
@@ -1690,8 +1690,9 @@
 }
 
 void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad,
+                                      const DrawRPDQParams* rpdq_params,
                                       DrawQuadParams* params) {
-  DrawColoredQuad(quad->color, nullptr, params);
+  DrawColoredQuad(quad->color, rpdq_params, params);
 }
 
 void SkiaRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad,
diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h
index 1b3807e8..0854d0c 100644
--- a/components/viz/service/display/skia_renderer.h
+++ b/components/viz/service/display/skia_renderer.h
@@ -174,16 +174,20 @@
                        SkPaint* paint,
                        DrawQuadParams* params);
 
-  // DebugBorder, Picture, RPDQ, and SolidColor quads cannot be batched. They
-  // either are not textures (debug, picture, solid color), or it's very likely
-  // the texture will have advanced paint effects (rpdq)
+  // RPDQ, DebugBorder and picture quads cannot be batched. They
+  // either are not textures (debug, picture), or it's very likely
+  // the texture will have advanced paint effects (rpdq). Additionally, they do
+  // not support being drawn directly for a pass-through RenderPass.
   void DrawRenderPassQuad(const RenderPassDrawQuad* quad,
                           DrawQuadParams* params);
   void DrawDebugBorderQuad(const DebugBorderDrawQuad* quad,
                            DrawQuadParams* params);
   void DrawPictureQuad(const PictureDrawQuad* quad, DrawQuadParams* params);
 
+  // Solid-color quads are not batchable, but can be drawn directly in place of
+  // a RenderPass (hence it takes the optional DrawRPDQParams).
   void DrawSolidColorQuad(const SolidColorDrawQuad* quad,
+                          const DrawRPDQParams* rpdq_params,
                           DrawQuadParams* params);
 
   void DrawStreamVideoQuad(const StreamVideoDrawQuad* quad,
diff --git a/components/viz/test/data/rotated_filter_skia_gl.png b/components/viz/test/data/rotated_filter_skia_gl.png
index 87175aa..93b2e10 100644
--- a/components/viz/test/data/rotated_filter_skia_gl.png
+++ b/components/viz/test/data/rotated_filter_skia_gl.png
Binary files differ
diff --git a/components/viz/test/data/rotated_filter_skia_vk.png b/components/viz/test/data/rotated_filter_skia_vk.png
index 5e2f6b6..93b2e10 100644
--- a/components/viz/test/data/rotated_filter_skia_vk.png
+++ b/components/viz/test/data/rotated_filter_skia_vk.png
Binary files differ
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 8dc2a22..c87da31d 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1038,10 +1038,14 @@
     "indexed_db/indexed_db_value.h",
     "indexed_db/leveldb/leveldb_env.cc",
     "indexed_db/leveldb/leveldb_env.h",
+    "indexed_db/leveldb/leveldb_factory.cc",
+    "indexed_db/leveldb/leveldb_factory.h",
     "indexed_db/leveldb/leveldb_write_batch.cc",
     "indexed_db/leveldb/leveldb_write_batch.h",
     "indexed_db/leveldb/transactional_leveldb_database.cc",
     "indexed_db/leveldb/transactional_leveldb_database.h",
+    "indexed_db/leveldb/transactional_leveldb_factory.cc",
+    "indexed_db/leveldb/transactional_leveldb_factory.h",
     "indexed_db/leveldb/transactional_leveldb_iterator.cc",
     "indexed_db/leveldb/transactional_leveldb_iterator.h",
     "indexed_db/leveldb/transactional_leveldb_transaction.cc",
diff --git a/content/browser/appcache/appcache_request_handler.cc b/content/browser/appcache/appcache_request_handler.cc
index e5364988..6316400 100644
--- a/content/browser/appcache/appcache_request_handler.cc
+++ b/content/browser/appcache/appcache_request_handler.cc
@@ -16,10 +16,13 @@
 #include "content/browser/appcache/appcache_request.h"
 #include "content/browser/appcache/appcache_subresource_url_factory.h"
 #include "content/browser/appcache/appcache_url_loader_job.h"
+#include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/navigation_subresource_loader_params.h"
+#include "content/public/common/content_client.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_job.h"
+#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
 
@@ -407,14 +410,40 @@
 
 // NetworkService loading:
 void AppCacheRequestHandler::RunLoaderCallbackForMainResource(
+    int frame_tree_node_id,
+    BrowserContext* browser_context,
     LoaderCallback callback,
     SingleRequestURLLoaderFactory::RequestHandler handler) {
-  // For now let |this| always also return the subresource loader
-  // if (and only if) this returns a non-null |start_loader_callback|
-  // for handling the main resource.
-  if (handler)
+  scoped_refptr<network::SharedURLLoaderFactory> single_request_factory;
+
+  // For now let |this| always also return the subresource loader if (and only
+  // if) this returns a non-null |handler| for handling the main resource.
+  if (handler) {
     should_create_subresource_loader_ = true;
-  std::move(callback).Run(std::move(handler));
+
+    single_request_factory =
+        base::MakeRefCounted<SingleRequestURLLoaderFactory>(std::move(handler));
+    FrameTreeNode* frame_tree_node =
+        FrameTreeNode::GloballyFindByID(frame_tree_node_id);
+    if (frame_tree_node) {
+      mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_factory;
+      auto factory_receiver = pending_factory.InitWithNewPipeAndPassReceiver();
+      bool use_proxy =
+          GetContentClient()->browser()->WillCreateURLLoaderFactory(
+              browser_context, frame_tree_node->current_frame_host(),
+              frame_tree_node->current_frame_host()->GetProcess()->GetID(),
+              ContentBrowserClient::URLLoaderFactoryType::kNavigation,
+              url::Origin(), &factory_receiver, nullptr /* header_client */,
+              nullptr /* bypass_redirect_checks */);
+      if (use_proxy) {
+        single_request_factory->Clone(std::move(factory_receiver));
+        single_request_factory =
+            base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>(
+                std::move(pending_factory));
+      }
+    }
+  }
+  std::move(callback).Run(std::move(single_request_factory));
 }
 
 // Sub-resource handling ----------------------------------------------
@@ -518,16 +547,25 @@
     BrowserContext* browser_context,
     LoaderCallback callback,
     FallbackCallback fallback_callback) {
-  loader_callback_ =
+  MaybeCreateLoaderInternal(
+      tentative_resource_request,
       base::BindOnce(&AppCacheRequestHandler::RunLoaderCallbackForMainResource,
-                     weak_factory_.GetWeakPtr(), std::move(callback));
+                     weak_factory_.GetWeakPtr(),
+                     tentative_resource_request.render_frame_id,
+                     browser_context, std::move(callback)));
+}
+
+void AppCacheRequestHandler::MaybeCreateLoaderInternal(
+    const network::ResourceRequest& resource_request,
+    AppCacheLoaderCallback callback) {
+  loader_callback_ = std::move(callback);
 
   // TODO(crbug.com/876531): Figure out how AppCache interception should
   // interact with URLLoaderThrottles. It might be incorrect to store
   // |tentative_resource_request| here, since throttles can rewrite headers
   // between now and when the request handler passed to |loader_callback_| is
   // invoked.
-  request_->set_request(tentative_resource_request);
+  request_->set_request(resource_request);
 
   MaybeLoadResource(nullptr);
   // If a job is created, the job assumes ownership of the callback and
@@ -602,21 +640,17 @@
 
 void AppCacheRequestHandler::MaybeCreateSubresourceLoader(
     const network::ResourceRequest& resource_request,
-    LoaderCallback loader_callback) {
+    AppCacheLoaderCallback loader_callback) {
   DCHECK(!job_);
   DCHECK(!is_main_resource());
-  // AppCache doesn't use the fallback_callback.
-  FallbackCallback fallback_callback = base::DoNothing();
-
   // Subresource loads start out just like a main resource loads, but they go
   // down different branches along the way to completion.
-  MaybeCreateLoader(resource_request, nullptr, std::move(loader_callback),
-                    std::move(fallback_callback));
+  MaybeCreateLoaderInternal(resource_request, std::move(loader_callback));
 }
 
 void AppCacheRequestHandler::MaybeFallbackForSubresourceResponse(
     const network::ResourceResponseHead& response,
-    LoaderCallback loader_callback) {
+    AppCacheLoaderCallback loader_callback) {
   DCHECK(!job_);
   DCHECK(!is_main_resource());
   loader_callback_ = std::move(loader_callback);
@@ -628,7 +662,7 @@
 
 void AppCacheRequestHandler::MaybeFallbackForSubresourceRedirect(
     const net::RedirectInfo& redirect_info,
-    LoaderCallback loader_callback) {
+    AppCacheLoaderCallback loader_callback) {
   DCHECK(!job_);
   DCHECK(!is_main_resource());
   loader_callback_ = std::move(loader_callback);
@@ -639,7 +673,7 @@
 
 void AppCacheRequestHandler::MaybeFollowSubresourceRedirect(
     const net::RedirectInfo& redirect_info,
-    LoaderCallback loader_callback) {
+    AppCacheLoaderCallback loader_callback) {
   DCHECK(!job_);
   DCHECK(!is_main_resource());
   loader_callback_ = std::move(loader_callback);
diff --git a/content/browser/appcache/appcache_request_handler.h b/content/browser/appcache/appcache_request_handler.h
index 964b796..036a33e 100644
--- a/content/browser/appcache/appcache_request_handler.h
+++ b/content/browser/appcache/appcache_request_handler.h
@@ -18,6 +18,7 @@
 #include "content/browser/appcache/appcache_request_handler.h"
 #include "content/browser/appcache/appcache_service_impl.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/common/content_export.h"
 #include "content/public/common/resource_type.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -50,6 +51,9 @@
       public AppCacheStorage::Delegate,
       public NavigationLoaderInterceptor {
  public:
+  using AppCacheLoaderCallback =
+      base::OnceCallback<void(SingleRequestURLLoaderFactory::RequestHandler)>;
+
   ~AppCacheRequestHandler() override;
 
   // NetworkService loading
@@ -84,15 +88,15 @@
   // methods is called and the LoaderCallback is invoked.
   void MaybeCreateSubresourceLoader(
       const network::ResourceRequest& resource_request,
-      LoaderCallback callback);
+      AppCacheLoaderCallback callback);
   void MaybeFallbackForSubresourceResponse(
       const network::ResourceResponseHead& response,
-      LoaderCallback callback);
+      AppCacheLoaderCallback callback);
   void MaybeFallbackForSubresourceRedirect(
       const net::RedirectInfo& redirect_info,
-      LoaderCallback callback);
+      AppCacheLoaderCallback callback);
   void MaybeFollowSubresourceRedirect(const net::RedirectInfo& redirect_info,
-                                      LoaderCallback callback);
+                                      AppCacheLoaderCallback callback);
 
   static std::unique_ptr<AppCacheRequestHandler>
   InitializeForMainResourceNetworkService(
@@ -115,6 +119,10 @@
                          bool should_reset_appcache,
                          std::unique_ptr<AppCacheRequest> request);
 
+  void MaybeCreateLoaderInternal(
+      const network::ResourceRequest& resource_request,
+      AppCacheLoaderCallback callback);
+
   // AppCacheHost::Observer.
   void OnDestructionImminent(AppCacheHost* host) override;
 
@@ -174,6 +182,8 @@
   // runs for the main resource. This flips |should_create_subresource_loader_|
   // if a non-null |handler| is given. Always invokes |callback| with |handler|.
   void RunLoaderCallbackForMainResource(
+      int frame_tree_node_id,
+      BrowserContext* browser_context,
       LoaderCallback callback,
       SingleRequestURLLoaderFactory::RequestHandler handler);
 
@@ -243,7 +253,7 @@
 
   // Network service related members.
 
-  LoaderCallback loader_callback_;
+  AppCacheLoaderCallback loader_callback_;
 
   // Flipped to true if AppCache wants to handle subresource requests
   // (i.e. when |loader_callback_| is fired with a non-null
diff --git a/content/browser/appcache/appcache_url_loader_job.cc b/content/browser/appcache/appcache_url_loader_job.cc
index 165c85b..df870138 100644
--- a/content/browser/appcache/appcache_url_loader_job.cc
+++ b/content/browser/appcache/appcache_url_loader_job.cc
@@ -53,7 +53,7 @@
   is_fallback_ = is_fallback;
 
   if (is_fallback_ && loader_callback_)
-    CallLoaderCallback();
+    CallLoaderCallback({});
 
   InitializeRangeRequestInfo(appcache_request_->GetHeaders());
   storage_->LoadResponseInfo(manifest_url_, entry_.response_id(), this);
@@ -81,20 +81,12 @@
   if (AppCacheRequestHandler::IsRunningInTests())
     return;
 
-  if (loader_callback_)
-    CallLoaderCallback();
-
-  if (!client_) {
-    // Although all callsites that lead to construction of AppCacheURLLoaderJob
-    // provide a NavigationLoaderInterceptor::LoaderCallback, some use weak
-    // pointers to bind it. So it's possible that in between the time that
-    // AppCacheURLLoaderJob grabs the response info from storage that the
-    // callback is now empty, which leads to client_ not being initialized.
-    DeleteSoon();
-    return;
+  if (loader_callback_) {
+    CallLoaderCallback(base::BindOnce(&AppCacheURLLoaderJob::NotifyCompleted,
+                                      GetDerivedWeakPtr(), net::ERR_FAILED));
+  } else {
+    NotifyCompleted(net::ERR_FAILED);
   }
-
-  NotifyCompleted(net::ERR_FAILED);
 }
 
 AppCacheURLLoaderJob* AppCacheURLLoaderJob::AsURLLoaderJob() {
@@ -128,6 +120,7 @@
 }
 
 void AppCacheURLLoaderJob::Start(
+    base::OnceClosure continuation,
     const network::ResourceRequest& /* resource_request */,
     network::mojom::URLLoaderRequest request,
     network::mojom::URLLoaderClientPtr client) {
@@ -139,12 +132,14 @@
   client_ = std::move(client);
   binding_.set_connection_error_handler(
       base::BindOnce(&AppCacheURLLoaderJob::DeleteSoon, GetDerivedWeakPtr()));
+  if (continuation)
+    std::move(continuation).Run();
 }
 
 AppCacheURLLoaderJob::AppCacheURLLoaderJob(
     AppCacheRequest* appcache_request,
     AppCacheStorage* storage,
-    NavigationLoaderInterceptor::LoaderCallback loader_callback)
+    AppCacheRequestHandler::AppCacheLoaderCallback loader_callback)
     : storage_(storage->GetWeakPtr()),
       start_time_tick_(base::TimeTicks::Now()),
       cache_id_(blink::mojom::kAppCacheNoCacheId),
@@ -158,12 +153,12 @@
       is_main_resource_load_(IsResourceTypeFrame(
           static_cast<ResourceType>(appcache_request->GetResourceType()))) {}
 
-void AppCacheURLLoaderJob::CallLoaderCallback() {
+void AppCacheURLLoaderJob::CallLoaderCallback(base::OnceClosure continuation) {
   DCHECK(loader_callback_);
   DCHECK(!binding_.is_bound());
   std::move(loader_callback_)
-      .Run(base::BindOnce(&AppCacheURLLoaderJob::Start, GetDerivedWeakPtr()));
-  DCHECK(binding_.is_bound());
+      .Run(base::BindOnce(&AppCacheURLLoaderJob::Start, GetDerivedWeakPtr(),
+                          std::move(continuation)));
 }
 
 void AppCacheURLLoaderJob::OnResponseInfoLoaded(
@@ -177,36 +172,13 @@
   }
 
   if (response_info) {
-    if (loader_callback_)
-      CallLoaderCallback();
-
-    if (!client_) {
-      // See comment in DeliverErrorResponse.
-      DeleteSoon();
-      return;
+    if (loader_callback_) {
+      CallLoaderCallback(base::BindOnce(
+          &AppCacheURLLoaderJob::ContinueOnResponseInfoLoaded,
+          GetDerivedWeakPtr(), base::WrapRefCounted(response_info)));
+    } else {
+      ContinueOnResponseInfoLoaded(response_info);
     }
-
-    info_ = response_info;
-    reader_ =
-        storage_->CreateResponseReader(manifest_url_, entry_.response_id());
-
-    if (is_range_request())
-      SetupRangeResponse();
-
-    response_body_stream_ = std::move(data_pipe_.producer_handle);
-
-    // TODO(ananta)
-    // Move the asynchronous reading and mojo pipe handling code to a helper
-    // class. That would also need a change to BlobURLLoader.
-
-    // Wait for the data pipe to be ready to accept data.
-    writable_handle_watcher_.Watch(
-        response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
-        base::BindRepeating(&AppCacheURLLoaderJob::OnResponseBodyStreamReady,
-                            GetDerivedWeakPtr()));
-
-    SendResponseInfo();
-    ReadMore();
     return;
   }
 
@@ -228,6 +200,30 @@
     DeliverErrorResponse();
 }
 
+void AppCacheURLLoaderJob::ContinueOnResponseInfoLoaded(
+    scoped_refptr<AppCacheResponseInfo> response_info) {
+  info_ = response_info;
+  reader_ = storage_->CreateResponseReader(manifest_url_, entry_.response_id());
+
+  if (is_range_request())
+    SetupRangeResponse();
+
+  response_body_stream_ = std::move(data_pipe_.producer_handle);
+
+  // TODO(ananta)
+  // Move the asynchronous reading and mojo pipe handling code to a helper
+  // class. That would also need a change to BlobURLLoader.
+
+  // Wait for the data pipe to be ready to accept data.
+  writable_handle_watcher_.Watch(
+      response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+      base::BindRepeating(&AppCacheURLLoaderJob::OnResponseBodyStreamReady,
+                          GetDerivedWeakPtr()));
+
+  SendResponseInfo();
+  ReadMore();
+}
+
 void AppCacheURLLoaderJob::OnReadComplete(int result) {
   if (result > 0) {
     uint32_t bytes_written = static_cast<uint32_t>(result);
diff --git a/content/browser/appcache/appcache_url_loader_job.h b/content/browser/appcache/appcache_url_loader_job.h
index 43e25739e..48ba7f0 100644
--- a/content/browser/appcache/appcache_url_loader_job.h
+++ b/content/browser/appcache/appcache_url_loader_job.h
@@ -42,12 +42,13 @@
   AppCacheURLLoaderJob(
       AppCacheRequest* appcache_request,
       AppCacheStorage* storage,
-      NavigationLoaderInterceptor::LoaderCallback loader_callback);
+      AppCacheRequestHandler::AppCacheLoaderCallback loader_callback);
 
   ~AppCacheURLLoaderJob() override;
 
   // Sets up the bindings.
-  void Start(const network::ResourceRequest& resource_request,
+  void Start(base::OnceClosure continuation,
+             const network::ResourceRequest& resource_request,
              network::mojom::URLLoaderRequest request,
              network::mojom::URLLoaderClientPtr client);
 
@@ -76,11 +77,13 @@
 
  protected:
   // Invokes the loader callback which is expected to setup the mojo binding.
-  void CallLoaderCallback();
+  void CallLoaderCallback(base::OnceClosure continuation);
 
   // AppCacheStorage::Delegate methods
   void OnResponseInfoLoaded(AppCacheResponseInfo* response_info,
                             int64_t response_id) override;
+  void ContinueOnResponseInfoLoaded(
+      scoped_refptr<AppCacheResponseInfo> response_info);
 
   // AppCacheResponseReader completion callback.
   void OnReadComplete(int result);
@@ -125,7 +128,7 @@
 
   // The Callback to be invoked in the network service land to indicate if
   // the resource request can be serviced via the AppCache.
-  NavigationLoaderInterceptor::LoaderCallback loader_callback_;
+  AppCacheRequestHandler::AppCacheLoaderCallback loader_callback_;
 
   // The AppCacheRequest instance, used to inform the loader job about range
   // request headers. Not owned by this class.
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index 3f64880..32f3e98 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -336,6 +336,7 @@
       switches::kLogBestEffortTasks,
       switches::kLogFile,
       switches::kLoggingLevel,
+      switches::kPerfettoDisableInterning,
       switches::kTraceToConsole,
       switches::kV,
       switches::kVModule,
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 6e1ebda..a37639e 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -37,7 +37,7 @@
 #include "content/public/common/url_constants.h"
 #include "net/base/filename_util.h"
 #include "net/base/url_util.h"
-#include "net/url_request/url_request.h"
+#include "net/net_buildflags.h"
 #include "services/network/public/cpp/resource_request_body.h"
 #include "storage/browser/file_system/file_permission_policy.h"
 #include "storage/browser/file_system/file_system_context.h"
@@ -525,6 +525,10 @@
   // We know about these schemes and believe them to be safe.
   RegisterWebSafeScheme(url::kHttpScheme);
   RegisterWebSafeScheme(url::kHttpsScheme);
+#if BUILDFLAG(ENABLE_WEBSOCKETS)
+  RegisterWebSafeScheme(url::kWsScheme);
+  RegisterWebSafeScheme(url::kWssScheme);
+#endif  // BUILDFLAG(ENABLE_WEBSOCKETS)
   RegisterWebSafeScheme(url::kFtpScheme);
   RegisterWebSafeScheme(url::kDataScheme);
   RegisterWebSafeScheme("feed");
@@ -930,8 +934,7 @@
   }
 
   // Also allow URLs destined for ShellExecute and not the browser itself.
-  return !GetContentClient()->browser()->IsHandledURL(url) &&
-         !net::URLRequest::IsHandledURL(url);
+  return !GetContentClient()->browser()->IsHandledURL(url);
 }
 
 bool ChildProcessSecurityPolicyImpl::CanRedirectToURL(const GURL& url) {
diff --git a/content/browser/cookie_store/cookie_store_manager_unittest.cc b/content/browser/cookie_store/cookie_store_manager_unittest.cc
index 3c2e0e4a..6580574eb 100644
--- a/content/browser/cookie_store/cookie_store_manager_unittest.cc
+++ b/content/browser/cookie_store/cookie_store_manager_unittest.cc
@@ -1139,7 +1139,7 @@
   ASSERT_TRUE(SetCanonicalCookie(net::CanonicalCookie(
       "cookie-name-1", "cookie-value-1", "example.com", "/", base::Time(),
       base::Time(), base::Time(),
-      /* secure = */ false,
+      /* secure = */ true,
       /* httponly = */ true, net::CookieSameSite::NO_RESTRICTION,
       net::COOKIE_PRIORITY_DEFAULT)));
   task_environment_.RunUntilIdle();
@@ -1149,7 +1149,7 @@
   ASSERT_TRUE(SetCanonicalCookie(net::CanonicalCookie(
       "cookie-name-2", "cookie-value-2", "example.com", "/", base::Time(),
       base::Time(), base::Time(),
-      /* secure = */ false,
+      /* secure = */ true,
       /* httponly = */ false, net::CookieSameSite::NO_RESTRICTION,
       net::COOKIE_PRIORITY_DEFAULT)));
   task_environment_.RunUntilIdle();
diff --git a/content/browser/data_url_loader_factory.cc b/content/browser/data_url_loader_factory.cc
index 79f80147..0740e40 100644
--- a/content/browser/data_url_loader_factory.cc
+++ b/content/browser/data_url_loader_factory.cc
@@ -4,9 +4,12 @@
 
 #include "content/browser/data_url_loader_factory.h"
 
+#include "base/memory/ref_counted.h"
 #include "mojo/public/cpp/system/data_pipe_producer.h"
 #include "mojo/public/cpp/system/string_data_source.h"
-#include "net/url_request/url_request_data_job.h"
+#include "net/base/data_url.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 
@@ -47,11 +50,6 @@
     const network::ResourceRequest& request,
     network::mojom::URLLoaderClientPtr client,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
-  std::string mime_type;
-  std::string charset;
-  std::string data;
-  auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(std::string());
-
   const GURL* url = nullptr;
   if (!url_.is_empty() && request.url.is_empty()) {
     url = &url_;
@@ -59,8 +57,12 @@
     url = &request.url;
   }
 
-  int result = net::URLRequestDataJob::BuildResponse(
-      *url, request.method, &mime_type, &charset, &data, headers.get());
+  std::string data;
+  scoped_refptr<net::HttpResponseHeaders> headers;
+  network::ResourceResponseHead response;
+  net::Error result =
+      net::DataURL::BuildResponse(*url, request.method, &response.mime_type,
+                                  &response.charset, &data, &response.headers);
   url_ = GURL();  // Don't need it anymore.
 
   if (result != net::OK) {
@@ -68,10 +70,6 @@
     return;
   }
 
-  network::ResourceResponseHead response;
-  response.mime_type = mime_type;
-  response.charset = charset;
-  response.headers = headers;
   client->OnReceiveResponse(response);
 
   mojo::ScopedDataPipeProducerHandle producer;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 2b0b8db1..114ad7c 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -5329,18 +5329,28 @@
     if (subresource_loader_params &&
         subresource_loader_params->pending_appcache_loader_factory.is_valid()) {
       // If the caller has supplied a factory for AppCache, use it.
-      auto pending_factory =
-          std::move(subresource_loader_params->pending_appcache_loader_factory);
+      mojo::Remote<network::mojom::URLLoaderFactory> appcache_remote(std::move(
+          subresource_loader_params->pending_appcache_loader_factory));
 
-#if defined(OS_ANDROID)
-      GetContentClient()
-          ->browser()
-          ->WillCreateURLLoaderFactoryForAppCacheSubresource(
-              GetProcess()->GetID(), &pending_factory);
-#endif
+      mojo::PendingRemote<network::mojom::URLLoaderFactory>
+          appcache_proxied_remote;
+      auto appcache_proxied_receiver =
+          appcache_proxied_remote.InitWithNewPipeAndPassReceiver();
+      bool use_proxy =
+          GetContentClient()->browser()->WillCreateURLLoaderFactory(
+              browser_context, this, GetProcess()->GetID(),
+              ContentBrowserClient::URLLoaderFactoryType::kDocumentSubResource,
+              GetOriginForURLLoaderFactory(navigation_request),
+              &appcache_proxied_receiver, nullptr /* header_client */,
+              nullptr /* bypass_redirect_checks */);
+      if (use_proxy) {
+        appcache_remote->Clone(std::move(appcache_proxied_receiver));
+        appcache_remote.reset();
+        appcache_remote.Bind(std::move(appcache_proxied_remote));
+      }
 
       subresource_loader_factories->pending_appcache_factory() =
-          std::move(pending_factory);
+          appcache_remote.Unbind();
 
       // Inject test intermediary if needed.
       if (!GetCreateNetworkFactoryCallbackForRenderFrame().is_null()) {
diff --git a/content/browser/indexed_db/docs/open_and_verify_leveldb_database.code2flow b/content/browser/indexed_db/docs/open_and_verify_leveldb_database.code2flow
index 6f3804ac0..14b4cfe 100644
--- a/content/browser/indexed_db/docs/open_and_verify_leveldb_database.code2flow
+++ b/content/browser/indexed_db/docs/open_and_verify_leveldb_database.code2flow
@@ -37,7 +37,6 @@
 }
 
 block Open LevelDB {
-  |Read free disk space|;
   switch (leveldb_env::OpenDB) {
     case Status Not OK:{
       Histogram(LevelDBOpenErrors);
@@ -46,12 +45,11 @@
       }
       goto open-loop-restart;
     }
-    case Status OK: {
-      Histogram(LevelDB.OpenTime);
-    }
+    case Status OK:
   }
 }
 
+Histogram(LevelDB.OpenTime);
 Create LevelDB Scopes;
 switch (Initialize LevelDB Scopes) {
   case Failure:
@@ -61,8 +59,8 @@
   case Success:
 }
 
-block Open LevelDBDatabase {
-  Create LevelDBDatabase;
+block Open TransactionalLevelDBDatabase {
+  Create TransactionalLevelDBDatabase;
   switch (Try to read schema) {
     case Corruption -
          Failure reading schema:
diff --git a/content/browser/indexed_db/docs/open_and_verify_leveldb_database.pdf b/content/browser/indexed_db/docs/open_and_verify_leveldb_database.pdf
index 64a23ca..fd4acbd 100644
--- a/content/browser/indexed_db/docs/open_and_verify_leveldb_database.pdf
+++ b/content/browser/indexed_db/docs/open_and_verify_leveldb_database.pdf
Binary files differ
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index 9d109954..4fa0b92 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -39,6 +39,7 @@
 #include "content/browser/indexed_db/leveldb/leveldb_env.h"
 #include "content/browser/indexed_db/leveldb/leveldb_write_batch.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h"
 #include "content/browser/indexed_db/scopes/leveldb_scope.h"
@@ -517,14 +518,14 @@
 IndexedDBBackingStore::IndexedDBBackingStore(
     Mode backing_store_mode,
     IndexedDBFactory* indexed_db_factory,
-    indexed_db::LevelDBFactory* leveldb_factory,
+    TransactionalLevelDBFactory* transactional_leveldb_factory,
     const Origin& origin,
     const FilePath& blob_path,
     std::unique_ptr<TransactionalLevelDBDatabase> db,
     base::SequencedTaskRunner* task_runner)
     : backing_store_mode_(backing_store_mode),
       indexed_db_factory_(indexed_db_factory),
-      leveldb_factory_(leveldb_factory),
+      transactional_leveldb_factory_(transactional_leveldb_factory),
       origin_(origin),
       blob_path_(blob_path),
       origin_identifier_(ComputeOriginIdentifier(origin)),
@@ -1643,7 +1644,7 @@
   bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
   DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
   std::unique_ptr<LevelDBDirectTransaction> transaction =
-      leveldb_factory_->CreateLevelDBDirectTransaction(db_.get());
+      transactional_leveldb_factory_->CreateLevelDBDirectTransaction(db_.get());
 
   BlobJournalType live_blob_journal, primary_journal;
   if (!GetLiveBlobJournal(transaction.get(), &live_blob_journal).ok())
@@ -1765,7 +1766,7 @@
   IDB_TRACE("IndexedDBBackingStore::CleanUpBlobJournal");
   DCHECK(!committing_transaction_count_);
   std::unique_ptr<LevelDBDirectTransaction> journal_transaction =
-      leveldb_factory_->CreateLevelDBDirectTransaction(db_.get());
+      transactional_leveldb_factory_->CreateLevelDBDirectTransaction(db_.get());
   BlobJournalType journal;
 
   Status s = GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
@@ -2862,8 +2863,9 @@
     IndexedDBBackingStore* backing_store,
     blink::mojom::IDBTransactionDurability durability)
     : backing_store_(backing_store),
-      leveldb_factory_(backing_store ? backing_store->leveldb_factory_
-                                     : nullptr),
+      transactional_leveldb_factory_(
+          backing_store ? backing_store->transactional_leveldb_factory_
+                        : nullptr),
       database_id_(-1),
       committing_(false),
       durability_(durability) {}
@@ -2875,7 +2877,7 @@
 void IndexedDBBackingStore::Transaction::Begin(std::vector<ScopeLock> locks) {
   IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
   DCHECK(!transaction_.get());
-  transaction_ = leveldb_factory_->CreateLevelDBTransaction(
+  transaction_ = transactional_leveldb_factory_->CreateLevelDBTransaction(
       backing_store_->db_.get(),
       backing_store_->db_->scopes()->CreateScope(
           std::move(locks), std::vector<LevelDBScopes::EmptyRange>()));
@@ -3051,7 +3053,7 @@
     // Read the persisted states of the primary/live blob journals,
     // so that they can be updated correctly by the transaction.
     std::unique_ptr<LevelDBDirectTransaction> journal_transaction =
-        leveldb_factory_->CreateLevelDBDirectTransaction(
+        transactional_leveldb_factory_->CreateLevelDBDirectTransaction(
             backing_store_->db_.get());
     s = GetPrimaryBlobJournal(journal_transaction.get(), &primary_journal);
     if (!s.ok())
@@ -3127,7 +3129,7 @@
   }
 
   std::unique_ptr<LevelDBDirectTransaction> update_journal_transaction =
-      leveldb_factory_->CreateLevelDBDirectTransaction(
+      transactional_leveldb_factory_->CreateLevelDBDirectTransaction(
           backing_store_->db_.get());
   UpdatePrimaryBlobJournal(update_journal_transaction.get(),
                            saved_primary_journal);
diff --git a/content/browser/indexed_db/indexed_db_backing_store.h b/content/browser/indexed_db/indexed_db_backing_store.h
index 77319ef8..6aabc1af 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.h
+++ b/content/browser/indexed_db/indexed_db_backing_store.h
@@ -54,13 +54,10 @@
 class IndexedDBFactory;
 struct IndexedDBValue;
 class TransactionalLevelDBDatabase;
+class TransactionalLevelDBFactory;
 class TransactionalLevelDBIterator;
 class TransactionalLevelDBTransaction;
 
-namespace indexed_db {
-class LevelDBFactory;
-}
-
 namespace indexed_db_backing_store_unittest {
 class IndexedDBBackingStoreTest;
 FORWARD_DECLARE_TEST(IndexedDBBackingStoreTest, ReadCorruptionInfo);
@@ -277,7 +274,7 @@
     // transactions & connections before destroying the backing store.
     // TODO(dmurph): Convert to WeakPtr. https://crbug.com/960992
     IndexedDBBackingStore* backing_store_;
-    indexed_db::LevelDBFactory* const leveldb_factory_;
+    TransactionalLevelDBFactory* const transactional_leveldb_factory_;
     scoped_refptr<TransactionalLevelDBTransaction> transaction_;
     std::map<std::string, std::unique_ptr<BlobChangeRecord>> blob_change_map_;
     std::map<std::string, std::unique_ptr<BlobChangeRecord>>
@@ -410,13 +407,14 @@
   static constexpr const base::TimeDelta kInitialJournalCleaningWindowTime =
       base::TimeDelta::FromSeconds(2);
 
-  IndexedDBBackingStore(Mode backing_store_mode,
-                        IndexedDBFactory* indexed_db_factory,
-                        indexed_db::LevelDBFactory* leveldb_factory,
-                        const url::Origin& origin,
-                        const base::FilePath& blob_path,
-                        std::unique_ptr<TransactionalLevelDBDatabase> db,
-                        base::SequencedTaskRunner* task_runner);
+  IndexedDBBackingStore(
+      Mode backing_store_mode,
+      IndexedDBFactory* indexed_db_factory,
+      TransactionalLevelDBFactory* transactional_leveldb_factory,
+      const url::Origin& origin,
+      const base::FilePath& blob_path,
+      std::unique_ptr<TransactionalLevelDBDatabase> db,
+      base::SequencedTaskRunner* task_runner);
   virtual ~IndexedDBBackingStore();
 
   // Initializes the backing store. This must be called before doing any
@@ -653,7 +651,7 @@
 
   Mode backing_store_mode_;
   IndexedDBFactory* indexed_db_factory_;
-  indexed_db::LevelDBFactory* const leveldb_factory_;
+  TransactionalLevelDBFactory* const transactional_leveldb_factory_;
   const url::Origin origin_;
   base::FilePath blob_path_;
 
diff --git a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
index c4c3fd5..c49d2639 100644
--- a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
@@ -69,7 +69,7 @@
   TestableIndexedDBBackingStore(
       IndexedDBBackingStore::Mode backing_store_mode,
       IndexedDBFactory* indexed_db_factory,
-      indexed_db::LevelDBFactory* leveldb_factory,
+      TransactionalLevelDBFactory* leveldb_factory,
       const url::Origin& origin,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
@@ -142,7 +142,6 @@
  public:
   explicit TestIDBFactory(IndexedDBContextImpl* idb_context)
       : IndexedDBFactoryImpl(idb_context,
-                             indexed_db::LevelDBFactory::Get(),
                              IndexedDBClassFactory::Get(),
                              base::DefaultClock::GetInstance()) {}
   ~TestIDBFactory() override = default;
@@ -150,7 +149,7 @@
  protected:
   std::unique_ptr<IndexedDBBackingStore> CreateBackingStore(
       IndexedDBBackingStore::Mode backing_store_mode,
-      indexed_db::LevelDBFactory* leveldb_factory,
+      TransactionalLevelDBFactory* leveldb_factory,
       const url::Origin& origin,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
diff --git a/content/browser/indexed_db/indexed_db_browsertest.cc b/content/browser/indexed_db/indexed_db_browsertest.cc
index ff0d136..a32930e 100644
--- a/content/browser/indexed_db/indexed_db_browsertest.cc
+++ b/content/browser/indexed_db/indexed_db_browsertest.cc
@@ -83,7 +83,6 @@
   void SetUp() override {
     GetTestClassFactory()->Reset();
     IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetIDBClassFactory);
-    indexed_db::LevelDBFactory::SetFactoryGetterForTesting(GetLevelDBFactory);
     ContentBrowserTest::SetUp();
   }
 
@@ -255,10 +254,6 @@
     return GetTestClassFactory();
   }
 
-  static indexed_db::LevelDBFactory* GetLevelDBFactory() {
-    return GetTestClassFactory();
-  }
-
  private:
   void WriteToIndexedDBOnIDBSequence(
       scoped_refptr<IndexedDBContextImpl> context,
diff --git a/content/browser/indexed_db/indexed_db_class_factory.cc b/content/browser/indexed_db/indexed_db_class_factory.cc
index 54b47566..9efbaa27 100644
--- a/content/browser/indexed_db/indexed_db_class_factory.cc
+++ b/content/browser/indexed_db/indexed_db_class_factory.cc
@@ -7,14 +7,32 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
 #include "content/browser/indexed_db/indexed_db_factory.h"
+#include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
 #include "content/browser/indexed_db/indexed_db_metadata_coding.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
+#include "content/browser/indexed_db/leveldb/leveldb_env.h"
+#include "content/browser/indexed_db/leveldb/leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/scopes/leveldb_scope.h"
+#include "third_party/leveldatabase/leveldb_chrome.h"
+#include "third_party/leveldatabase/src/include/leveldb/filter_policy.h"
 
 namespace content {
-
+namespace {
+DefaultLevelDBFactory* GetDefaultLevelDBFactory() {
+  static base::NoDestructor<DefaultLevelDBFactory> leveldb_factory(
+      IndexedDBClassFactory::GetLevelDBOptions(), "indexed-db");
+  return leveldb_factory.get();
+}
+DefaultTransactionalLevelDBFactory* GetDefaultTransactionalLevelDBFactory() {
+  static base::NoDestructor<DefaultTransactionalLevelDBFactory>
+      transactional_leveldb_factory;
+  return transactional_leveldb_factory.get();
+}
+}  // namespace
 static IndexedDBClassFactory::GetterCallback* s_factory_getter;
 static ::base::LazyInstance<IndexedDBClassFactory>::Leaky s_factory =
     LAZY_INSTANCE_INITIALIZER;
@@ -23,6 +41,7 @@
   s_factory_getter = cb;
 }
 
+// static
 IndexedDBClassFactory* IndexedDBClassFactory::Get() {
   if (s_factory_getter)
     return (*s_factory_getter)();
@@ -30,6 +49,41 @@
     return s_factory.Pointer();
 }
 
+// static
+leveldb_env::Options IndexedDBClassFactory::GetLevelDBOptions() {
+  static const leveldb::FilterPolicy* kIDBFilterPolicy =
+      leveldb::NewBloomFilterPolicy(10);
+  leveldb_env::Options options;
+  options.comparator = indexed_db::GetDefaultLevelDBComparator();
+  options.paranoid_checks = true;
+  options.filter_policy = kIDBFilterPolicy;
+  options.compression = leveldb::kSnappyCompression;
+  // For info about the troubles we've run into with this parameter, see:
+  // https://crbug.com/227313#c11
+  options.max_open_files = 80;
+  options.env = LevelDBEnv::Get();
+  options.block_cache = leveldb_chrome::GetSharedWebBlockCache();
+  return options;
+}
+
+IndexedDBClassFactory::IndexedDBClassFactory()
+    : IndexedDBClassFactory(GetDefaultLevelDBFactory(),
+                            GetDefaultTransactionalLevelDBFactory()) {}
+
+IndexedDBClassFactory::IndexedDBClassFactory(
+    LevelDBFactory* leveldb_factory,
+    TransactionalLevelDBFactory* transactional_leveldb_factory)
+    : leveldb_factory_(leveldb_factory),
+      transactional_leveldb_factory_(transactional_leveldb_factory) {}
+
+LevelDBFactory& IndexedDBClassFactory::leveldb_factory() {
+  return *leveldb_factory_;
+}
+TransactionalLevelDBFactory&
+IndexedDBClassFactory::transactional_leveldb_factory() {
+  return *transactional_leveldb_factory_;
+}
+
 std::pair<std::unique_ptr<IndexedDBDatabase>, leveldb::Status>
 IndexedDBClassFactory::CreateIndexedDBDatabase(
     const base::string16& name,
@@ -66,4 +120,12 @@
       std::move(tear_down_callback), backing_store_transaction));
 }
 
+void IndexedDBClassFactory::SetLevelDBFactoryForTesting(
+    LevelDBFactory* leveldb_factory) {
+  if (leveldb_factory)
+    leveldb_factory_ = leveldb_factory;
+  else
+    leveldb_factory_ = GetDefaultLevelDBFactory();
+}
+
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_class_factory.h b/content/browser/indexed_db/indexed_db_class_factory.h
index 5dba65c..3a097bae 100644
--- a/content/browser/indexed_db/indexed_db_class_factory.h
+++ b/content/browser/indexed_db/indexed_db_class_factory.h
@@ -23,6 +23,7 @@
 #include "content/common/content_export.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/status.h"
 
 namespace content {
@@ -30,6 +31,8 @@
 class IndexedDBConnection;
 class IndexedDBFactory;
 class IndexedDBTransaction;
+class LevelDBFactory;
+class TransactionalLevelDBFactory;
 
 // Use this factory to create some IndexedDB objects. Exists solely to
 // facilitate tests which sometimes need to inject mock objects into the system.
@@ -47,6 +50,12 @@
 
   static void SetIndexedDBClassFactoryGetter(GetterCallback* cb);
 
+  // Visible for testing.
+  static leveldb_env::Options GetLevelDBOptions();
+
+  virtual LevelDBFactory& leveldb_factory();
+  virtual TransactionalLevelDBFactory& transactional_leveldb_factory();
+
   // Returns a constructed database, or a leveldb::Status error if there was a
   // problem initializing the database. |run_tasks_callback| is called when the
   // database has tasks to run.
@@ -70,10 +79,18 @@
       IndexedDBTransaction::TearDownCallback tear_down_callback,
       IndexedDBBackingStore::Transaction* backing_store_transaction);
 
+  void SetLevelDBFactoryForTesting(LevelDBFactory* leveldb_factory);
+
  protected:
-  IndexedDBClassFactory() {}
-  virtual ~IndexedDBClassFactory() {}
+  IndexedDBClassFactory();
+  IndexedDBClassFactory(
+      LevelDBFactory* leveldb_factory,
+      TransactionalLevelDBFactory* transactional_leveldb_factory);
+  virtual ~IndexedDBClassFactory() = default;
   friend struct base::LazyInstanceTraitsBase<IndexedDBClassFactory>;
+
+  LevelDBFactory* leveldb_factory_;
+  TransactionalLevelDBFactory* transactional_leveldb_factory_;
 };
 
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc b/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
index faf19b71..0e3e70a 100644
--- a/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
@@ -19,6 +19,7 @@
 #include "content/browser/indexed_db/leveldb/fake_leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/leveldb_env.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/scopes/leveldb_scopes.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/leveldatabase/env_chromium.h"
@@ -39,14 +40,14 @@
   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
   const base::FilePath path = temp_directory.GetPath();
 
-  auto* leveldb_factory = indexed_db::LevelDBFactory::Get();
+  DefaultTransactionalLevelDBFactory transactional_leveldb_factory;
   auto task_runner = base::SequencedTaskRunnerHandle::Get();
   std::unique_ptr<IndexedDBBackingStore> backing_store = std::make_unique<
       IndexedDBBackingStore>(
-      IndexedDBBackingStore::Mode::kInMemory, nullptr, leveldb_factory, origin,
-      path,
-      leveldb_factory->CreateLevelDBDatabase(
-          indexed_db::FakeLevelDBFactory::GetBrokenLevelDB(
+      IndexedDBBackingStore::Mode::kInMemory, nullptr,
+      &transactional_leveldb_factory, origin, path,
+      transactional_leveldb_factory.CreateLevelDBDatabase(
+          FakeLevelDBFactory::GetBrokenLevelDB(
               leveldb::Status::IOError("It's broken!"), path),
           nullptr, task_runner.get(),
           TransactionalLevelDBDatabase::kDefaultMaxOpenIteratorsPerDatabase),
@@ -65,7 +66,7 @@
   auto task_runner = base::SequencedTaskRunnerHandle::Get();
   leveldb::Status s;
 
-  auto* leveldb_factory = indexed_db::LevelDBFactory::Get();
+  DefaultTransactionalLevelDBFactory transactional_leveldb_factory;
   std::array<leveldb::Status, 4> errors = {
       MakeIOError("some filename", "some message", leveldb_env::kNewLogger,
                   base::File::FILE_ERROR_NO_SPACE),
@@ -78,12 +79,11 @@
   for (leveldb::Status error_status : errors) {
     std::unique_ptr<IndexedDBBackingStore> backing_store = std::make_unique<
         IndexedDBBackingStore>(
-        IndexedDBBackingStore::Mode::kInMemory, nullptr, leveldb_factory,
-        origin, path,
-        leveldb_factory->CreateLevelDBDatabase(
-            indexed_db::FakeLevelDBFactory::GetBrokenLevelDB(error_status,
-                                                             path),
-            nullptr, task_runner.get(),
+        IndexedDBBackingStore::Mode::kInMemory, nullptr,
+        &transactional_leveldb_factory, origin, path,
+        transactional_leveldb_factory.CreateLevelDBDatabase(
+            FakeLevelDBFactory::GetBrokenLevelDB(error_status, path), nullptr,
+            task_runner.get(),
             TransactionalLevelDBDatabase::kDefaultMaxOpenIteratorsPerDatabase),
         task_runner.get());
     leveldb::Status s = backing_store->Initialize(false);
diff --git a/content/browser/indexed_db/indexed_db_connection_coordinator.cc b/content/browser/indexed_db/indexed_db_connection_coordinator.cc
index 3797ae27..5608ead4 100644
--- a/content/browser/indexed_db/indexed_db_connection_coordinator.cc
+++ b/content/browser/indexed_db/indexed_db_connection_coordinator.cc
@@ -18,8 +18,8 @@
 #include "content/browser/indexed_db/indexed_db_database_error.h"
 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
 #include "content/browser/indexed_db/indexed_db_origin_state.h"
-#include "content/browser/indexed_db/leveldb/leveldb_env.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h"
 #include "content/browser/indexed_db/scopes/leveldb_scope.h"
 #include "content/browser/indexed_db/scopes/leveldb_scopes.h"
@@ -451,7 +451,7 @@
       scoped_refptr<TransactionalLevelDBTransaction> txn;
       TransactionalLevelDBDatabase* db = db_->backing_store_->db();
       if (db) {
-        txn = db->leveldb_class_factory()->CreateLevelDBTransaction(
+        txn = db->class_factory()->CreateLevelDBTransaction(
             db, db->scopes()->CreateScope(std::move(lock_receiver_.locks), {}));
         txn->set_commit_cleanup_complete_callback(
             std::move(on_database_deleted_));
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 5b1ae30a..39bca017 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -32,7 +32,7 @@
 #include "content/browser/indexed_db/indexed_db_quota_client.h"
 #include "content/browser/indexed_db/indexed_db_tracing.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
-#include "content/browser/indexed_db/leveldb/leveldb_env.h"
+#include "content/browser/indexed_db/leveldb/leveldb_factory.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_usage_info.h"
 #include "content/public/common/content_switches.h"
@@ -115,10 +115,7 @@
     // detect when dbs are newly created.
     GetOriginSet();
     indexeddb_factory_ = std::make_unique<IndexedDBFactoryImpl>(
-        this,
-        leveldb_factory_for_testing_ ? leveldb_factory_for_testing_
-                                     : indexed_db::LevelDBFactory::Get(),
-        IndexedDBClassFactory::Get(), clock_);
+        this, IndexedDBClassFactory::Get(), clock_);
   }
   return indexeddb_factory_.get();
 }
@@ -329,7 +326,8 @@
   EnsureDiskUsageCacheInitialized(origin);
 
   leveldb::Status s =
-      indexed_db::DefaultLevelDBFactory().DestroyLevelDB(idb_directory);
+      IndexedDBClassFactory::Get()->leveldb_factory().DestroyLevelDB(
+          idb_directory);
   if (!s.ok()) {
     LOG(WARNING) << "Failed to delete LevelDB database: "
                  << idb_directory.AsUTF8Unsafe();
@@ -526,12 +524,6 @@
   }
 }
 
-void IndexedDBContextImpl::SetLevelDBFactoryForTesting(
-    indexed_db::LevelDBFactory* factory) {
-  DCHECK(factory);
-  leveldb_factory_for_testing_ = factory;
-}
-
 IndexedDBContextImpl::~IndexedDBContextImpl() {
   DCHECK(TaskRunner()->RunsTasksInCurrentSequence());
   if (indexeddb_factory_.get())
diff --git a/content/browser/indexed_db/indexed_db_context_impl.h b/content/browser/indexed_db/indexed_db_context_impl.h
index a2743e2..21bf078 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.h
+++ b/content/browser/indexed_db/indexed_db_context_impl.h
@@ -40,10 +40,6 @@
 class IndexedDBConnection;
 class IndexedDBFactoryImpl;
 
-namespace indexed_db {
-class LevelDBFactory;
-}
-
 class CONTENT_EXPORT IndexedDBContextImpl : public IndexedDBContext {
  public:
   // Recorded in histograms, so append only.
@@ -153,8 +149,6 @@
                                      const base::string16& database_name,
                                      const base::string16& object_store_name);
 
-  void SetLevelDBFactoryForTesting(indexed_db::LevelDBFactory* factory);
-
  protected:
   ~IndexedDBContextImpl() override;
 
@@ -197,7 +191,6 @@
   std::unique_ptr<std::set<url::Origin>> origin_set_;
   std::map<url::Origin, int64_t> origin_size_map_;
   base::ObserverList<Observer>::Unchecked observers_;
-  indexed_db::LevelDBFactory* leveldb_factory_for_testing_ = nullptr;
   base::Clock* clock_;
 
   DISALLOW_COPY_AND_ASSIGN(IndexedDBContextImpl);
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.cc b/content/browser/indexed_db/indexed_db_factory_impl.cc
index 1996d41..47c9d33 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.cc
+++ b/content/browser/indexed_db/indexed_db_factory_impl.cc
@@ -20,11 +20,13 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/system/sys_info.h"
 #include "base/timer/timer.h"
 #include "content/browser/indexed_db/indexed_db_class_factory.h"
 #include "content/browser/indexed_db/indexed_db_connection.h"
@@ -39,7 +41,9 @@
 #include "content/browser/indexed_db/indexed_db_task_helper.h"
 #include "content/browser/indexed_db/indexed_db_tombstone_sweeper.h"
 #include "content/browser/indexed_db/indexed_db_tracing.h"
+#include "content/browser/indexed_db/leveldb/leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/scopes/leveldb_scopes.h"
 #include "content/browser/indexed_db/scopes/leveldb_scopes_factory.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
@@ -154,15 +158,12 @@
 
 IndexedDBFactoryImpl::IndexedDBFactoryImpl(
     IndexedDBContextImpl* context,
-    indexed_db::LevelDBFactory* leveldb_factory,
     IndexedDBClassFactory* indexed_db_class_factory,
     base::Clock* clock)
     : context_(context),
-      leveldb_factory_(leveldb_factory),
-      indexed_db_class_factory_(indexed_db_class_factory),
+      class_factory_(indexed_db_class_factory),
       clock_(clock) {
   DCHECK(context);
-  DCHECK(leveldb_factory);
   DCHECK(indexed_db_class_factory);
   DCHECK(clock);
 }
@@ -282,7 +283,7 @@
     return;
   }
   std::unique_ptr<IndexedDBDatabase> database;
-  std::tie(database, s) = indexed_db_class_factory_->CreateIndexedDBDatabase(
+  std::tie(database, s) = class_factory_->CreateIndexedDBDatabase(
       name, factory->backing_store(), this,
       base::BindRepeating(&IndexedDBFactoryImpl::MaybeRunTasksForOrigin,
                           origin_state_destruction_weak_factory_.GetWeakPtr(),
@@ -372,7 +373,7 @@
   }
 
   std::unique_ptr<IndexedDBDatabase> database;
-  std::tie(database, s) = indexed_db_class_factory_->CreateIndexedDBDatabase(
+  std::tie(database, s) = class_factory_->CreateIndexedDBDatabase(
       name, factory->backing_store(), this,
       base::BindRepeating(&IndexedDBFactoryImpl::MaybeRunTasksForOrigin,
                           origin_state_destruction_weak_factory_.GetWeakPtr(),
@@ -464,7 +465,8 @@
   //       so our corruption info file will remain.
   const base::FilePath file_path =
       path_base.Append(indexed_db::GetLevelDBFileName(saved_origin));
-  leveldb::Status s = leveldb_factory_->DestroyLevelDB(file_path);
+  leveldb::Status s =
+      class_factory_->leveldb_factory().DestroyLevelDB(file_path);
   DLOG_IF(ERROR, !s.ok()) << "Unable to delete backing store: " << s.ToString();
   base::UmaHistogramEnumeration(
       "WebCore.IndexedDB.DestroyCorruptBackingStoreStatus",
@@ -759,9 +761,9 @@
   auto origin_state = std::make_unique<IndexedDBOriginState>(
       origin,
       /*persist_for_incognito=*/is_incognito_and_in_memory, clock_,
-      leveldb_factory_, &earliest_sweep_, std::move(lock_manager),
-      std::move(run_tasks_callback), std::move(tear_down_callback),
-      std::move(backing_store));
+      &class_factory_->transactional_leveldb_factory(), &earliest_sweep_,
+      std::move(lock_manager), std::move(run_tasks_callback),
+      std::move(tear_down_callback), std::move(backing_store));
 
   it = factories_per_origin_.emplace(origin, std::move(origin_state)).first;
 
@@ -772,14 +774,14 @@
 
 std::unique_ptr<IndexedDBBackingStore> IndexedDBFactoryImpl::CreateBackingStore(
     IndexedDBBackingStore::Mode backing_store_mode,
-    indexed_db::LevelDBFactory* leveldb_factory,
+    TransactionalLevelDBFactory* transactional_leveldb_factory,
     const url::Origin& origin,
     const base::FilePath& blob_path,
     std::unique_ptr<TransactionalLevelDBDatabase> db,
     base::SequencedTaskRunner* task_runner) {
   return std::make_unique<IndexedDBBackingStore>(
-      backing_store_mode, this, leveldb_factory, origin, blob_path,
-      std::move(db), task_runner);
+      backing_store_mode, this, transactional_leveldb_factory, origin,
+      blob_path, std::move(db), task_runner);
 }
 
 std::tuple<std::unique_ptr<IndexedDBBackingStore>,
@@ -826,7 +828,7 @@
           {"IndexedDB (database was corrupt): ", corruption_message});
       // This is a special case where we want to make sure the database is
       // deleted, so we try to delete again.
-      status = leveldb_factory_->DestroyLevelDB(database_path);
+      status = class_factory_->leveldb_factory().DestroyLevelDB(database_path);
       base::UmaHistogramEnumeration(
           "WebCore.IndexedDB.DestroyCorruptBackingStoreStatus",
           leveldb_env::GetLevelDBStatusUMAValue(status),
@@ -838,26 +840,44 @@
     }
   }
 
-  bool is_disk_full;
-  scoped_refptr<LevelDBState> state;
-
   // Open the leveldb database.
-  std::tie(state, status, is_disk_full) = leveldb_factory_->OpenLevelDBState(
-      database_path, indexed_db::GetDefaultLevelDBComparator(),
-      create_if_missing);
+  scoped_refptr<LevelDBState> database_state;
+  bool is_disk_full;
+  {
+    IDB_TRACE("IndexedDBFactoryImpl::OpenLevelDB");
+    base::TimeTicks begin_time = base::TimeTicks::Now();
+    size_t write_buffer_size = leveldb_env::WriteBufferSize(
+        base::SysInfo::AmountOfTotalDiskSpace(database_path));
+    std::tie(database_state, status, is_disk_full) =
+        class_factory_->leveldb_factory().OpenLevelDBState(
+            database_path, create_if_missing, write_buffer_size);
+    if (UNLIKELY(!status.ok())) {
+      if (!status.IsNotFound()) {
+        indexed_db::ReportLevelDBError("WebCore.IndexedDB.LevelDBOpenErrors",
+                                       status);
+      }
+      return {nullptr, status, IndexedDBDataLossInfo(), is_disk_full};
+    }
+    UMA_HISTOGRAM_MEDIUM_TIMES("WebCore.IndexedDB.LevelDB.OpenTime",
+                               base::TimeTicks::Now() - begin_time);
+  }
 
-  if (UNLIKELY(!status.ok()))
-    return {nullptr, status, IndexedDBDataLossInfo(), is_disk_full};
-
+  // Create the LevelDBScopes wrapper.
   std::unique_ptr<LevelDBScopes> scopes;
-  std::tie(scopes, status) = scopes_factory->CreateAndInitializeLevelDBScopes(
-      std::move(scopes_options), state);
-  if (UNLIKELY(!status.ok()))
-    return {nullptr, status, std::move(data_loss_info), /*is_disk_full=*/false};
+  {
+    IDB_TRACE("IndexedDBFactoryImpl::OpenLevelDBScopes");
+    DCHECK(scopes_factory);
+    std::tie(scopes, status) = scopes_factory->CreateAndInitializeLevelDBScopes(
+        std::move(scopes_options), database_state);
+    if (UNLIKELY(!status.ok()))
+      return {nullptr, status, std::move(data_loss_info),
+              /*is_disk_full=*/false};
+  }
 
+  // Create the TransactionalLevelDBDatabase wrapper.
   std::unique_ptr<TransactionalLevelDBDatabase> database =
-      leveldb_factory_->CreateLevelDBDatabase(
-          std::move(state), std::move(scopes), context_->TaskRunner(),
+      class_factory_->transactional_leveldb_factory().CreateLevelDBDatabase(
+          std::move(database_state), std::move(scopes), context_->TaskRunner(),
           TransactionalLevelDBDatabase::kDefaultMaxOpenIteratorsPerDatabase);
 
   bool are_schemas_known = false;
@@ -887,8 +907,8 @@
       is_incognito_and_in_memory ? IndexedDBBackingStore::Mode::kInMemory
                                  : IndexedDBBackingStore::Mode::kOnDisk;
   std::unique_ptr<IndexedDBBackingStore> backing_store = CreateBackingStore(
-      backing_store_mode, leveldb_factory_, origin, blob_path,
-      std::move(database), context_->TaskRunner());
+      backing_store_mode, &class_factory_->transactional_leveldb_factory(),
+      origin, blob_path, std::move(database), context_->TaskRunner());
   status = backing_store->Initialize(
       /*cleanup_live_journal=*/(!is_incognito_and_in_memory &&
                                 first_open_since_startup));
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.h b/content/browser/indexed_db/indexed_db_factory_impl.h
index e8424e8..c032d98 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.h
+++ b/content/browser/indexed_db/indexed_db_factory_impl.h
@@ -28,7 +28,6 @@
 #include "content/browser/indexed_db/indexed_db_factory.h"
 #include "content/browser/indexed_db/indexed_db_origin_state_handle.h"
 #include "content/browser/indexed_db/indexed_db_task_helper.h"
-#include "content/browser/indexed_db/leveldb/leveldb_env.h"
 #include "content/browser/indexed_db/scopes/leveldb_scopes_factory.h"
 #include "third_party/leveldatabase/src/include/leveldb/status.h"
 #include "url/origin.h"
@@ -42,6 +41,7 @@
 }
 
 namespace content {
+class TransactionalLevelDBFactory;
 class TransactionalLevelDBDatabase;
 class IndexedDBClassFactory;
 class IndexedDBContextImpl;
@@ -51,7 +51,6 @@
 class CONTENT_EXPORT IndexedDBFactoryImpl : public IndexedDBFactory {
  public:
   IndexedDBFactoryImpl(IndexedDBContextImpl* context,
-                       indexed_db::LevelDBFactory* leveldb_factory,
                        IndexedDBClassFactory* indexed_db_class_factory,
                        base::Clock* clock);
   ~IndexedDBFactoryImpl() override;
@@ -143,7 +142,7 @@
   // Used by unittests to allow subclassing of IndexedDBBackingStore.
   virtual std::unique_ptr<IndexedDBBackingStore> CreateBackingStore(
       IndexedDBBackingStore::Mode backing_store_mode,
-      indexed_db::LevelDBFactory* leveldb_factory,
+      TransactionalLevelDBFactory* leveldb_factory,
       const url::Origin& origin,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
@@ -207,8 +206,7 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
   IndexedDBContextImpl* context_;
-  indexed_db::LevelDBFactory* const leveldb_factory_;
-  IndexedDBClassFactory* const indexed_db_class_factory_;
+  IndexedDBClassFactory* const class_factory_;
   base::Clock* const clock_;
   base::Time earliest_sweep_;
 
diff --git a/content/browser/indexed_db/indexed_db_factory_unittest.cc b/content/browser/indexed_db/indexed_db_factory_unittest.cc
index 2563efc..723d8c9 100644
--- a/content/browser/indexed_db/indexed_db_factory_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_factory_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/default_clock.h"
+#include "content/browser/indexed_db/indexed_db_class_factory.h"
 #include "content/browser/indexed_db/indexed_db_connection.h"
 #include "content/browser/indexed_db/indexed_db_context_impl.h"
 #include "content/browser/indexed_db/indexed_db_data_format_version.h"
@@ -109,6 +110,7 @@
     }
     if (temp_dir_.IsValid())
       ASSERT_TRUE(temp_dir_.Delete());
+    IndexedDBClassFactory::Get()->SetLevelDBFactoryForTesting(nullptr);
   }
 
   void SetupContext() {
@@ -127,13 +129,13 @@
         base::SequencedTaskRunnerHandle::Get());
   }
 
-  void SetupContextWithFactories(indexed_db::LevelDBFactory* factory,
-                                 base::Clock* clock) {
+  void SetupContextWithFactories(LevelDBFactory* factory, base::Clock* clock) {
     context_ = base::MakeRefCounted<IndexedDBContextImpl>(
         CreateAndReturnTempDir(&temp_dir_),
         /*special_storage_policy=*/nullptr, quota_manager_proxy_.get(), clock,
         base::SequencedTaskRunnerHandle::Get());
-    context_->SetLevelDBFactoryForTesting(factory);
+    if (factory)
+      IndexedDBClassFactory::Get()->SetLevelDBFactoryForTesting(factory);
   }
 
   // Runs through the upgrade flow to create a basic database connection. There
@@ -299,7 +301,7 @@
   base::test::ScopedFeatureList feature_list;
   base::SimpleTestClock clock;
   clock.SetNow(base::Time::Now());
-  SetupContextWithFactories(indexed_db::LevelDBFactory::Get(), &clock);
+  SetupContextWithFactories(nullptr, &clock);
 
   const Origin origin = Origin::Create(GURL("http://localhost:81"));
 
@@ -705,7 +707,8 @@
 };
 
 TEST_F(IndexedDBFactoryTest, QuotaErrorOnDiskFull) {
-  indexed_db::FakeLevelDBFactory fake_ldb_factory;
+  FakeLevelDBFactory fake_ldb_factory(
+      IndexedDBClassFactory::GetLevelDBOptions(), "indexed-db");
   fake_ldb_factory.EnqueueNextOpenLevelDBStateResult(
       nullptr, leveldb::Status::IOError("Disk is full."), true);
   SetupContextWithFactories(&fake_ldb_factory,
diff --git a/content/browser/indexed_db/indexed_db_fake_backing_store.cc b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
index d48f6f6..9409fde4 100644
--- a/content/browser/indexed_db/indexed_db_fake_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
@@ -7,9 +7,11 @@
 #include <utility>
 
 #include "base/files/file_path.h"
+#include "base/no_destructor.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "content/browser/indexed_db/leveldb/leveldb_env.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 
 namespace content {
 namespace {
@@ -17,12 +19,17 @@
 using blink::IndexedDBKey;
 using blink::IndexedDBKeyRange;
 
+TransactionalLevelDBFactory* GetTransactionalLevelDBFactory() {
+  static base::NoDestructor<DefaultTransactionalLevelDBFactory> factory;
+  return factory.get();
+}
+
 }  // namespace
 
 IndexedDBFakeBackingStore::IndexedDBFakeBackingStore()
     : IndexedDBBackingStore(IndexedDBBackingStore::Mode::kInMemory,
                             nullptr /* indexed_db_factory */,
-                            indexed_db::LevelDBFactory::Get(),
+                            GetTransactionalLevelDBFactory(),
                             url::Origin::Create(GURL("http://localhost:81")),
                             base::FilePath(),
                             std::unique_ptr<TransactionalLevelDBDatabase>(),
@@ -32,7 +39,7 @@
     base::SequencedTaskRunner* task_runner)
     : IndexedDBBackingStore(IndexedDBBackingStore::Mode::kOnDisk,
                             factory,
-                            indexed_db::LevelDBFactory::Get(),
+                            GetTransactionalLevelDBFactory(),
                             url::Origin::Create(GURL("http://localhost:81")),
                             base::FilePath(),
                             std::unique_ptr<TransactionalLevelDBDatabase>(),
diff --git a/content/browser/indexed_db/indexed_db_metadata_coding.cc b/content/browser/indexed_db/indexed_db_metadata_coding.cc
index 0c632cef..0b76c60 100644
--- a/content/browser/indexed_db/indexed_db_metadata_coding.cc
+++ b/content/browser/indexed_db/indexed_db_metadata_coding.cc
@@ -13,8 +13,8 @@
 #include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
 #include "content/browser/indexed_db/indexed_db_reporting.h"
 #include "content/browser/indexed_db/indexed_db_tracing.h"
-#include "content/browser/indexed_db/leveldb/leveldb_env.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h"
 #include "content/browser/indexed_db/scopes/leveldb_scope_deletion_mode.h"
@@ -528,7 +528,7 @@
     IndexedDBDatabaseMetadata* metadata) {
   // TODO(jsbell): Don't persist metadata if open fails. http://crbug.com/395472
   std::unique_ptr<LevelDBDirectTransaction> transaction =
-      db->leveldb_class_factory()->CreateLevelDBDirectTransaction(db);
+      db->class_factory()->CreateLevelDBDirectTransaction(db);
 
   int64_t row_id = 0;
   Status s = indexed_db::GetNewDatabaseId(transaction.get(), &row_id);
diff --git a/content/browser/indexed_db/indexed_db_origin_state.cc b/content/browser/indexed_db/indexed_db_origin_state.cc
index 0630516..f070616 100644
--- a/content/browser/indexed_db/indexed_db_origin_state.cc
+++ b/content/browser/indexed_db/indexed_db_origin_state.cc
@@ -23,6 +23,7 @@
 #include "content/browser/indexed_db/indexed_db_tombstone_sweeper.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
 
@@ -79,7 +80,7 @@
     url::Origin origin,
     bool persist_for_incognito,
     base::Clock* clock,
-    indexed_db::LevelDBFactory* leveldb_factory,
+    TransactionalLevelDBFactory* transactional_leveldb_factory,
     base::Time* earliest_global_sweep_time,
     std::unique_ptr<DisjointRangeLockManager> lock_manager,
     TasksAvailableCallback notify_tasks_callback,
@@ -88,7 +89,7 @@
     : origin_(std::move(origin)),
       persist_for_incognito_(persist_for_incognito),
       clock_(clock),
-      leveldb_factory_(leveldb_factory),
+      transactional_leveldb_factory_(transactional_leveldb_factory),
       earliest_global_sweep_time_(earliest_global_sweep_time),
       lock_manager_(std::move(lock_manager)),
       backing_store_(std::move(backing_store)),
@@ -322,7 +323,8 @@
   // A sweep will happen now, so reset the sweep timers.
   *earliest_global_sweep_time_ = GenerateNextGlobalSweepTime(now);
   std::unique_ptr<LevelDBDirectTransaction> txn =
-      leveldb_factory_->CreateLevelDBDirectTransaction(backing_store_->db());
+      transactional_leveldb_factory_->CreateLevelDBDirectTransaction(
+          backing_store_->db());
   s = indexed_db::SetEarliestSweepTime(txn.get(),
                                        GenerateNextOriginSweepTime(now));
   // TODO(dmurph): Log this or report to UMA.
diff --git a/content/browser/indexed_db/indexed_db_origin_state.h b/content/browser/indexed_db/indexed_db_origin_state.h
index 1b68130..3d18c41 100644
--- a/content/browser/indexed_db/indexed_db_origin_state.h
+++ b/content/browser/indexed_db/indexed_db_origin_state.h
@@ -26,10 +26,7 @@
 class IndexedDBDatabase;
 class IndexedDBFactoryImpl;
 class IndexedDBPreCloseTaskQueue;
-
-namespace indexed_db {
-class LevelDBFactory;
-}  // namespace indexed_db
+class TransactionalLevelDBFactory;
 
 constexpr const char kIDBCloseImmediatelySwitch[] = "idb-close-immediately";
 
@@ -77,15 +74,16 @@
 
   // Calling |destruct_myself| should destruct this object.
   // |earliest_global_sweep_time| is expected to outlive this object.
-  IndexedDBOriginState(url::Origin origin,
-                       bool persist_for_incognito,
-                       base::Clock* clock,
-                       indexed_db::LevelDBFactory* leveldb_factory,
-                       base::Time* earliest_global_sweep_time,
-                       std::unique_ptr<DisjointRangeLockManager> lock_manager,
-                       TasksAvailableCallback notify_tasks_callback,
-                       TearDownCallback tear_down_callback,
-                       std::unique_ptr<IndexedDBBackingStore> backing_store);
+  IndexedDBOriginState(
+      url::Origin origin,
+      bool persist_for_incognito,
+      base::Clock* clock,
+      TransactionalLevelDBFactory* transactional_leveldb_factory,
+      base::Time* earliest_global_sweep_time,
+      std::unique_ptr<DisjointRangeLockManager> lock_manager,
+      TasksAvailableCallback notify_tasks_callback,
+      TearDownCallback tear_down_callback,
+      std::unique_ptr<IndexedDBBackingStore> backing_store);
   ~IndexedDBOriginState();
 
   void AbortAllTransactions(bool compact);
@@ -184,7 +182,7 @@
   bool has_blobs_outstanding_ = false;
   bool skip_closing_sequence_ = false;
   base::Clock* const clock_;
-  indexed_db::LevelDBFactory* const leveldb_factory_;
+  TransactionalLevelDBFactory* const transactional_leveldb_factory_;
 
   bool running_tasks_ = false;
   bool task_run_scheduled_ = false;
diff --git a/content/browser/indexed_db/indexed_db_tombstone_sweeper_unittest.cc b/content/browser/indexed_db/indexed_db_tombstone_sweeper_unittest.cc
index a9d8d8d..fc036fe8b 100644
--- a/content/browser/indexed_db/indexed_db_tombstone_sweeper_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_tombstone_sweeper_unittest.cc
@@ -13,10 +13,13 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/time/tick_clock.h"
+#include "content/browser/indexed_db/indexed_db_class_factory.h"
 #include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
 #include "content/browser/indexed_db/leveldb/leveldb_env.h"
+#include "content/browser/indexed_db/leveldb/leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/mock_level_db.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/scopes/leveldb_scopes.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -140,13 +143,16 @@
     scoped_refptr<LevelDBState> level_db_state;
     leveldb::Status s;
     std::tie(level_db_state, s, std::ignore) =
-        indexed_db::LevelDBFactory::Get()->OpenLevelDBState(
+        IndexedDBClassFactory::Get()->leveldb_factory().OpenLevelDBState(
             base::FilePath(), indexed_db::GetDefaultLevelDBComparator(),
             /* create_if_missing=*/true);
     ASSERT_TRUE(s.ok());
-    in_memory_db_ = indexed_db::LevelDBFactory::Get()->CreateLevelDBDatabase(
-        std::move(level_db_state), nullptr, nullptr,
-        TransactionalLevelDBDatabase::kDefaultMaxOpenIteratorsPerDatabase);
+    in_memory_db_ =
+        IndexedDBClassFactory::Get()
+            ->transactional_leveldb_factory()
+            .CreateLevelDBDatabase(std::move(level_db_state), nullptr, nullptr,
+                                   TransactionalLevelDBDatabase::
+                                       kDefaultMaxOpenIteratorsPerDatabase);
     sweeper_ = std::make_unique<IndexedDBTombstoneSweeper>(
         kRoundIterations, kMaxIterations, in_memory_db_->db());
     sweeper_->SetStartSeedsForTesting(0, 0, 0);
diff --git a/content/browser/indexed_db/leveldb/fake_leveldb_factory.cc b/content/browser/indexed_db/leveldb/fake_leveldb_factory.cc
index 8e854f7..456d4fe 100644
--- a/content/browser/indexed_db/leveldb/fake_leveldb_factory.cc
+++ b/content/browser/indexed_db/leveldb/fake_leveldb_factory.cc
@@ -20,7 +20,6 @@
 #include "third_party/leveldatabase/src/include/leveldb/status.h"
 
 namespace content {
-namespace indexed_db {
 namespace {
 class FlakyIterator;
 
@@ -359,7 +358,9 @@
 
 }  // namespace
 
-FakeLevelDBFactory::FakeLevelDBFactory() = default;
+FakeLevelDBFactory::FakeLevelDBFactory(leveldb_env::Options database_options,
+                                       const std::string& in_memory_db_name)
+    : DefaultLevelDBFactory(database_options, in_memory_db_name) {}
 FakeLevelDBFactory::~FakeLevelDBFactory() {}
 
 // static
@@ -385,7 +386,7 @@
     leveldb::Status error_to_return,
     const base::FilePath& reported_file_path) {
   return LevelDBState::CreateForDiskDB(
-      indexed_db::GetDefaultLevelDBComparator(),
+      leveldb::BytewiseComparator(),
       std::make_unique<BrokenDB>(error_to_return), reported_file_path);
 }
 
@@ -397,11 +398,13 @@
 }
 
 std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status>
-FakeLevelDBFactory::OpenDB(const leveldb_env::Options& options,
-                           const std::string& name) {
+FakeLevelDBFactory::OpenDB(const std::string& name,
+                           bool create_if_missing,
+                           size_t write_buffer_size) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (next_dbs_.empty())
-    return DefaultLevelDBFactory::OpenDB(options, name);
+    return DefaultLevelDBFactory::OpenDB(name, create_if_missing,
+                                         write_buffer_size);
   auto tuple = std::move(next_dbs_.front());
   next_dbs_.pop();
   return tuple;
@@ -418,17 +421,16 @@
 
 std::tuple<scoped_refptr<LevelDBState>, leveldb::Status, bool /*disk_full*/>
 FakeLevelDBFactory::OpenLevelDBState(const base::FilePath& file_name,
-                                     const leveldb::Comparator* ldb_comparator,
-                                     bool create_if_missing) {
+                                     bool create_if_missing,
+                                     size_t write_buffer_size) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (next_leveldb_states_.empty()) {
-    return DefaultLevelDBFactory::OpenLevelDBState(file_name, ldb_comparator,
-                                                   create_if_missing);
+    return DefaultLevelDBFactory::OpenLevelDBState(file_name, create_if_missing,
+                                                   write_buffer_size);
   }
   auto tuple = std::move(next_leveldb_states_.front());
   next_leveldb_states_.pop();
   return tuple;
 }
 
-}  // namespace indexed_db
 }  // namespace content
diff --git a/content/browser/indexed_db/leveldb/fake_leveldb_factory.h b/content/browser/indexed_db/leveldb/fake_leveldb_factory.h
index adb518a..73c1c749 100644
--- a/content/browser/indexed_db/leveldb/fake_leveldb_factory.h
+++ b/content/browser/indexed_db/leveldb/fake_leveldb_factory.h
@@ -10,17 +10,17 @@
 #include <utility>
 
 #include "base/sequence_checker.h"
-#include "content/browser/indexed_db/leveldb/leveldb_env.h"
+#include "content/browser/indexed_db/leveldb/leveldb_factory.h"
 
 namespace content {
-namespace indexed_db {
 
 // Used for unittests, this factory will only create in-memory leveldb
 // databases, and will optionally allow the user to override the next
 // LevelDBState returned with |EnqueueNextLevelDBState|.
 class FakeLevelDBFactory : public DefaultLevelDBFactory {
  public:
-  FakeLevelDBFactory();
+  FakeLevelDBFactory(leveldb_env::Options database_options,
+                     const std::string& in_memory_db_name);
   ~FakeLevelDBFactory() override;
 
   struct FlakePoint {
@@ -49,8 +49,9 @@
                                leveldb::Status status);
 
   std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status> OpenDB(
-      const leveldb_env::Options& options,
-      const std::string& name) override;
+      const std::string& name,
+      bool create_if_missing,
+      size_t write_buffer_size) override;
 
   void EnqueueNextOpenLevelDBStateResult(scoped_refptr<LevelDBState> state,
                                          leveldb::Status status,
@@ -58,8 +59,8 @@
 
   std::tuple<scoped_refptr<LevelDBState>, leveldb::Status, bool /*disk_full*/>
   OpenLevelDBState(const base::FilePath& file_name,
-                   const leveldb::Comparator* ldb_comparator,
-                   bool create_if_missing) override;
+                   bool create_if_missing,
+                   size_t write_buffer_size) override;
 
  private:
   SEQUENCE_CHECKER(sequence_checker_);
@@ -72,7 +73,6 @@
       next_leveldb_states_;
 };
 
-}  // namespace indexed_db
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_INDEXED_DB_LEVELDB_FAKE_LEVELDB_FACTORY_H_
diff --git a/content/browser/indexed_db/leveldb/leveldb_env.cc b/content/browser/indexed_db/leveldb/leveldb_env.cc
index 21de8bf..24d0591 100644
--- a/content/browser/indexed_db/leveldb/leveldb_env.cc
+++ b/content/browser/indexed_db/leveldb/leveldb_env.cc
@@ -4,22 +4,6 @@
 
 #include "content/browser/indexed_db/leveldb/leveldb_env.h"
 
-#include "base/files/file_util.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/no_destructor.h"
-#include "base/system/sys_info.h"
-#include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
-#include "content/browser/indexed_db/indexed_db_reporting.h"
-#include "content/browser/indexed_db/indexed_db_tracing.h"
-#include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
-#include "content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h"
-#include "content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h"
-#include "content/browser/indexed_db/scopes/leveldb_scope.h"
-#include "content/browser/indexed_db/scopes/leveldb_scopes.h"
-#include "third_party/leveldatabase/leveldb_chrome.h"
-#include "third_party/leveldatabase/src/include/leveldb/filter_policy.h"
-
 namespace content {
 
 LevelDBEnv::LevelDBEnv() : ChromiumEnv("LevelDBEnv.IDB") {}
@@ -29,188 +13,4 @@
   return g_leveldb_env.get();
 }
 
-namespace indexed_db {
-
-leveldb_env::Options GetLevelDBOptions(leveldb::Env* env,
-                                       const leveldb::Comparator* comparator,
-                                       bool create_if_missing,
-                                       size_t write_buffer_size,
-                                       bool paranoid_checks) {
-  static const leveldb::FilterPolicy* kIDBFilterPolicy =
-      leveldb::NewBloomFilterPolicy(10);
-  leveldb_env::Options options;
-  options.comparator = comparator;
-  options.create_if_missing = create_if_missing;
-  options.paranoid_checks = paranoid_checks;
-  options.filter_policy = kIDBFilterPolicy;
-  options.compression = leveldb::kSnappyCompression;
-  options.write_buffer_size = write_buffer_size;
-  // For info about the troubles we've run into with this parameter, see:
-  // https://crbug.com/227313#c11
-  options.max_open_files = 80;
-  options.env = env;
-  options.block_cache = leveldb_chrome::GetSharedWebBlockCache();
-  return options;
-}
-
-static LevelDBFactory::GetterCallback* s_ldb_factory_getter;
-
-// static
-LevelDBFactory* LevelDBFactory::Get() {
-  static base::NoDestructor<DefaultLevelDBFactory> singleton;
-  if (s_ldb_factory_getter)
-    return (*s_ldb_factory_getter)();
-  return singleton.get();
-}
-
-// static
-void LevelDBFactory::SetFactoryGetterForTesting(
-    LevelDBFactory::GetterCallback* cb) {
-  s_ldb_factory_getter = cb;
-}
-
-std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status>
-DefaultLevelDBFactory::OpenDB(const leveldb_env::Options& options,
-                              const std::string& name) {
-  std::unique_ptr<leveldb::DB> db;
-  leveldb::Status s = leveldb_env::OpenDB(options, name, &db);
-  return {std::move(db), s};
-}
-
-std::tuple<scoped_refptr<LevelDBState>, leveldb::Status, bool /* disk_full*/>
-DefaultLevelDBFactory::OpenLevelDBState(
-    const base::FilePath& file_name,
-    const leveldb::Comparator* ldb_comparator,
-    bool create_if_missing) {
-  // Please see docs/open_and_verify_leveldb_database.code2flow, and the
-  // generated pdf (from https://code2flow.com).
-  // The intended strategy here is to have this function match that flowchart,
-  // where the flowchart should be seen as the 'master' logic template. Please
-  // check the git history of both to make sure they are supposed to be in sync.
-  IDB_TRACE("indexed_db::OpenLDB");
-  base::TimeTicks begin_time = base::TimeTicks::Now();
-  leveldb::Status status;
-  std::unique_ptr<leveldb::DB> db;
-
-  if (file_name.empty()) {
-    if (!create_if_missing)
-      return {nullptr, leveldb::Status::NotFound("", ""), false};
-
-    std::unique_ptr<leveldb::Env> in_memory_env =
-        leveldb_chrome::NewMemEnv("indexed-db", LevelDBEnv::Get());
-
-    constexpr int64_t kBytesInOneMegabyte = 1024 * 1024;
-    leveldb_env::Options in_memory_options = GetLevelDBOptions(
-        in_memory_env.get(), ldb_comparator, create_if_missing,
-        /* default of 4MB */ 4 * kBytesInOneMegabyte,
-        /*paranoid_checks=*/false);
-    std::tie(db, status) = OpenDB(in_memory_options, std::string());
-    if (!status.ok()) {
-      LOG(ERROR) << "Failed to open in-memory LevelDB database: "
-                 << status.ToString();
-      return {nullptr, status, false};
-    }
-
-    return {LevelDBState::CreateForInMemoryDB(std::move(in_memory_env),
-                                              ldb_comparator, std::move(db),
-                                              "in-memory-database"),
-            status, false};
-  }
-
-  leveldb_env::Options options =
-      GetLevelDBOptions(LevelDBEnv::Get(), ldb_comparator, create_if_missing,
-                        leveldb_env::WriteBufferSize(
-                            base::SysInfo::AmountOfTotalDiskSpace(file_name)),
-                        /*paranoid_checks=*/true);
-
-  // ChromiumEnv assumes UTF8, converts back to FilePath before using.
-  std::tie(db, status) = OpenDB(options, file_name.AsUTF8Unsafe());
-  if (!status.ok()) {
-    if (!create_if_missing)
-      return {nullptr, leveldb::Status::NotFound("", ""), false};
-
-    ReportLevelDBError("WebCore.IndexedDB.LevelDBOpenErrors", status);
-
-    constexpr int64_t kBytesInOneKilobyte = 1024;
-    int64_t free_disk_space_bytes =
-        base::SysInfo::AmountOfFreeDiskSpace(file_name);
-    bool below_100kb = free_disk_space_bytes != -1 &&
-                       free_disk_space_bytes < 100 * kBytesInOneKilobyte;
-
-    // Disks with <100k of free space almost never succeed in opening a
-    // leveldb database.
-    bool is_disk_full = below_100kb || leveldb_env::IndicatesDiskFull(status);
-
-    LOG(ERROR) << "Failed to open LevelDB database from "
-               << file_name.AsUTF8Unsafe() << "," << status.ToString();
-    return {nullptr, status, is_disk_full};
-  }
-
-  UMA_HISTOGRAM_MEDIUM_TIMES("WebCore.IndexedDB.LevelDB.OpenTime",
-                             base::TimeTicks::Now() - begin_time);
-
-  return {LevelDBState::CreateForDiskDB(ldb_comparator, std::move(db),
-                                        std::move(file_name)),
-          status, false};
-}
-
-std::unique_ptr<TransactionalLevelDBDatabase>
-DefaultLevelDBFactory::CreateLevelDBDatabase(
-    scoped_refptr<LevelDBState> state,
-    std::unique_ptr<LevelDBScopes> scopes,
-    scoped_refptr<base::SequencedTaskRunner> task_runner,
-    size_t max_open_iterators) {
-  return base::WrapUnique(new TransactionalLevelDBDatabase(
-      std::move(state), std::move(scopes), this, std::move(task_runner),
-      max_open_iterators));
-}
-std::unique_ptr<LevelDBDirectTransaction>
-DefaultLevelDBFactory::CreateLevelDBDirectTransaction(
-    TransactionalLevelDBDatabase* db) {
-  return base::WrapUnique(new LevelDBDirectTransaction(db));
-}
-
-scoped_refptr<TransactionalLevelDBTransaction>
-DefaultLevelDBFactory::CreateLevelDBTransaction(
-    TransactionalLevelDBDatabase* db,
-    std::unique_ptr<LevelDBScope> scope) {
-  return base::WrapRefCounted(
-      new TransactionalLevelDBTransaction(db, std::move(scope)));
-}
-
-std::unique_ptr<TransactionalLevelDBIterator>
-DefaultLevelDBFactory::CreateIterator(
-    std::unique_ptr<leveldb::Iterator> it,
-    base::WeakPtr<TransactionalLevelDBDatabase> db,
-    base::WeakPtr<TransactionalLevelDBTransaction> txn,
-    std::unique_ptr<LevelDBSnapshot> snapshot) {
-  return base::WrapUnique(new TransactionalLevelDBIterator(
-      std::move(it), std::move(db), std::move(txn), std::move(snapshot)));
-}
-
-leveldb::Status DefaultLevelDBFactory::DestroyLevelDB(
-    scoped_refptr<LevelDBState> level_db_state) {
-  DCHECK(level_db_state);
-  DCHECK(!level_db_state->in_memory_env() &&
-         !level_db_state->database_path().empty())
-      << "Cannot destroy an in-memory databases.";
-  DCHECK(level_db_state->HasOneRef())
-      << "Cannot destroy the database when someone else is keeping the state "
-         "alive.";
-  leveldb_env::Options options;
-  options.env = LevelDBEnv::Get();
-  std::string file_path = level_db_state->database_path().AsUTF8Unsafe();
-  level_db_state.reset();
-  // ChromiumEnv assumes UTF8, converts back to FilePath before using.
-  return leveldb::DestroyDB(std::move(file_path), options);
-}
-
-leveldb::Status DefaultLevelDBFactory::DestroyLevelDB(
-    const base::FilePath& path) {
-  leveldb_env::Options options;
-  options.env = LevelDBEnv::Get();
-  return leveldb::DestroyDB(path.AsUTF8Unsafe(), options);
-}
-
-}  // namespace indexed_db
 }  // namespace content
diff --git a/content/browser/indexed_db/leveldb/leveldb_env.h b/content/browser/indexed_db/leveldb/leveldb_env.h
index a1a0afd0..98acac75 100644
--- a/content/browser/indexed_db/leveldb/leveldb_env.h
+++ b/content/browser/indexed_db/leveldb/leveldb_env.h
@@ -8,39 +8,15 @@
 #include <memory>
 #include <tuple>
 
-#include "base/files/file_path.h"
-#include "base/lazy_instance.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "content/browser/indexed_db/scopes/leveldb_state.h"
+#include "base/no_destructor.h"
 #include "content/common/content_export.h"
 #include "third_party/leveldatabase/env_chromium.h"
-#include "third_party/leveldatabase/src/include/leveldb/comparator.h"
-#include "third_party/leveldatabase/src/include/leveldb/status.h"
-
-namespace base {
-template <typename T>
-class NoDestructor;
-}
-
-namespace leveldb {
-class Iterator;
-}  // namespace leveldb
 
 namespace content {
-class TransactionalLevelDBDatabase;
-class TransactionalLevelDBIterator;
-class LevelDBScope;
-class LevelDBScopes;
-class LevelDBSnapshot;
-class TransactionalLevelDBTransaction;
-class LevelDBDirectTransaction;
 
 // The leveldb::Env used by the Indexed DB backend.
 class LevelDBEnv : public leveldb_env::ChromiumEnv {
  public:
-
   CONTENT_EXPORT static LevelDBEnv* Get();
 
  private:
@@ -48,115 +24,6 @@
   LevelDBEnv();
 };
 
-namespace indexed_db {
-
-// Visible for testing.
-CONTENT_EXPORT leveldb_env::Options GetLevelDBOptions(
-    leveldb::Env* env,
-    const leveldb::Comparator* comparator,
-    bool create_if_missing,
-    size_t write_buffer_size,
-    bool paranoid_checks);
-
-// Factory class used to open leveldb databases, and stores all necessary
-// objects in a LevelDBState. This interface exists so that it can be mocked out
-// for tests.
-class CONTENT_EXPORT LevelDBFactory {
- public:
-  using GetterCallback = LevelDBFactory*();
-
-  // Returns a singleton factory, which can be customized below for testing.
-  // Note: it is best to avoid using this method, and instead acquiring a
-  // LevelDBFactory through dependency injection.
-  static LevelDBFactory* Get();
-
-  static void SetFactoryGetterForTesting(LevelDBFactory::GetterCallback* cb);
-
-  virtual ~LevelDBFactory() = default;
-
-  virtual std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status> OpenDB(
-      const leveldb_env::Options& options,
-      const std::string& name) = 0;
-
-  // Opens a leveldb database with the given comparators and populates it in
-  // |output_state|. If the |file_name| is empty, then the database will be
-  // in-memory. The comparator names must match. If |create_if_missing| is
-  // false and the database doesn't exist, then the returned tuple will be
-  // {nullptr, leveldb::Status::NotFound("", ""), false}.
-  virtual std::
-      tuple<scoped_refptr<LevelDBState>, leveldb::Status, bool /* disk_full*/>
-      OpenLevelDBState(const base::FilePath& file_name,
-                       const leveldb::Comparator* ldb_comparator,
-                       bool create_if_missing) = 0;
-
-  // All calls to the LevelDBDatabase class should be done on |task_runner|.
-  virtual std::unique_ptr<TransactionalLevelDBDatabase> CreateLevelDBDatabase(
-      scoped_refptr<LevelDBState> state,
-      std::unique_ptr<LevelDBScopes> scopes,
-      scoped_refptr<base::SequencedTaskRunner> task_runner,
-      size_t max_open_iterators) = 0;
-
-  // A LevelDBDirectTransaction is a simple wrapper of a WriteBatch, which
-  // writes it on |Commit|.
-  virtual std::unique_ptr<LevelDBDirectTransaction>
-  CreateLevelDBDirectTransaction(TransactionalLevelDBDatabase* db) = 0;
-
-  // A LevelDBTransaction uses the LevelDBScope to write changes, and
-  // appropriately flushes the scope for reads.
-  virtual scoped_refptr<TransactionalLevelDBTransaction>
-  CreateLevelDBTransaction(TransactionalLevelDBDatabase* db,
-                           std::unique_ptr<LevelDBScope> scope) = 0;
-
-  virtual std::unique_ptr<TransactionalLevelDBIterator> CreateIterator(
-      std::unique_ptr<leveldb::Iterator> it,
-      base::WeakPtr<TransactionalLevelDBDatabase> db,
-      base::WeakPtr<TransactionalLevelDBTransaction> txn,
-      std::unique_ptr<LevelDBSnapshot> snapshot) = 0;
-
-  // A somewhat safe way to destroy a leveldb database. This asserts that there
-  // are no other references to the given LevelDBState, and deletes the database
-  // on disk. |level_db_state| must only have one ref.
-  virtual leveldb::Status DestroyLevelDB(
-      scoped_refptr<LevelDBState> output_state) = 0;
-
-  // Assumes that there is no leveldb database currently running for this path.
-  virtual leveldb::Status DestroyLevelDB(const base::FilePath& path) = 0;
-};
-
-class CONTENT_EXPORT DefaultLevelDBFactory : public LevelDBFactory {
- public:
-  DefaultLevelDBFactory() = default;
-  ~DefaultLevelDBFactory() override {}
-
-  std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status> OpenDB(
-      const leveldb_env::Options& options,
-      const std::string& name) override;
-
-  std::tuple<scoped_refptr<LevelDBState>, leveldb::Status, bool /* disk_full*/>
-  OpenLevelDBState(const base::FilePath& file_name,
-                   const leveldb::Comparator* ldb_comparator,
-                   bool create_if_missing) override;
-  std::unique_ptr<TransactionalLevelDBDatabase> CreateLevelDBDatabase(
-      scoped_refptr<LevelDBState> state,
-      std::unique_ptr<LevelDBScopes> scopes,
-      scoped_refptr<base::SequencedTaskRunner> task_runner,
-      size_t max_open_iterators) override;
-  std::unique_ptr<LevelDBDirectTransaction> CreateLevelDBDirectTransaction(
-      TransactionalLevelDBDatabase* db) override;
-  scoped_refptr<TransactionalLevelDBTransaction> CreateLevelDBTransaction(
-      TransactionalLevelDBDatabase* db,
-      std::unique_ptr<LevelDBScope> scope) override;
-  std::unique_ptr<TransactionalLevelDBIterator> CreateIterator(
-      std::unique_ptr<leveldb::Iterator> it,
-      base::WeakPtr<TransactionalLevelDBDatabase> db,
-      base::WeakPtr<TransactionalLevelDBTransaction> txn,
-      std::unique_ptr<LevelDBSnapshot> snapshot) override;
-  leveldb::Status DestroyLevelDB(
-      scoped_refptr<LevelDBState> output_state) override;
-  leveldb::Status DestroyLevelDB(const base::FilePath& path) override;
-};
-
-}  // namespace indexed_db
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_ENV_H_
diff --git a/content/browser/indexed_db/leveldb/leveldb_env_unittest.cc b/content/browser/indexed_db/leveldb/leveldb_env_unittest.cc
index b74bf38..8ed8a407 100644
--- a/content/browser/indexed_db/leveldb/leveldb_env_unittest.cc
+++ b/content/browser/indexed_db/leveldb/leveldb_env_unittest.cc
@@ -10,18 +10,32 @@
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/optional.h"
-#include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
+#include "content/browser/indexed_db/leveldb/leveldb_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/leveldatabase/src/include/leveldb/comparator.h"
 
 namespace content {
 namespace indexed_db {
 namespace {
+leveldb_env::Options GetLevelDBOptions() {
+  static base::NoDestructor<leveldb_env::ChromiumEnv> test_env;
+  static const leveldb::FilterPolicy* kFilterPolicy =
+      leveldb::NewBloomFilterPolicy(10);
+  leveldb_env::Options options;
+  options.comparator = leveldb::BytewiseComparator();
+  options.paranoid_checks = true;
+  options.filter_policy = kFilterPolicy;
+  options.compression = leveldb::kSnappyCompression;
+  options.env = test_env.get();
+  return options;
+}
 
 TEST(LevelDBEnvTest, TestInMemory) {
-  DefaultLevelDBFactory default_factory;
+  DefaultLevelDBFactory default_factory(GetLevelDBOptions(),
+                                        "test-in-memory-db");
   scoped_refptr<LevelDBState> state;
   std::tie(state, std::ignore, std::ignore) = default_factory.OpenLevelDBState(
-      base::FilePath(), GetDefaultLevelDBComparator(),
+      base::FilePath(), leveldb::BytewiseComparator(),
       /* create_if_missing=*/true);
   EXPECT_TRUE(state);
   EXPECT_TRUE(state->in_memory_env());
diff --git a/content/browser/indexed_db/leveldb/leveldb_factory.cc b/content/browser/indexed_db/leveldb/leveldb_factory.cc
new file mode 100644
index 0000000..b0a8225
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_factory.cc
@@ -0,0 +1,103 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/leveldb/leveldb_factory.h"
+
+#include "base/system/sys_info.h"
+#include "third_party/leveldatabase/leveldb_chrome.h"
+
+namespace content {
+
+DefaultLevelDBFactory::DefaultLevelDBFactory(
+    leveldb_env::Options database_options,
+    const std::string& in_memory_db_name)
+    : options_(database_options), in_memory_db_name_(in_memory_db_name) {}
+DefaultLevelDBFactory::~DefaultLevelDBFactory() = default;
+
+std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status>
+DefaultLevelDBFactory::OpenInMemoryDB(leveldb::Env* in_memory_env) {
+  constexpr int64_t kBytesInOneMegabyte = 1024 * 1024;
+  leveldb_env::Options in_memory_options = options_;
+  in_memory_options.write_buffer_size =
+      /* default of 4MB */ 4 * kBytesInOneMegabyte;
+  in_memory_options.paranoid_checks = false;
+  in_memory_options.env = in_memory_env;
+  in_memory_options.create_if_missing = true;
+  std::unique_ptr<leveldb::DB> db;
+  leveldb::Status s =
+      leveldb_env::OpenDB(in_memory_options, std::string(), &db);
+  return {std::move(db), s};
+}
+
+std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status>
+DefaultLevelDBFactory::OpenDB(const std::string& name,
+                              bool create_if_missing,
+                              size_t write_buffer_size) {
+  std::unique_ptr<leveldb::DB> db;
+  options_.create_if_missing = create_if_missing;
+  options_.write_buffer_size = write_buffer_size;
+  leveldb::Status s = leveldb_env::OpenDB(options_, name, &db);
+  return {std::move(db), s};
+}
+
+std::tuple<scoped_refptr<LevelDBState>,
+           leveldb::Status,
+           /* is_disk_full= */ bool>
+DefaultLevelDBFactory::OpenLevelDBState(const base::FilePath& file_name,
+                                        bool create_if_missing,
+                                        size_t write_buffer_size) {
+  leveldb::Status status;
+  std::unique_ptr<leveldb::DB> db;
+
+  if (file_name.empty()) {
+    if (!create_if_missing)
+      return {nullptr, leveldb::Status::NotFound("", ""), false};
+
+    std::unique_ptr<leveldb::Env> in_memory_env =
+        leveldb_chrome::NewMemEnv(in_memory_db_name_, options_.env);
+    std::tie(db, status) = OpenInMemoryDB(in_memory_env.get());
+    if (UNLIKELY(!status.ok())) {
+      LOG(ERROR) << "Failed to open in-memory LevelDB database: "
+                 << status.ToString();
+      return {nullptr, status, false};
+    }
+
+    return {LevelDBState::CreateForInMemoryDB(
+                std::move(in_memory_env), options_.comparator, std::move(db),
+                "in-memory-database"),
+            status, false};
+  }
+
+  // ChromiumEnv assumes UTF8, converts back to FilePath before using.
+  std::tie(db, status) =
+      OpenDB(file_name.AsUTF8Unsafe(), create_if_missing, write_buffer_size);
+  if (UNLIKELY(!status.ok())) {
+    if (!create_if_missing && status.IsInvalidArgument())
+      return {nullptr, leveldb::Status::NotFound("", ""), false};
+    constexpr int64_t kBytesInOneKilobyte = 1024;
+    int64_t free_disk_space_bytes =
+        base::SysInfo::AmountOfFreeDiskSpace(file_name);
+    bool below_100kb = free_disk_space_bytes != -1 &&
+                       free_disk_space_bytes < 100 * kBytesInOneKilobyte;
+
+    // Disks with <100k of free space almost never succeed in opening a
+    // leveldb database.
+    bool is_disk_full = below_100kb || leveldb_env::IndicatesDiskFull(status);
+
+    LOG(ERROR) << "Failed to open LevelDB database from "
+               << file_name.AsUTF8Unsafe() << "," << status.ToString();
+    return {nullptr, status, is_disk_full};
+  }
+
+  return {LevelDBState::CreateForDiskDB(options_.comparator, std::move(db),
+                                        std::move(file_name)),
+          status, false};
+}
+
+leveldb::Status DefaultLevelDBFactory::DestroyLevelDB(
+    const base::FilePath& path) {
+  return leveldb::DestroyDB(path.AsUTF8Unsafe(), options_);
+}
+
+}  // namespace content
diff --git a/content/browser/indexed_db/leveldb/leveldb_factory.h b/content/browser/indexed_db/leveldb/leveldb_factory.h
new file mode 100644
index 0000000..fc83d085
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/leveldb_factory.h
@@ -0,0 +1,90 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_FACTORY_H_
+#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_FACTORY_H_
+
+#include <tuple>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "content/browser/indexed_db/scopes/leveldb_state.h"
+#include "content/common/content_export.h"
+#include "third_party/leveldatabase/env_chromium.h"
+#include "third_party/leveldatabase/src/include/leveldb/comparator.h"
+#include "third_party/leveldatabase/src/include/leveldb/status.h"
+
+namespace content {
+
+// Factory class used to open leveldb databases, and stores all necessary
+// objects in a LevelDBState. This interface exists so that it can be mocked out
+// for tests.
+class CONTENT_EXPORT LevelDBFactory {
+ public:
+  virtual ~LevelDBFactory() = default;
+
+  // Creates an in-memory database.
+  virtual std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status>
+  OpenInMemoryDB(leveldb::Env* in_memory_env) = 0;
+
+  // Opens a leveldb database with the given name. If |create_if_missing| is
+  // true, then the database will be created if it does not exist.
+  virtual std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status> OpenDB(
+      const std::string& name,
+      bool create_if_missing,
+      size_t write_buffer_size) = 0;
+
+  // Opens a leveldb database and returns it inside of a LevelDBState. If the
+  // |file_name| is empty, then the database will be in-memory. If
+  // |create_if_missing| is false and the database doesn't exist, then the
+  // return tuple will be {nullptr, leveldb::Status::NotFound("", ""), false}.
+  virtual std::tuple<scoped_refptr<LevelDBState>,
+                     leveldb::Status,
+                     /* is_disk_full= */ bool>
+  OpenLevelDBState(const base::FilePath& file_name,
+                   bool create_if_missing,
+                   size_t write_buffer_size) = 0;
+
+  // Assumes that there is no leveldb database currently running for this path.
+  virtual leveldb::Status DestroyLevelDB(const base::FilePath& path) = 0;
+};
+
+class CONTENT_EXPORT DefaultLevelDBFactory : public LevelDBFactory {
+ public:
+  // The |database_options| are used to open any database. The
+  // |create_if_missing| is supplied by Open*() calls, and |write_buffer_size|
+  // is calculated during the Open* calls as well. |in_memory_db_name| is used
+  // for memory profiling reporting in the in-memory database environment.
+  DefaultLevelDBFactory(leveldb_env::Options database_options,
+                        const std::string& in_memory_db_name);
+  ~DefaultLevelDBFactory() override;
+
+  std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status> OpenInMemoryDB(
+      leveldb::Env* in_memory_env) override;
+
+  std::tuple<std::unique_ptr<leveldb::DB>, leveldb::Status> OpenDB(
+      const std::string& name,
+      bool create_if_missing,
+      size_t write_buffer_size) override;
+
+  std::tuple<scoped_refptr<LevelDBState>,
+             leveldb::Status,
+             /* is_disk_full= */ bool>
+  OpenLevelDBState(const base::FilePath& file_name,
+                   bool create_if_missing,
+                   size_t write_buffer_size) override;
+
+  leveldb::Status DestroyLevelDB(const base::FilePath& path) override;
+
+ protected:
+  leveldb_env::Options options_;
+  std::string in_memory_db_name_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_INDEXED_DB_LEVELDB_LEVELDB_FACTORY_H_
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_database.cc b/content/browser/indexed_db/leveldb/transactional_leveldb_database.cc
index 8aaf778c..cac139a7 100644
--- a/content/browser/indexed_db/leveldb/transactional_leveldb_database.cc
+++ b/content/browser/indexed_db/leveldb/transactional_leveldb_database.cc
@@ -30,8 +30,8 @@
 #include "content/browser/indexed_db/indexed_db_class_factory.h"
 #include "content/browser/indexed_db/indexed_db_reporting.h"
 #include "content/browser/indexed_db/indexed_db_tracing.h"
-#include "content/browser/indexed_db/leveldb/leveldb_env.h"
 #include "content/browser/indexed_db/leveldb/leveldb_write_batch.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h"
 #include "content/browser/indexed_db/scopes/leveldb_scopes.h"
@@ -80,7 +80,7 @@
 TransactionalLevelDBDatabase::TransactionalLevelDBDatabase(
     scoped_refptr<LevelDBState> level_db_state,
     std::unique_ptr<LevelDBScopes> leveldb_scopes,
-    indexed_db::LevelDBFactory* class_factory,
+    TransactionalLevelDBFactory* class_factory,
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     size_t max_open_iterators)
     : level_db_state_(std::move(level_db_state)),
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_database.h b/content/browser/indexed_db/leveldb/transactional_leveldb_database.h
index 1539855..0ac82fb 100644
--- a/content/browser/indexed_db/leveldb/transactional_leveldb_database.h
+++ b/content/browser/indexed_db/leveldb/transactional_leveldb_database.h
@@ -32,16 +32,12 @@
 
 namespace content {
 class TransactionalLevelDBDatabase;
+class TransactionalLevelDBFactory;
 class TransactionalLevelDBIterator;
 class TransactionalLevelDBTransaction;
 class LevelDBScopes;
 class LevelDBWriteBatch;
 
-namespace indexed_db {
-class LevelDBFactory;
-class DefaultLevelDBFactory;
-}  // namespace indexed_db
-
 // This class manages the acquisition and release of a leveldb snapshot.
 class CONTENT_EXPORT LevelDBSnapshot {
  public:
@@ -96,18 +92,16 @@
   LevelDBScopes* scopes() { return scopes_.get(); }
   base::Time LastModified() const { return last_modified_; }
 
-  indexed_db::LevelDBFactory* leveldb_class_factory() const {
-    return class_factory_;
-  }
+  TransactionalLevelDBFactory* class_factory() const { return class_factory_; }
 
   void SetClockForTesting(std::unique_ptr<base::Clock> clock);
 
  private:
+  friend class DefaultTransactionalLevelDBFactory;
   friend class LevelDBSnapshot;
   friend class TransactionalLevelDBIterator;
   friend class TransactionalLevelDBTransaction;
   friend class LevelDBTestDatabase;
-  friend class indexed_db::DefaultLevelDBFactory;
   FRIEND_TEST_ALL_PREFIXES(IndexedDBTest, DeleteFailsIfDirectoryLocked);
   class IteratorNotifier;
 
@@ -116,7 +110,7 @@
   TransactionalLevelDBDatabase(
       scoped_refptr<LevelDBState> level_db_state,
       std::unique_ptr<LevelDBScopes> leveldb_scopes,
-      indexed_db::LevelDBFactory* factory,
+      TransactionalLevelDBFactory* factory,
       scoped_refptr<base::SequencedTaskRunner> task_runner,
       size_t max_open_iterators);
 
@@ -140,7 +134,7 @@
 
   scoped_refptr<LevelDBState> level_db_state_;
   std::unique_ptr<LevelDBScopes> scopes_;
-  indexed_db::LevelDBFactory* class_factory_;
+  TransactionalLevelDBFactory* class_factory_;
   base::Time last_modified_;
   std::unique_ptr<base::Clock> clock_;
 
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_factory.cc b/content/browser/indexed_db/leveldb/transactional_leveldb_factory.cc
new file mode 100644
index 0000000..ee6abf57
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/transactional_leveldb_factory.cc
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
+
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h"
+#include "content/browser/indexed_db/scopes/leveldb_scope.h"
+#include "content/browser/indexed_db/scopes/leveldb_scopes.h"
+
+namespace content {
+
+std::unique_ptr<TransactionalLevelDBDatabase>
+DefaultTransactionalLevelDBFactory::CreateLevelDBDatabase(
+    scoped_refptr<LevelDBState> state,
+    std::unique_ptr<LevelDBScopes> scopes,
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    size_t max_open_iterators) {
+  return base::WrapUnique(new TransactionalLevelDBDatabase(
+      std::move(state), std::move(scopes), this, std::move(task_runner),
+      max_open_iterators));
+}
+std::unique_ptr<LevelDBDirectTransaction>
+DefaultTransactionalLevelDBFactory::CreateLevelDBDirectTransaction(
+    TransactionalLevelDBDatabase* db) {
+  return base::WrapUnique(new LevelDBDirectTransaction(db));
+}
+
+scoped_refptr<TransactionalLevelDBTransaction>
+DefaultTransactionalLevelDBFactory::CreateLevelDBTransaction(
+    TransactionalLevelDBDatabase* db,
+    std::unique_ptr<LevelDBScope> scope) {
+  return base::WrapRefCounted(
+      new TransactionalLevelDBTransaction(db, std::move(scope)));
+}
+
+std::unique_ptr<TransactionalLevelDBIterator>
+DefaultTransactionalLevelDBFactory::CreateIterator(
+    std::unique_ptr<leveldb::Iterator> it,
+    base::WeakPtr<TransactionalLevelDBDatabase> db,
+    base::WeakPtr<TransactionalLevelDBTransaction> txn,
+    std::unique_ptr<LevelDBSnapshot> snapshot) {
+  return base::WrapUnique(new TransactionalLevelDBIterator(
+      std::move(it), std::move(db), std::move(txn), std::move(snapshot)));
+}
+
+}  // namespace content
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_factory.h b/content/browser/indexed_db/leveldb/transactional_leveldb_factory.h
new file mode 100644
index 0000000..90b376620
--- /dev/null
+++ b/content/browser/indexed_db/leveldb/transactional_leveldb_factory.h
@@ -0,0 +1,79 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_LEVELDB_TRANSACTIONAL_LEVELDB_FACTORY_H_
+#define CONTENT_BROWSER_INDEXED_DB_LEVELDB_TRANSACTIONAL_LEVELDB_FACTORY_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/indexed_db/scopes/leveldb_state.h"
+#include "content/common/content_export.h"
+
+namespace leveldb {
+class Iterator;
+}  // namespace leveldb
+
+namespace content {
+class TransactionalLevelDBDatabase;
+class TransactionalLevelDBIterator;
+class LevelDBScope;
+class LevelDBScopes;
+class LevelDBSnapshot;
+class LevelDBDirectTransaction;
+class TransactionalLevelDBTransaction;
+
+class CONTENT_EXPORT TransactionalLevelDBFactory {
+ public:
+  virtual ~TransactionalLevelDBFactory() = default;
+
+  virtual std::unique_ptr<TransactionalLevelDBDatabase> CreateLevelDBDatabase(
+      scoped_refptr<LevelDBState> state,
+      std::unique_ptr<LevelDBScopes> scopes,
+      scoped_refptr<base::SequencedTaskRunner> task_runner,
+      size_t max_open_iterators) = 0;
+
+  // A LevelDBDirectTransaction is a simple wrapper of a WriteBatch, which
+  // writes it on |Commit|.
+  virtual std::unique_ptr<LevelDBDirectTransaction>
+  CreateLevelDBDirectTransaction(TransactionalLevelDBDatabase* db) = 0;
+
+  // A LevelDBTransaction uses the LevelDBScope to write changes, and
+  // appropriately flushes the scope for reads.
+  virtual scoped_refptr<TransactionalLevelDBTransaction>
+  CreateLevelDBTransaction(TransactionalLevelDBDatabase* db,
+                           std::unique_ptr<LevelDBScope> scope) = 0;
+
+  virtual std::unique_ptr<TransactionalLevelDBIterator> CreateIterator(
+      std::unique_ptr<leveldb::Iterator> it,
+      base::WeakPtr<TransactionalLevelDBDatabase> db,
+      base::WeakPtr<TransactionalLevelDBTransaction> txn,
+      std::unique_ptr<LevelDBSnapshot> snapshot) = 0;
+};
+
+class CONTENT_EXPORT DefaultTransactionalLevelDBFactory
+    : public TransactionalLevelDBFactory {
+ public:
+  DefaultTransactionalLevelDBFactory() = default;
+  ~DefaultTransactionalLevelDBFactory() override = default;
+
+  std::unique_ptr<TransactionalLevelDBDatabase> CreateLevelDBDatabase(
+      scoped_refptr<LevelDBState> state,
+      std::unique_ptr<LevelDBScopes> scopes,
+      scoped_refptr<base::SequencedTaskRunner> task_runner,
+      size_t max_open_iterators) override;
+  std::unique_ptr<LevelDBDirectTransaction> CreateLevelDBDirectTransaction(
+      TransactionalLevelDBDatabase* db) override;
+  scoped_refptr<TransactionalLevelDBTransaction> CreateLevelDBTransaction(
+      TransactionalLevelDBDatabase* db,
+      std::unique_ptr<LevelDBScope> scope) override;
+  std::unique_ptr<TransactionalLevelDBIterator> CreateIterator(
+      std::unique_ptr<leveldb::Iterator> it,
+      base::WeakPtr<TransactionalLevelDBDatabase> db,
+      base::WeakPtr<TransactionalLevelDBTransaction> txn,
+      std::unique_ptr<LevelDBSnapshot> snapshot) override;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_INDEXED_DB_LEVELDB_TRANSACTIONAL_LEVELDB_FACTORY_H_
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h b/content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h
index 4e9c4ac..19c94a9 100644
--- a/content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h
+++ b/content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h
@@ -24,10 +24,6 @@
 class TransactionalLevelDBTransaction;
 class LevelDBSnapshot;
 
-namespace indexed_db {
-class DefaultLevelDBFactory;
-}
-
 // This iterator is meant to stay 'live' to the data on disk for a given
 // transaction, and be evict-able for saving memory. Specifically, it supports:
 // * Staying up to date with the data on disk as long as the NotifyModified
@@ -60,7 +56,7 @@
   bool IsEvicted() const { return iterator_state_ != IteratorState::kActive; }
 
  protected:
-  friend class indexed_db::DefaultLevelDBFactory;
+  friend class DefaultTransactionalLevelDBFactory;
 
   TransactionalLevelDBIterator(
       std::unique_ptr<leveldb::Iterator> iterator,
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h b/content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h
index 351d3ac..c310c83 100644
--- a/content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h
+++ b/content/browser/indexed_db/leveldb/transactional_leveldb_transaction.h
@@ -27,10 +27,6 @@
 class LevelDBScope;
 class LevelDBWriteBatch;
 
-namespace indexed_db {
-class DefaultLevelDBFactory;
-}  // namespace indexed_db
-
 // Represents a transaction on top of a TransactionalLevelDBDatabase, and is
 // backed by a LevelDBScope. This class is not thread-safe.
 // Isolation: Read committed
@@ -90,7 +86,7 @@
   leveldb::Status ForceWriteChangesAndUndoLog();
 
  protected:
-  friend class indexed_db::DefaultLevelDBFactory;
+  friend class DefaultTransactionalLevelDBFactory;
   friend class TransactionalLevelDBTransactionTest;
 
   TransactionalLevelDBTransaction(TransactionalLevelDBDatabase* db,
@@ -162,7 +158,7 @@
   TransactionalLevelDBDatabase* db() { return db_; }
 
  protected:
-  friend class indexed_db::DefaultLevelDBFactory;
+  friend class DefaultTransactionalLevelDBFactory;
 
   explicit LevelDBDirectTransaction(TransactionalLevelDBDatabase* db);
 
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_transaction_unittest.cc b/content/browser/indexed_db/leveldb/transactional_leveldb_transaction_unittest.cc
index c149a7e0..db868d6 100644
--- a/content/browser/indexed_db/leveldb/transactional_leveldb_transaction_unittest.cc
+++ b/content/browser/indexed_db/leveldb/transactional_leveldb_transaction_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/bind_test_util.h"
 #include "content/browser/indexed_db/leveldb/leveldb_env.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_iterator.h"
 #include "content/browser/indexed_db/scopes/disjoint_range_lock_manager.h"
 #include "content/browser/indexed_db/scopes/leveldb_scope.h"
@@ -57,7 +58,7 @@
     s = scopes_system->StartRecoveryAndCleanupTasks(
         LevelDBScopes::TaskRunnerMode::kNewCleanupAndRevertSequences);
     ASSERT_TRUE(s.ok()) << s.ToString();
-    leveldb_database_ = leveldb_factory_->CreateLevelDBDatabase(
+    leveldb_database_ = transactional_leveldb_factory_.CreateLevelDBDatabase(
         leveldb_, std::move(scopes_system),
         base::SequencedTaskRunnerHandle::Get(), kTestingMaxOpenCursors);
   }
@@ -134,7 +135,7 @@
   TransactionalLevelDBDatabase* db() { return leveldb_database_.get(); }
 
   scoped_refptr<TransactionalLevelDBTransaction> CreateTransaction() {
-    return indexed_db::LevelDBFactory::Get()->CreateLevelDBTransaction(
+    return transactional_leveldb_factory_.CreateLevelDBTransaction(
         db(),
         db()->scopes()->CreateScope(
             AcquireLocksSync(&lock_manager_, {CreateSimpleSharedLock()}), {}));
@@ -143,6 +144,7 @@
   leveldb::Status failure_status_;
 
  private:
+  DefaultTransactionalLevelDBFactory transactional_leveldb_factory_;
   std::unique_ptr<TransactionalLevelDBDatabase> leveldb_database_;
   DisjointRangeLockManager lock_manager_ = {3};
 
diff --git a/content/browser/indexed_db/leveldb/transactional_leveldb_unittest.cc b/content/browser/indexed_db/leveldb/transactional_leveldb_unittest.cc
index 27727e6..9498f5f 100644
--- a/content/browser/indexed_db/leveldb/transactional_leveldb_unittest.cc
+++ b/content/browser/indexed_db/leveldb/transactional_leveldb_unittest.cc
@@ -21,10 +21,13 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "content/browser/indexed_db/leveldb/leveldb_env.h"
+#include "content/browser/indexed_db/leveldb/leveldb_factory.h"
 #include "content/browser/indexed_db/leveldb/leveldb_write_batch.h"
 #include "content/browser/indexed_db/leveldb/transactional_leveldb_database.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/scopes/disjoint_range_lock_manager.h"
 #include "content/browser/indexed_db/scopes/leveldb_scopes.h"
+#include "content/browser/indexed_db/scopes/leveldb_scopes_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/leveldb_chrome.h"
@@ -33,6 +36,7 @@
 namespace content {
 namespace leveldb_unittest {
 
+constexpr size_t kWriteBufferSize = 4 * 1024 * 1024;
 static const size_t kDefaultMaxOpenIteratorsPerDatabase = 50;
 
 class SimpleLDBComparator : public leveldb::Comparator {
@@ -51,21 +55,22 @@
   void FindShortSuccessor(std::string* key) const override {}
 };
 
-std::tuple<scoped_refptr<LevelDBState>, leveldb::Status, bool /* disk_full*/>
-OpenLevelDB(base::FilePath file_name) {
-  return indexed_db::LevelDBFactory::Get()->OpenLevelDBState(
-      file_name, SimpleLDBComparator::Get(), /* create_if_missing=*/true);
-}
-
-class TransactionalLevelDBDatabaseTest : public testing::Test {
+class TransactionalLevelDBDatabaseTest : public LevelDBScopesTestBase {
  public:
   TransactionalLevelDBDatabaseTest() = default;
   ~TransactionalLevelDBDatabaseTest() override = default;
 
-  leveldb::Status OpenLevelDBDatabase(scoped_refptr<LevelDBState> ldb_state) {
+  void TearDown() override {
+    // Delete the database first to free the internal LevelDBState reference.
+    transactional_leveldb_database_.reset();
+    LevelDBScopesTestBase::TearDown();
+  }
+
+  leveldb::Status OpenLevelDBDatabase() {
+    CHECK(leveldb_);
     lock_manager_ = std::make_unique<DisjointRangeLockManager>(1);
     std::unique_ptr<LevelDBScopes> scopes = std::make_unique<LevelDBScopes>(
-        std::vector<uint8_t>{'a'}, 1024ul, ldb_state, lock_manager_.get(),
+        std::vector<uint8_t>{'a'}, 1024ul, leveldb_, lock_manager_.get(),
         base::DoNothing());
     leveldb::Status status = scopes->Initialize();
     if (!status.ok())
@@ -74,75 +79,74 @@
         LevelDBScopes::TaskRunnerMode::kUseCurrentSequence);
     if (!status.ok())
       return status;
-    leveldb_database_ =
-        indexed_db::LevelDBFactory::Get()->CreateLevelDBDatabase(
-            std::move(ldb_state), std::move(scopes), nullptr,
+    transactional_leveldb_database_ =
+        transactional_leveldb_factory_.CreateLevelDBDatabase(
+            leveldb_, std::move(scopes), nullptr,
             kDefaultMaxOpenIteratorsPerDatabase);
     return leveldb::Status::OK();
   }
 
-  base::test::TaskEnvironment task_env_;
+ protected:
+  DefaultTransactionalLevelDBFactory transactional_leveldb_factory_;
+  std::unique_ptr<TransactionalLevelDBDatabase> transactional_leveldb_database_;
   std::unique_ptr<DisjointRangeLockManager> lock_manager_;
-  std::unique_ptr<TransactionalLevelDBDatabase> leveldb_database_;
 };
 
 TEST_F(TransactionalLevelDBDatabaseTest, CorruptionTest) {
-  base::ScopedTempDir temp_directory;
-  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
-
   const std::string key("key");
   const std::string value("value");
+
+  ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
+
   std::string put_value;
   std::string got_value;
   scoped_refptr<LevelDBState> ldb_state;
   leveldb::Status status;
-  std::tie(ldb_state, status, std::ignore) =
-      OpenLevelDB(temp_directory.GetPath());
+  std::tie(leveldb_, status, std::ignore) = leveldb_factory_->OpenLevelDBState(
+      temp_directory_.GetPath(), true, kWriteBufferSize);
   EXPECT_TRUE(status.ok());
 
-  status = OpenLevelDBDatabase(ldb_state);
+  status = OpenLevelDBDatabase();
   EXPECT_TRUE(status.ok());
-  EXPECT_TRUE(leveldb_database_);
+  EXPECT_TRUE(transactional_leveldb_database_);
   put_value = value;
-  status = leveldb_database_->Put(key, &put_value);
+  status = transactional_leveldb_database_->Put(key, &put_value);
   EXPECT_TRUE(status.ok());
-  leveldb_database_.reset();
-  ldb_state.reset();
+  transactional_leveldb_database_.reset();
+  CloseScopesAndDestroyLevelDBState();
 
-  std::tie(ldb_state, status, std::ignore) =
-      OpenLevelDB(temp_directory.GetPath());
+  std::tie(leveldb_, status, std::ignore) = leveldb_factory_->OpenLevelDBState(
+      temp_directory_.GetPath(), true, kWriteBufferSize);
   EXPECT_TRUE(status.ok());
 
-  OpenLevelDBDatabase(ldb_state);
+  status = OpenLevelDBDatabase();
   EXPECT_TRUE(status.ok());
   bool found = false;
-  status = leveldb_database_->Get(key, &got_value, &found);
+  status = transactional_leveldb_database_->Get(key, &got_value, &found);
   EXPECT_TRUE(status.ok());
   EXPECT_TRUE(found);
   EXPECT_EQ(value, got_value);
-  leveldb_database_.reset();
-  ldb_state.reset();
+  transactional_leveldb_database_.reset();
+  CloseScopesAndDestroyLevelDBState();
 
-  EXPECT_TRUE(
-      leveldb_chrome::CorruptClosedDBForTesting(temp_directory.GetPath()));
+  EXPECT_TRUE(leveldb_chrome::CorruptClosedDBForTesting(DatabaseDirFilePath()));
 
-  std::tie(ldb_state, status, std::ignore) =
-      OpenLevelDB(temp_directory.GetPath());
+  std::tie(leveldb_, status, std::ignore) = leveldb_factory_->OpenLevelDBState(
+      temp_directory_.GetPath(), true, kWriteBufferSize);
   EXPECT_FALSE(status.ok());
   EXPECT_TRUE(status.IsCorruption());
 
-  status = indexed_db::LevelDBFactory::Get()->DestroyLevelDB(
-      temp_directory.GetPath());
+  status = leveldb_factory_->DestroyLevelDB(DatabaseDirFilePath());
   EXPECT_TRUE(status.ok());
 
-  std::tie(ldb_state, status, std::ignore) =
-      OpenLevelDB(temp_directory.GetPath());
+  std::tie(leveldb_, status, std::ignore) = leveldb_factory_->OpenLevelDBState(
+      temp_directory_.GetPath(), true, kWriteBufferSize);
   EXPECT_TRUE(status.ok());
 
-  status = OpenLevelDBDatabase(ldb_state);
+  status = OpenLevelDBDatabase();
   EXPECT_TRUE(status.ok());
-  ASSERT_TRUE(leveldb_database_);
-  status = leveldb_database_->Get(key, &got_value, &found);
+  ASSERT_TRUE(transactional_leveldb_database_);
+  status = transactional_leveldb_database_->Get(key, &got_value, &found);
   EXPECT_TRUE(status.ok());
   EXPECT_FALSE(found);
 }
@@ -172,6 +176,7 @@
 }
 
 TEST_F(TransactionalLevelDBDatabaseTest, LastModified) {
+  SetUpRealDatabase();
   const std::string key("key");
   const std::string value("value");
   std::string put_value;
@@ -179,28 +184,22 @@
   base::SimpleTestClock* clock_ptr = test_clock.get();
   clock_ptr->Advance(base::TimeDelta::FromHours(2));
 
-  leveldb::Status status;
-  scoped_refptr<LevelDBState> ldb_state;
-  std::tie(ldb_state, status, std::ignore) = OpenLevelDB(base::FilePath());
-  EXPECT_TRUE(status.ok());
-
-  status = OpenLevelDBDatabase(ldb_state);
-  EXPECT_TRUE(status.ok());
-  ASSERT_TRUE(leveldb_database_);
-  leveldb_database_->SetClockForTesting(std::move(test_clock));
+  leveldb::Status status = OpenLevelDBDatabase();
+  ASSERT_TRUE(status.ok());
+  transactional_leveldb_database_->SetClockForTesting(std::move(test_clock));
   // Calling |Put| sets time modified.
   put_value = value;
   base::Time now_time = clock_ptr->Now();
-  status = leveldb_database_->Put(key, &put_value);
+  status = transactional_leveldb_database_->Put(key, &put_value);
   EXPECT_TRUE(status.ok());
-  EXPECT_EQ(now_time, leveldb_database_->LastModified());
+  EXPECT_EQ(now_time, transactional_leveldb_database_->LastModified());
 
   // Calling |Remove| sets time modified.
   clock_ptr->Advance(base::TimeDelta::FromSeconds(200));
   now_time = clock_ptr->Now();
-  status = leveldb_database_->Remove(key);
+  status = transactional_leveldb_database_->Remove(key);
   EXPECT_TRUE(status.ok());
-  EXPECT_EQ(now_time, leveldb_database_->LastModified());
+  EXPECT_EQ(now_time, transactional_leveldb_database_->LastModified());
 
   // Calling |Write| sets time modified
   clock_ptr->Advance(base::TimeDelta::FromMinutes(15));
@@ -208,9 +207,9 @@
   auto batch = LevelDBWriteBatch::Create();
   batch->Put(key, value);
   batch->Remove(key);
-  status = leveldb_database_->Write(batch.get());
+  status = transactional_leveldb_database_->Write(batch.get());
   EXPECT_TRUE(status.ok());
-  EXPECT_EQ(now_time, leveldb_database_->LastModified());
+  EXPECT_EQ(now_time, transactional_leveldb_database_->LastModified());
 }
 
 }  // namespace leveldb_unittest
diff --git a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
index edd9d35..bfb2d01 100644
--- a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
+++ b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
@@ -108,7 +108,7 @@
  public:
   LevelDBTestDatabase(scoped_refptr<LevelDBState> level_db_state,
                       std::unique_ptr<LevelDBScopes> leveldb_scopes,
-                      indexed_db::LevelDBFactory* factory,
+                      TransactionalLevelDBFactory* factory,
                       scoped_refptr<base::SequencedTaskRunner> task_runner,
                       size_t max_open_iterators,
                       FailMethod fail_method,
@@ -357,10 +357,13 @@
 MockBrowserTestIndexedDBClassFactory::MockBrowserTestIndexedDBClassFactory()
     : failure_class_(FAIL_CLASS_NOTHING),
       failure_method_(FAIL_METHOD_NOTHING),
-      only_trace_calls_(false) {
-}
+      only_trace_calls_(false) {}
 
-MockBrowserTestIndexedDBClassFactory::~MockBrowserTestIndexedDBClassFactory() {
+MockBrowserTestIndexedDBClassFactory::~MockBrowserTestIndexedDBClassFactory() {}
+
+TransactionalLevelDBFactory&
+MockBrowserTestIndexedDBClassFactory::transactional_leveldb_factory() {
+  return *this;
 }
 
 std::pair<std::unique_ptr<IndexedDBDatabase>, leveldb::Status>
@@ -413,7 +416,7 @@
         max_open_iterators, failure_method_,
         fail_on_call_num_[FAIL_CLASS_LEVELDB_DATABASE]);
   } else {
-    return DefaultLevelDBFactory::CreateLevelDBDatabase(
+    return DefaultTransactionalLevelDBFactory::CreateLevelDBDatabase(
         std::move(state), std::move(scopes), std::move(task_runner),
         max_open_iterators);
   }
@@ -431,7 +434,8 @@
         db, failure_method_,
         fail_on_call_num_[FAIL_CLASS_LEVELDB_DIRECT_TRANSACTION]);
   } else {
-    return DefaultLevelDBFactory::CreateLevelDBDirectTransaction(db);
+    return DefaultTransactionalLevelDBFactory::CreateLevelDBDirectTransaction(
+        db);
   }
 }
 
@@ -452,8 +456,8 @@
           db, std::move(scope), failure_method_,
           fail_on_call_num_[FAIL_CLASS_LEVELDB_TRANSACTION]);
     } else {
-      return DefaultLevelDBFactory::CreateLevelDBTransaction(db,
-                                                             std::move(scope));
+      return DefaultTransactionalLevelDBFactory::CreateLevelDBTransaction(
+          db, std::move(scope));
     }
   }
 }
@@ -478,7 +482,7 @@
           std::move(iterator), db, std::move(txn), std::move(snapshot),
           failure_method_, fail_on_call_num_[FAIL_CLASS_LEVELDB_ITERATOR]);
     } else {
-      return DefaultLevelDBFactory::CreateIterator(
+      return DefaultTransactionalLevelDBFactory::CreateIterator(
           std::move(iterator), db, std::move(txn), std::move(snapshot));
     }
   }
diff --git a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
index c03e4c3c..f2b74b41 100644
--- a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
+++ b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
@@ -15,7 +15,7 @@
 #include "content/browser/indexed_db/indexed_db_class_factory.h"
 #include "content/browser/indexed_db/indexed_db_database.h"
 #include "content/browser/indexed_db/indexed_db_task_helper.h"
-#include "content/browser/indexed_db/leveldb/leveldb_env.h"
+#include "content/browser/indexed_db/leveldb/transactional_leveldb_factory.h"
 #include "content/browser/indexed_db/scopes/scopes_lock_manager.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
 
@@ -24,6 +24,9 @@
 class IndexedDBConnection;
 class IndexedDBMetadataCoding;
 class LevelDBDirectTransaction;
+class LevelDBScope;
+class LevelDBScopes;
+class LevelDBSnapshot;
 class TransactionalLevelDBTransaction;
 class TransactionalLevelDBDatabase;
 
@@ -46,11 +49,13 @@
 
 class MockBrowserTestIndexedDBClassFactory
     : public IndexedDBClassFactory,
-      public indexed_db::DefaultLevelDBFactory {
+      public DefaultTransactionalLevelDBFactory {
  public:
   MockBrowserTestIndexedDBClassFactory();
   ~MockBrowserTestIndexedDBClassFactory() override;
 
+  TransactionalLevelDBFactory& transactional_leveldb_factory() override;
+
   std::pair<std::unique_ptr<IndexedDBDatabase>, leveldb::Status>
   CreateIndexedDBDatabase(
       const base::string16& name,
diff --git a/content/browser/indexed_db/scopes/leveldb_scope.cc b/content/browser/indexed_db/scopes/leveldb_scope.cc
index 3740726..2c8220a 100644
--- a/content/browser/indexed_db/scopes/leveldb_scope.cc
+++ b/content/browser/indexed_db/scopes/leveldb_scope.cc
@@ -11,7 +11,6 @@
 #include "base/debug/stack_trace.h"
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
-#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
 #include "content/browser/indexed_db/scopes/leveldb_scopes_coding.h"
 #include "third_party/leveldatabase/src/include/leveldb/comparator.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
diff --git a/content/browser/indexed_db/scopes/leveldb_scope_unittest.cc b/content/browser/indexed_db/scopes/leveldb_scope_unittest.cc
index 06616de..3b116f1 100644
--- a/content/browser/indexed_db/scopes/leveldb_scope_unittest.cc
+++ b/content/browser/indexed_db/scopes/leveldb_scope_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/bind_test_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "content/browser/indexed_db/leveldb/fake_leveldb_factory.h"
 #include "content/browser/indexed_db/scopes/disjoint_range_lock_manager.h"
 #include "content/browser/indexed_db/scopes/leveldb_scope.h"
 #include "content/browser/indexed_db/scopes/leveldb_scopes.h"
@@ -531,8 +532,7 @@
 
 TEST_F(LevelDBScopeTest, BrokenDBForInitialize) {
   leveldb::Status error = leveldb::Status::IOError("test");
-  leveldb_ =
-      indexed_db::FakeLevelDBFactory::GetBrokenLevelDB(error, base::FilePath());
+  leveldb_ = FakeLevelDBFactory::GetBrokenLevelDB(error, base::FilePath());
   DisjointRangeLockManager lock_manager(3);
 
   leveldb::Status failure_status = leveldb::Status::OK();
diff --git a/content/browser/indexed_db/scopes/leveldb_scopes.cc b/content/browser/indexed_db/scopes/leveldb_scopes.cc
index 3d28fb15..8d34fd3d 100644
--- a/content/browser/indexed_db/scopes/leveldb_scopes.cc
+++ b/content/browser/indexed_db/scopes/leveldb_scopes.cc
@@ -43,6 +43,7 @@
 leveldb::Status LevelDBScopes::Initialize() {
 #if DCHECK_IS_ON()
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(level_db_);
   DCHECK(!initialize_called_) << "Initialize() already called";
   initialize_called_ = true;
 #endif  // DCHECK_IS_ON()
diff --git a/content/browser/indexed_db/scopes/leveldb_scopes_tasks_unittest.cc b/content/browser/indexed_db/scopes/leveldb_scopes_tasks_unittest.cc
index 2bcdea65..a3eb9d1 100644
--- a/content/browser/indexed_db/scopes/leveldb_scopes_tasks_unittest.cc
+++ b/content/browser/indexed_db/scopes/leveldb_scopes_tasks_unittest.cc
@@ -277,11 +277,10 @@
   const int64_t kNumOpsBeforeFlakesStop =
       kNumOpsBeforeFlakesStart + kNumFlakeOps;
   for (int i = kNumOpsBeforeFlakesStart; i < kNumOpsBeforeFlakesStop + 1; ++i) {
-    indexed_db::FakeLevelDBFactory::FlakePoint flake = {
+    FakeLevelDBFactory::FlakePoint flake = {
         i, leveldb::Status::IOError(base::StringPrintf("io error %d", i)), ""};
 
-    SetUpFlakyDB(
-        std::queue<indexed_db::FakeLevelDBFactory::FlakePoint>({flake}));
+    SetUpFlakyDB(std::queue<FakeLevelDBFactory::FlakePoint>({flake}));
     CreateBasicScopeScenario(kScopeNumber, /*ignore_cleanup_tasks=*/false);
 
     {
@@ -310,11 +309,10 @@
   const int64_t kNumOpsBeforeFlakesStop =
       kNumOpsBeforeFlakesStart + kNumFlakeOps;
   for (int i = kNumOpsBeforeFlakesStart; i < kNumOpsBeforeFlakesStop + 1; ++i) {
-    indexed_db::FakeLevelDBFactory::FlakePoint flake = {
+    FakeLevelDBFactory::FlakePoint flake = {
         i, leveldb::Status::IOError(base::StringPrintf("io error %d", i)), ""};
 
-    SetUpFlakyDB(
-        std::queue<indexed_db::FakeLevelDBFactory::FlakePoint>({flake}));
+    SetUpFlakyDB(std::queue<FakeLevelDBFactory::FlakePoint>({flake}));
     // This tests that the cleanup tasks are executed when the mode is
     // kExecuteCleanupTasks.
     CreateBasicScopeScenario(kScopeNumber, /*ignore_cleanup_tasks=*/false);
diff --git a/content/browser/indexed_db/scopes/leveldb_scopes_test_utils.cc b/content/browser/indexed_db/scopes/leveldb_scopes_test_utils.cc
index 60a99bb5..5d59471 100644
--- a/content/browser/indexed_db/scopes/leveldb_scopes_test_utils.cc
+++ b/content/browser/indexed_db/scopes/leveldb_scopes_test_utils.cc
@@ -5,6 +5,7 @@
 #include "content/browser/indexed_db/scopes/leveldb_scopes_test_utils.h"
 
 #include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/stringprintf.h"
@@ -12,12 +13,31 @@
 #include "base/test/bind_test_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "content/browser/indexed_db/leveldb/leveldb_env.h"
+#include "content/browser/indexed_db/leveldb/leveldb_factory.h"
 #include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/leveldb_chrome.h"
 #include "third_party/leveldatabase/src/include/leveldb/comparator.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "third_party/leveldatabase/src/include/leveldb/filter_policy.h"
 
 namespace content {
+namespace {
+constexpr size_t kWriteBufferSize = 4 * 1024 * 1024;
+
+leveldb_env::Options GetLevelDBOptions() {
+  static base::NoDestructor<leveldb_env::ChromiumEnv> gTestEnv;
+  static const leveldb::FilterPolicy* kFilterPolicy =
+      leveldb::NewBloomFilterPolicy(10);
+  leveldb_env::Options options;
+  options.comparator = leveldb::BytewiseComparator();
+  options.paranoid_checks = true;
+  options.filter_policy = kFilterPolicy;
+  options.compression = leveldb::kSnappyCompression;
+  options.env = gTestEnv.get();
+  return options;
+}
+
+}  // namespace
 
 constexpr const size_t LevelDBScopesTestBase::kWriteBatchSizeForTesting;
 
@@ -27,11 +47,22 @@
 void LevelDBScopesTestBase::SetUp() {
   large_string_.assign(kWriteBatchSizeForTesting + 1, 'e');
   if (!leveldb_factory_)
-    leveldb_factory_ = indexed_db::LevelDBFactory::Get();
+    leveldb_factory_ = std::make_unique<FakeLevelDBFactory>(GetLevelDBOptions(),
+                                                            "scopes-test-db");
 }
 
 void LevelDBScopesTestBase::TearDown() {
   if (leveldb_) {
+    CloseScopesAndDestroyLevelDBState();
+    if (temp_directory_.IsValid()) {
+      leveldb_factory_->DestroyLevelDB(temp_directory_.GetPath());
+      ASSERT_TRUE(temp_directory_.Delete());
+    }
+  }
+}
+
+void LevelDBScopesTestBase::CloseScopesAndDestroyLevelDBState() {
+  if (leveldb_) {
     base::RunLoop loop;
     if (leveldb_->RequestDestruction(loop.QuitClosure(),
                                      base::SequencedTaskRunnerHandle::Get())) {
@@ -39,10 +70,6 @@
       loop.Run();
     }
     leveldb_.reset();
-    if (temp_directory_.IsValid()) {
-      leveldb_factory_->DestroyLevelDB(temp_directory_.GetPath());
-      ASSERT_TRUE(temp_directory_.Delete());
-    }
   }
 }
 
@@ -50,11 +77,7 @@
   if (leveldb_)
     TearDown();
   ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
-  leveldb::Status status;
-  std::tie(leveldb_, status, std::ignore) = leveldb_factory_->OpenLevelDBState(
-      temp_directory_.GetPath(), leveldb::BytewiseComparator(), true);
-  ASSERT_TRUE(status.ok()) << status.ToString();
-  ASSERT_TRUE(leveldb_);
+  CreateAndSaveLevelDBState();
 }
 
 void LevelDBScopesTestBase::SetUpBreakableDB(
@@ -63,60 +86,44 @@
     TearDown();
   ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
 
-  leveldb_env::Options options = indexed_db::GetLevelDBOptions(
-      LevelDBEnv::Get(), leveldb::BytewiseComparator(),
-      /*create_if_missing=*/true,
-      leveldb_env::WriteBufferSize(
-          base::SysInfo::AmountOfTotalDiskSpace(temp_directory_.GetPath())),
-      /*paranoid_checks=*/true);
-
   leveldb::Status status;
-  indexed_db::FakeLevelDBFactory factory;
   std::unique_ptr<leveldb::DB> temp_real_db;
   std::tie(temp_real_db, status) =
-      factory.OpenDB(options, temp_directory_.GetPath().AsUTF8Unsafe());
+      leveldb_factory_->OpenDB(temp_directory_.GetPath().AsUTF8Unsafe(),
+                               /*create_if_missing=*/true, kWriteBufferSize);
   ASSERT_TRUE(status.ok());
   ASSERT_TRUE(temp_real_db);
 
-  std::unique_ptr<leveldb::DB> db;
-  std::tie(db, *callback) = indexed_db::FakeLevelDBFactory::CreateBreakableDB(
-      std::move(temp_real_db));
-  ASSERT_TRUE(db);
-  leveldb_ = LevelDBState::CreateForDiskDB(
-      leveldb::BytewiseComparator(), std::move(db), temp_directory_.GetPath());
+  std::unique_ptr<leveldb::DB> breakable_db;
+  std::tie(breakable_db, *callback) =
+      FakeLevelDBFactory::CreateBreakableDB(std::move(temp_real_db));
+  ASSERT_TRUE(breakable_db);
+
+  leveldb_factory_->EnqueueNextOpenDBResult(std::move(breakable_db),
+                                            leveldb::Status::OK());
+  CreateAndSaveLevelDBState();
 }
 
 void LevelDBScopesTestBase::SetUpFlakyDB(
-    std::queue<indexed_db::FakeLevelDBFactory::FlakePoint> flake_points) {
+    std::queue<FakeLevelDBFactory::FlakePoint> flake_points) {
   if (leveldb_)
     TearDown();
   ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
   leveldb::Status status;
 
-  indexed_db::FakeLevelDBFactory fake_factory;
-
-  leveldb_env::Options options = indexed_db::GetLevelDBOptions(
-      LevelDBEnv::Get(), leveldb::BytewiseComparator(),
-      /*create_if_missing=*/true,
-      leveldb_env::WriteBufferSize(
-          base::SysInfo::AmountOfTotalDiskSpace(temp_directory_.GetPath())),
-      /*paranoid_checks=*/true);
   std::unique_ptr<leveldb::DB> temp_db;
   std::tie(temp_db, status) =
-      fake_factory.OpenDB(options, temp_directory_.GetPath().AsUTF8Unsafe());
+      leveldb_factory_->OpenDB(temp_directory_.GetPath().AsUTF8Unsafe(),
+                               /*create_if_missing=*/true, kWriteBufferSize);
   ASSERT_TRUE(status.ok());
   ASSERT_TRUE(temp_db);
 
-  std::unique_ptr<leveldb::DB> flaky_db =
-      indexed_db::FakeLevelDBFactory::CreateFlakyDB(std::move(temp_db),
-                                                    std::move(flake_points));
+  std::unique_ptr<leveldb::DB> flaky_db = FakeLevelDBFactory::CreateFlakyDB(
+      std::move(temp_db), std::move(flake_points));
 
-  fake_factory.EnqueueNextOpenDBResult(std::move(flaky_db),
-                                       leveldb::Status::OK());
-  std::tie(leveldb_, status, std::ignore) = fake_factory.OpenLevelDBState(
-      temp_directory_.GetPath(), leveldb::BytewiseComparator(), true);
-  ASSERT_TRUE(status.ok());
-  ASSERT_TRUE(leveldb_);
+  leveldb_factory_->EnqueueNextOpenDBResult(std::move(flaky_db),
+                                            leveldb::Status::OK());
+  CreateAndSaveLevelDBState();
 }
 
 void LevelDBScopesTestBase::WriteScopesMetadata(int64_t scope_number,
@@ -238,4 +245,16 @@
           ScopesLockManager::LockType::kExclusive};
 }
 
+const base::FilePath& LevelDBScopesTestBase::DatabaseDirFilePath() {
+  return temp_directory_.GetPath();
+}
+
+void LevelDBScopesTestBase::CreateAndSaveLevelDBState() {
+  leveldb::Status status;
+  std::tie(leveldb_, status, std::ignore) = leveldb_factory_->OpenLevelDBState(
+      temp_directory_.GetPath(), true, kWriteBufferSize);
+  ASSERT_TRUE(status.ok()) << status.ToString();
+  ASSERT_TRUE(leveldb_);
+}
+
 }  // namespace content
diff --git a/content/browser/indexed_db/scopes/leveldb_scopes_test_utils.h b/content/browser/indexed_db/scopes/leveldb_scopes_test_utils.h
index e63f3bd9..3c67ec6f 100644
--- a/content/browser/indexed_db/scopes/leveldb_scopes_test_utils.h
+++ b/content/browser/indexed_db/scopes/leveldb_scopes_test_utils.h
@@ -20,10 +20,6 @@
 
 namespace content {
 
-namespace indexed_db {
-class LevelDBFactory;
-}
-
 class LevelDBScopesTestBase : public testing::Test {
  public:
   static constexpr const size_t kWriteBatchSizeForTesting = 1024;
@@ -37,6 +33,10 @@
   // deleted off disk.
   void TearDown() override;
 
+  // Ensures that |leveldb_| is destroyed correctly, but doesn't delete the
+  // database on disk.
+  void CloseScopesAndDestroyLevelDBState();
+
   // Initializes |leveldb_| to be a read database backed on disk.
   void SetUpRealDatabase();
 
@@ -45,8 +45,7 @@
   void SetUpBreakableDB(base::OnceCallback<void(leveldb::Status)>* callback);
 
   // Initializes |leveldb_| to be a flaky database that will
-  void SetUpFlakyDB(
-      std::queue<indexed_db::FakeLevelDBFactory::FlakePoint> flake_points);
+  void SetUpFlakyDB(std::queue<FakeLevelDBFactory::FlakePoint> flake_points);
 
   // Writes |metadata_buffer_| to disk.
   void WriteScopesMetadata(int64_t scope_number, bool ignore_cleanup_tasks);
@@ -93,6 +92,11 @@
   ScopesLockManager::ScopeLockRequest CreateSharedLock(int i);
   ScopesLockManager::ScopeLockRequest CreateExclusiveLock(int i);
 
+  const base::FilePath& DatabaseDirFilePath();
+
+ private:
+  void CreateAndSaveLevelDBState();
+
  protected:
   base::ScopedAllowBaseSyncPrimitivesForTesting allow_;
   base::test::TaskEnvironment task_env_;
@@ -103,7 +107,7 @@
   const std::vector<uint8_t> metadata_prefix_ = {'a'};
   const std::vector<uint8_t> db_prefix_ = {'b'};
 
-  indexed_db::LevelDBFactory* leveldb_factory_ = nullptr;
+  std::unique_ptr<FakeLevelDBFactory> leveldb_factory_;
   scoped_refptr<LevelDBState> leveldb_;
   std::string large_string_;
   LevelDBScopesUndoTask undo_task_buffer_;
diff --git a/content/browser/loader/navigation_loader_interceptor.h b/content/browser/loader/navigation_loader_interceptor.h
index ad204af6..41e7e09 100644
--- a/content/browser/loader/navigation_loader_interceptor.h
+++ b/content/browser/loader/navigation_loader_interceptor.h
@@ -10,11 +10,11 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/optional.h"
-#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 
@@ -37,7 +37,7 @@
   virtual ~NavigationLoaderInterceptor() = default;
 
   using LoaderCallback =
-      base::OnceCallback<void(SingleRequestURLLoaderFactory::RequestHandler)>;
+      base::OnceCallback<void(scoped_refptr<network::SharedURLLoaderFactory>)>;
   using FallbackCallback =
       base::OnceCallback<void(bool /* reset_subresource_loader_params */)>;
 
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 1474b3e0..9561b791f 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -33,6 +33,7 @@
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/browser/loader/navigation_url_loader_delegate.h"
 #include "content/browser/loader/prefetch_url_loader_service.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/navigation_subresource_loader_params.h"
 #include "content/browser/service_worker/service_worker_navigation_handle.h"
 #include "content/browser/service_worker/service_worker_navigation_handle_core.h"
@@ -113,7 +114,19 @@
       LoaderCallback callback,
       FallbackCallback fallback_callback) override {
     browser_interceptor_->MaybeCreateLoader(
-        tentative_resource_request, browser_context, std::move(callback));
+        tentative_resource_request, browser_context,
+        base::BindOnce(
+            [](LoaderCallback callback,
+               URLLoaderRequestInterceptor::RequestHandler handler) {
+              if (handler) {
+                std::move(callback).Run(
+                    base::MakeRefCounted<SingleRequestURLLoaderFactory>(
+                        std::move(handler)));
+              } else {
+                std::move(callback).Run({});
+              }
+            },
+            std::move(callback)));
   }
 
  private:
@@ -525,7 +538,7 @@
     received_response_ = false;
     head_ = network::ResourceResponseHead();
     MaybeStartLoader(nullptr /* interceptor */,
-                     {} /* single_request_handler */);
+                     {} /* single_request_factory */);
   }
 
   // |interceptor| is non-null if this is called by one of the interceptors
@@ -534,11 +547,11 @@
   // non-null if the interceptor wants to handle the request.
   void MaybeStartLoader(
       NavigationLoaderInterceptor* interceptor,
-      SingleRequestURLLoaderFactory::RequestHandler single_request_handler) {
+      scoped_refptr<network::SharedURLLoaderFactory> single_request_factory) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(started_);
 
-    if (single_request_handler) {
+    if (single_request_factory) {
       // |interceptor| wants to handle the request with
       // |single_request_handler|.
       DCHECK(interceptor);
@@ -553,11 +566,10 @@
 
       default_loader_used_ = false;
       url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart(
-          base::MakeRefCounted<SingleRequestURLLoaderFactory>(
-              std::move(single_request_handler)),
-          std::move(throttles), frame_tree_node_id_,
-          global_request_id_.request_id, network::mojom::kURLLoadOptionNone,
-          resource_request_.get(), this, kNavigationUrlLoaderTrafficAnnotation,
+          std::move(single_request_factory), std::move(throttles),
+          frame_tree_node_id_, global_request_id_.request_id,
+          network::mojom::kURLLoadOptionNone, resource_request_.get(), this,
+          kNavigationUrlLoaderTrafficAnnotation,
           base::ThreadTaskRunnerHandle::Get());
 
       subresource_loader_params_ =
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc
index f889eb0..53425a5 100644
--- a/content/browser/loader/navigation_url_loader_impl_unittest.cc
+++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -17,6 +17,7 @@
 #include "content/browser/frame_host/navigation_request_info.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/browser/loader/navigation_url_loader.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
 #include "content/common/navigation_params.h"
 #include "content/common/navigation_params.mojom.h"
@@ -75,8 +76,9 @@
                          BrowserContext* browser_context,
                          LoaderCallback callback,
                          FallbackCallback fallback_callback) override {
-    std::move(callback).Run(base::BindOnce(
-        &TestNavigationLoaderInterceptor::StartLoader, base::Unretained(this)));
+    std::move(callback).Run(base::MakeRefCounted<SingleRequestURLLoaderFactory>(
+        base::BindOnce(&TestNavigationLoaderInterceptor::StartLoader,
+                       base::Unretained(this))));
   }
 
   void StartLoader(const network::ResourceRequest& resource_request,
diff --git a/content/browser/loader/single_request_url_loader_factory.cc b/content/browser/loader/single_request_url_loader_factory.cc
index 6e955913..054173c 100644
--- a/content/browser/loader/single_request_url_loader_factory.cc
+++ b/content/browser/loader/single_request_url_loader_factory.cc
@@ -94,7 +94,9 @@
 
 void SingleRequestURLLoaderFactory::Clone(
     mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
-  NOTREACHED();
+  // Pass |this| as the recevier context to make sure this object stays alive
+  // while it still has receivers.
+  receivers_.Add(this, std::move(receiver), this);
 }
 
 std::unique_ptr<network::SharedURLLoaderFactoryInfo>
diff --git a/content/browser/loader/single_request_url_loader_factory.h b/content/browser/loader/single_request_url_loader_factory.h
index 996d0d0..8ce8c902 100644
--- a/content/browser/loader/single_request_url_loader_factory.h
+++ b/content/browser/loader/single_request_url_loader_factory.h
@@ -8,7 +8,9 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #include "base/memory/ref_counted.h"
+#include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 
 namespace content {
@@ -16,7 +18,8 @@
 // An implementation of SharedURLLoaderFactory which handles only a single
 // request. It's an error to call CreateLoaderAndStart() more than a total of
 // one time across this object or any of its clones.
-class SingleRequestURLLoaderFactory : public network::SharedURLLoaderFactory {
+class CONTENT_EXPORT SingleRequestURLLoaderFactory
+    : public network::SharedURLLoaderFactory {
  public:
   using RequestHandler =
       base::OnceCallback<void(const network::ResourceRequest& resource_request,
@@ -47,6 +50,10 @@
 
   scoped_refptr<HandlerState> state_;
 
+  mojo::ReceiverSet<network::mojom::URLLoaderFactory,
+                    scoped_refptr<SingleRequestURLLoaderFactory>>
+      receivers_;
+
   DISALLOW_COPY_AND_ASSIGN(SingleRequestURLLoaderFactory);
 };
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 1154d5e4..d8c2a92 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3125,6 +3125,7 @@
     switches::kNoZygote,
     switches::kOverridePluginPowerSaverForTesting,
     switches::kPassiveListenersDefault,
+    switches::kPerfettoDisableInterning,
     switches::kPpapiInProcess,
     switches::kProfilingAtStart,
     switches::kProfilingFile,
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index b53df05..de92181 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -113,7 +113,7 @@
     const network::ResourceRequest& tentative_resource_request,
     BrowserContext* browser_context,
     ResourceContext* resource_context,
-    NavigationLoaderInterceptor::LoaderCallback callback,
+    ServiceWorkerLoaderCallback callback,
     NavigationLoaderInterceptor::FallbackCallback fallback_callback) {
   // InitializeProvider() will update the host. This is important to do before
   // falling back to network below, so service worker APIs still work even if
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.h b/content/browser/service_worker/service_worker_controllee_request_handler.h
index 12ee11f..762fac0 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.h
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.h
@@ -15,6 +15,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/service_worker/service_worker_navigation_loader.h"
 #include "content/common/content_export.h"
 #include "content/public/common/resource_type.h"
@@ -50,11 +51,13 @@
   // This could get called multiple times during the lifetime in redirect
   // cases. (In fallback-to-network cases we basically forward the request
   // to the request to the next request handler)
+  using ServiceWorkerLoaderCallback =
+      base::OnceCallback<void(SingleRequestURLLoaderFactory::RequestHandler)>;
   void MaybeCreateLoader(
       const network::ResourceRequest& tentative_request,
       BrowserContext* browser_context,
       ResourceContext* resource_context,
-      NavigationLoaderInterceptor::LoaderCallback callback,
+      ServiceWorkerLoaderCallback callback,
       NavigationLoaderInterceptor::FallbackCallback fallback_callback);
   // Returns params with the ControllerServiceWorkerInfoPtr if we have found
   // a matching controller service worker for the |request| that is given
@@ -114,7 +117,7 @@
   bool force_update_started_;
   base::TimeTicks registration_lookup_start_time_;
 
-  NavigationLoaderInterceptor::LoaderCallback loader_callback_;
+  ServiceWorkerLoaderCallback loader_callback_;
   NavigationLoaderInterceptor::FallbackCallback fallback_callback_;
 
   base::WeakPtrFactory<ServiceWorkerControlleeRequestHandler> weak_factory_{
diff --git a/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc b/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc
index 75d4505..a3c6e60d 100644
--- a/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader_interceptor.cc
@@ -218,7 +218,7 @@
     // after starting it.
     original_callback = std::move(loader_callback);
     loader_callback =
-        base::BindOnce([](SingleRequestURLLoaderFactory::RequestHandler) {});
+        base::BindOnce([](scoped_refptr<network::SharedURLLoaderFactory>) {});
     initialize_provider_only = true;
   }
 
@@ -269,9 +269,9 @@
   // |handler_on_core_thread| expects to run on the core thread. Give our own
   // wrapper to the loader callback.
   std::move(loader_callback)
-      .Run(base::BindOnce(
+      .Run(base::MakeRefCounted<SingleRequestURLLoaderFactory>(base::BindOnce(
           &ServiceWorkerNavigationLoaderInterceptor::RequestHandlerWrapper,
-          GetWeakPtr(), std::move(handler_on_core_thread)));
+          GetWeakPtr(), std::move(handler_on_core_thread))));
 }
 
 void ServiceWorkerNavigationLoaderInterceptor::FallbackCallbackWrapper(
diff --git a/content/browser/service_worker/service_worker_navigation_loader_interceptor.h b/content/browser/service_worker/service_worker_navigation_loader_interceptor.h
index c6d5de9..7173fced 100644
--- a/content/browser/service_worker/service_worker_navigation_loader_interceptor.h
+++ b/content/browser/service_worker/service_worker_navigation_loader_interceptor.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/navigation_subresource_loader_params.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index ef7f537..09069a7 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -693,6 +693,20 @@
   kSurfaceLayer,
 };
 
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+bool IsScreenTooSmallForPopup(const ScreenInfo& screen_info) {
+  // Small display size will cause popup positions to be adjusted,
+  // causing test failures.
+  //
+  // The size adjustment happens in adjustWindowRect()
+  // (third_party/blink/renderer/core/html/forms/resources/pickerCommon.js
+  // lines 132-133).
+  static constexpr gfx::Size kMinimumScreenSize(300, 300);
+  return screen_info.rect.width() < kMinimumScreenSize.width() ||
+         screen_info.rect.height() < kMinimumScreenSize.height();
+}
+#endif
+
 }  // namespace
 
 class SitePerProcessHitTestBrowserTest
@@ -5620,12 +5634,13 @@
   SetWebEventPositions(&click_event, gfx::Point(1, 1), rwhv_root);
   rwhv_child->ProcessMouseEvent(click_event, ui::LatencyInfo());
 
+  ScreenInfo screen_info;
+  shell()->web_contents()->GetRenderWidgetHostView()->GetScreenInfo(
+      &screen_info);
+
   filter->Wait();
   gfx::Rect popup_rect = filter->last_initial_rect();
   if (IsUseZoomForDSFEnabled()) {
-    ScreenInfo screen_info;
-    shell()->web_contents()->GetRenderWidgetHostView()->GetScreenInfo(
-        &screen_info);
     popup_rect = gfx::ScaleToRoundedRect(popup_rect,
                                          1 / screen_info.device_scale_factor);
   }
@@ -5635,8 +5650,10 @@
   EXPECT_EQ(popup_rect.x(), 9);
   EXPECT_EQ(popup_rect.y(), 9);
 #else
-  EXPECT_EQ(popup_rect.x() - rwhv_root->GetViewBounds().x(), 354);
-  EXPECT_EQ(popup_rect.y() - rwhv_root->GetViewBounds().y(), 94);
+  if (!IsScreenTooSmallForPopup(screen_info)) {
+    EXPECT_EQ(popup_rect.x() - rwhv_root->GetViewBounds().x(), 354);
+    EXPECT_EQ(popup_rect.y() - rwhv_root->GetViewBounds().y(), 94);
+  }
 #endif
 
 #if defined(OS_LINUX)
@@ -5756,12 +5773,17 @@
 
   gfx::Rect popup_rect = filter->last_initial_rect();
 
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_ANDROID)
   EXPECT_EQ(popup_rect.x(), 9);
   EXPECT_EQ(popup_rect.y(), 9);
 #else
-  EXPECT_EQ(popup_rect.x() - rwhv_root->GetViewBounds().x(), 354);
-  EXPECT_EQ(popup_rect.y() - rwhv_root->GetViewBounds().y(), 154);
+  ScreenInfo screen_info;
+  shell()->web_contents()->GetRenderWidgetHostView()->GetScreenInfo(
+      &screen_info);
+  if (!IsScreenTooSmallForPopup(screen_info)) {
+    EXPECT_EQ(popup_rect.x() - rwhv_root->GetViewBounds().x(), 354);
+    EXPECT_EQ(popup_rect.y() - rwhv_root->GetViewBounds().y(), 154);
+  }
 #endif
 
   // Save the screen rect for b_node. Since it updates asynchronously from
@@ -5803,12 +5825,14 @@
 
   popup_rect = filter->last_initial_rect();
 
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_ANDROID)
   EXPECT_EQ(popup_rect.x(), 9);
   EXPECT_EQ(popup_rect.y(), 9);
 #else
-  EXPECT_EQ(popup_rect.x() - rwhv_root->GetViewBounds().x(), 203);
-  EXPECT_EQ(popup_rect.y() - rwhv_root->GetViewBounds().y(), 248);
+  if (!IsScreenTooSmallForPopup(screen_info)) {
+    EXPECT_EQ(popup_rect.x() - rwhv_root->GetViewBounds().x(), 203);
+    EXPECT_EQ(popup_rect.y() - rwhv_root->GetViewBounds().y(), 248);
+  }
 #endif
 }
 
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc
index 289f536..0390f26 100644
--- a/content/browser/storage_partition_impl_unittest.cc
+++ b/content/browser/storage_partition_impl_unittest.cc
@@ -144,7 +144,7 @@
   bool ContainsCookie() {
     get_cookie_success_ = false;
     storage_partition_->GetCookieManagerForBrowserProcess()->GetCookieList(
-        kOrigin1.GetURL(), net::CookieOptions(),
+        kOrigin1.GetURL(), net::CookieOptions::MakeAllInclusive(),
         base::BindOnce(&RemoveCookieTester::GetCookieListCallback,
                        base::Unretained(this)));
     await_completion_.BlockUntilNotified();
@@ -157,7 +157,7 @@
         kOrigin1.GetURL(), "A=1", base::Time::Now(),
         base::nullopt /* server_time */, &status));
     storage_partition_->GetCookieManagerForBrowserProcess()->SetCanonicalCookie(
-        *cc, kOrigin1.scheme(), net::CookieOptions(),
+        *cc, kOrigin1.scheme(), net::CookieOptions::MakeAllInclusive(),
         base::BindOnce(&RemoveCookieTester::SetCookieCallback,
                        base::Unretained(this)));
     await_completion_.BlockUntilNotified();
diff --git a/content/browser/web_package/bundled_exchanges_handle.cc b/content/browser/web_package/bundled_exchanges_handle.cc
index b810d8e7..fa4ab72 100644
--- a/content/browser/web_package/bundled_exchanges_handle.cc
+++ b/content/browser/web_package/bundled_exchanges_handle.cc
@@ -10,6 +10,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/web_package/bundled_exchanges_handle_tracker.h"
 #include "content/browser/web_package/bundled_exchanges_navigation_info.h"
 #include "content/browser/web_package/bundled_exchanges_reader.h"
@@ -168,8 +169,9 @@
       std::move(callback).Run({});
       return;
     }
-    std::move(callback).Run(base::BindOnce(&InterceptorForFile::StartResponse,
-                                           weak_factory_.GetWeakPtr()));
+    std::move(callback).Run(
+        base::MakeRefCounted<SingleRequestURLLoaderFactory>(base::BindOnce(
+            &InterceptorForFile::StartResponse, weak_factory_.GetWeakPtr())));
   }
 
   bool MaybeCreateLoaderForResponse(
@@ -293,9 +295,9 @@
                          LoaderCallback callback,
                          FallbackCallback fallback_callback) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    std::move(callback).Run(
+    std::move(callback).Run(base::MakeRefCounted<SingleRequestURLLoaderFactory>(
         base::BindOnce(&InterceptorForTrustableFile::CreateURLLoader,
-                       weak_factory_.GetWeakPtr()));
+                       weak_factory_.GetWeakPtr())));
   }
 
   void CreateURLLoader(const network::ResourceRequest& resource_request,
@@ -413,9 +415,10 @@
                          FallbackCallback fallback_callback) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     DCHECK(url_loader_factory_->reader()->HasEntry(resource_request.url));
-    std::move(callback).Run(base::BindOnce(
-        &InterceptorForTrackedNavigationFromTrustableFile::CreateURLLoader,
-        weak_factory_.GetWeakPtr()));
+    std::move(callback).Run(
+        base::MakeRefCounted<SingleRequestURLLoaderFactory>(base::BindOnce(
+            &InterceptorForTrackedNavigationFromTrustableFile::CreateURLLoader,
+            weak_factory_.GetWeakPtr())));
   }
 
   void CreateURLLoader(const network::ResourceRequest& resource_request,
@@ -475,9 +478,10 @@
                          LoaderCallback callback,
                          FallbackCallback fallback_callback) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    std::move(callback).Run(base::BindOnce(
-        &InterceptorForTrackedNavigationFromFile::CreateURLLoader,
-        weak_factory_.GetWeakPtr()));
+    std::move(callback).Run(
+        base::MakeRefCounted<SingleRequestURLLoaderFactory>(base::BindOnce(
+            &InterceptorForTrackedNavigationFromFile::CreateURLLoader,
+            weak_factory_.GetWeakPtr())));
   }
 
   bool ShouldBypassRedirectChecks() override { return true; }
@@ -564,9 +568,9 @@
                          LoaderCallback callback,
                          FallbackCallback fallback_callback) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    std::move(callback).Run(
+    std::move(callback).Run(base::MakeRefCounted<SingleRequestURLLoaderFactory>(
         base::BindOnce(&InterceptorForNavigationInfo::CreateURLLoader,
-                       weak_factory_.GetWeakPtr()));
+                       weak_factory_.GetWeakPtr())));
   }
 
   void CreateURLLoader(const network::ResourceRequest& resource_request,
diff --git a/content/browser/web_package/prefetched_signed_exchange_cache.cc b/content/browser/web_package/prefetched_signed_exchange_cache.cc
index 1bf7de4..1698f92d 100644
--- a/content/browser/web_package/prefetched_signed_exchange_cache.cc
+++ b/content/browser/web_package/prefetched_signed_exchange_cache.cc
@@ -12,6 +12,7 @@
 #include "components/link_header_util/link_header_util.h"
 #include "content/browser/loader/cross_origin_read_blocking_checker.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/navigation_subresource_loader_params.h"
 #include "content/browser/web_package/signed_exchange_utils.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -402,17 +403,19 @@
     if (state_ == State::kInitial &&
         tentative_resource_request.url == exchange_->outer_url()) {
       state_ = State::kOuterRequestRequested;
-      std::move(callback).Run(base::BindOnce(
-          &PrefetchedNavigationLoaderInterceptor::StartRedirectResponse,
-          weak_factory_.GetWeakPtr()));
+      std::move(callback).Run(
+          base::MakeRefCounted<SingleRequestURLLoaderFactory>(base::BindOnce(
+              &PrefetchedNavigationLoaderInterceptor::StartRedirectResponse,
+              weak_factory_.GetWeakPtr())));
       return;
     }
     if (tentative_resource_request.url == exchange_->inner_url()) {
       DCHECK_EQ(State::kOuterRequestRequested, state_);
       state_ = State::kInnerResponseRequested;
-      std::move(callback).Run(base::BindOnce(
-          &PrefetchedNavigationLoaderInterceptor::StartInnerResponse,
-          weak_factory_.GetWeakPtr()));
+      std::move(callback).Run(
+          base::MakeRefCounted<SingleRequestURLLoaderFactory>(base::BindOnce(
+              &PrefetchedNavigationLoaderInterceptor::StartInnerResponse,
+              weak_factory_.GetWeakPtr())));
       return;
     }
     NOTREACHED();
diff --git a/content/browser/web_package/signed_exchange_request_handler.cc b/content/browser/web_package/signed_exchange_request_handler.cc
index 567b77dd..43143a8 100644
--- a/content/browser/web_package/signed_exchange_request_handler.cc
+++ b/content/browser/web_package/signed_exchange_request_handler.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/feature_list.h"
+#include "content/browser/loader/single_request_url_loader_factory.h"
 #include "content/browser/web_package/signed_exchange_devtools_proxy.h"
 #include "content/browser/web_package/signed_exchange_loader.h"
 #include "content/browser/web_package/signed_exchange_prefetch_metric_recorder.h"
@@ -69,9 +70,9 @@
 
   DCHECK(tentative_resource_request.url.EqualsIgnoringRef(
       *signed_exchange_loader_->inner_request_url()));
-  std::move(callback).Run(
+  std::move(callback).Run(base::MakeRefCounted<SingleRequestURLLoaderFactory>(
       base::BindOnce(&SignedExchangeRequestHandler::StartResponse,
-                     weak_factory_.GetWeakPtr()));
+                     weak_factory_.GetWeakPtr())));
 }
 
 bool SignedExchangeRequestHandler::MaybeCreateLoaderForResponse(
diff --git a/content/browser/worker_host/worker_script_loader.cc b/content/browser/worker_host/worker_script_loader.cc
index db2395f..0ecc2faf 100644
--- a/content/browser/worker_host/worker_script_loader.cc
+++ b/content/browser/worker_host/worker_script_loader.cc
@@ -112,7 +112,7 @@
 
 void WorkerScriptLoader::MaybeStartLoader(
     NavigationLoaderInterceptor* interceptor,
-    SingleRequestURLLoaderFactory::RequestHandler single_request_handler) {
+    scoped_refptr<network::SharedURLLoaderFactory> single_request_factory) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!completed_);
   DCHECK(interceptor);
@@ -123,12 +123,11 @@
   subresource_loader_params_ =
       interceptor->MaybeCreateSubresourceLoaderParams();
 
-  if (single_request_handler) {
+  if (single_request_factory) {
     // The interceptor elected to handle the request. Use it.
     network::mojom::URLLoaderClientPtr client;
     url_loader_client_binding_.Bind(mojo::MakeRequest(&client));
-    url_loader_factory_ = base::MakeRefCounted<SingleRequestURLLoaderFactory>(
-        std::move(single_request_handler));
+    url_loader_factory_ = std::move(single_request_factory);
     url_loader_factory_->CreateLoaderAndStart(
         mojo::MakeRequest(&url_loader_), routing_id_, request_id_, options_,
         resource_request_, std::move(client), traffic_annotation_);
diff --git a/content/browser/worker_host/worker_script_loader.h b/content/browser/worker_host/worker_script_loader.h
index bce694e..143a409 100644
--- a/content/browser/worker_host/worker_script_loader.h
+++ b/content/browser/worker_host/worker_script_loader.h
@@ -122,7 +122,7 @@
   void Start();
   void MaybeStartLoader(
       NavigationLoaderInterceptor* interceptor,
-      SingleRequestURLLoaderFactory::RequestHandler single_request_handler);
+      scoped_refptr<network::SharedURLLoaderFactory> single_request_factory);
   void LoadFromNetwork(bool reset_subresource_loader_params);
   void CommitCompleted(const network::URLLoaderCompletionStatus& status);
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index 7405e306..b9c7025 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -904,7 +904,7 @@
 
         // Map can be null after WebView gets gc'ed on its way to destruction.
         if (userDataHost == null) {
-            Log.e(TAG, "UserDataHost can't be found");
+            Log.d(TAG, "UserDataHost can't be found");
             return null;
         }
 
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 696db8d4..ee168f4 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -747,12 +747,6 @@
   return false;
 }
 
-#if defined(OS_ANDROID)
-void ContentBrowserClient::WillCreateURLLoaderFactoryForAppCacheSubresource(
-    int render_process_id,
-    mojo::PendingRemote<network::mojom::URLLoaderFactory>* pending_factory) {}
-#endif
-
 bool ContentBrowserClient::WillInterceptWebSocket(RenderFrameHost*) {
   return false;
 }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 594eea04..d2d5de2e 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1300,19 +1300,6 @@
           header_client,
       bool* bypass_redirect_checks);
 
-#if defined(OS_ANDROID)
-  // Similar to WillCreateURLLoaderFactory but for appcache subresources.
-  // WillCreateURLLoaderFactory couldn't be used because when the appcache
-  // URLLoaderFactory is sent on the UI thread, the
-  // PendingReceiver<URLLoaderFactory> is already consumed. And on the IO thread
-  // when it's created but before it's consumed, we don't have the process ID.
-  // This is behind an OS_ANDROID ifdef because we don't need this on desktop.
-  // Only WebView should implement this, see https://crbug.com/977873.
-  virtual void WillCreateURLLoaderFactoryForAppCacheSubresource(
-      int render_process_id,
-      mojo::PendingRemote<network::mojom::URLLoaderFactory>* pending_factory);
-#endif
-
   // Returns true when the embedder wants to intercept a websocket connection.
   virtual bool WillInterceptWebSocket(RenderFrameHost* frame);
 
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index 96a5794..75fd208 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -42,7 +42,6 @@
 #include "content/renderer/loader/sync_load_response.h"
 #include "content/renderer/loader/web_url_request_util.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "net/base/data_url.h"
 #include "net/base/filename_util.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/ip_endpoint.h"
@@ -57,7 +56,6 @@
 #include "net/ssl/ssl_cipher_suite_names.h"
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "net/ssl/ssl_info.h"
-#include "net/url_request/url_request_data_job.h"
 #include "services/network/loader_util.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/resource_response.h"
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 30911a65..6c454813 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -48,8 +48,6 @@
 #include "device/bluetooth/public/mojom/test/fake_bluetooth.mojom.h"
 #include "media/mojo/buildflags.h"
 #include "net/ssl/client_cert_identity.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 #include "services/service_manager/public/cpp/manifest.h"
 #include "services/service_manager/public/cpp/manifest_builder.h"
@@ -256,21 +254,17 @@
 bool ShellContentBrowserClient::IsHandledURL(const GURL& url) {
   if (!url.is_valid())
     return false;
-  // Keep in sync with ProtocolHandlers added by
-  // ShellURLRequestContextGetter::GetURLRequestContext().
   static const char* const kProtocolList[] = {
-      url::kBlobScheme,
-      url::kFileSystemScheme,
-      kChromeUIScheme,
-      kChromeDevToolsScheme,
-      url::kDataScheme,
+      url::kHttpScheme, url::kHttpsScheme,     url::kWsScheme,
+      url::kWssScheme,  url::kBlobScheme,      url::kFileSystemScheme,
+      kChromeUIScheme,  kChromeDevToolsScheme, url::kDataScheme,
       url::kFileScheme,
   };
-  for (size_t i = 0; i < base::size(kProtocolList); ++i) {
-    if (url.scheme() == kProtocolList[i])
+  for (const char* supported_protocol : kProtocolList) {
+    if (url.scheme_piece() == supported_protocol)
       return true;
   }
-  return net::URLRequest::IsHandledProtocol(url.scheme());
+  return false;
 }
 
 void ShellContentBrowserClient::BindInterfaceRequestFromFrame(
diff --git a/docs/gwp_asan.md b/docs/gwp_asan.md
index 90cdc7a..d2f6571 100644
--- a/docs/gwp_asan.md
+++ b/docs/gwp_asan.md
@@ -5,6 +5,9 @@
 causing memory errors to crash and report additional debugging context about
 the error.
 
+It is also known by its recursive backronym, GWP-ASan Will Provide Allocation
+Sanity.
+
 ## Allocator
 
 The GuardedPageAllocator returns allocations on pages buffered on both sides by
@@ -47,10 +50,9 @@
 
 ## Status
 
-GWP-ASan is implemented for malloc and PartitionAlloc, but not for Oilpan or v8,
-on Windows and macOS. It is currently enabled by default for malloc. The
-allocator parameters can be manually modified by using an invocation like the
-following:
+GWP-ASan is implemented for malloc and PartitionAlloc. It is enabled by default
+on Windows and macOS. The allocator parameters can be manually modified by using
+an invocation like the following:
 
 ```shell
 chrome --enable-features="GwpAsanMalloc<Study" \
@@ -81,6 +83,10 @@
 - GWP-ASan does not hook PDFium's fork of PartitionAlloc.
 - Right-aligned allocations to catch overflows are not perfectly right-aligned,
   so small out-of-bounds accesses may be missed.
+- GWP-ASan does not sample some early allocations that occur before field trial
+  initialization.
+- Depending on the platform, GWP-ASan may or may not hook malloc allocations
+  that occur in code not linked directly against Chrome.
 
 ## Testing
 
diff --git a/docs/testing/code_coverage_in_gerrit.md b/docs/testing/code_coverage_in_gerrit.md
index 03a92b25e..ba98485e 100644
--- a/docs/testing/code_coverage_in_gerrit.md
+++ b/docs/testing/code_coverage_in_gerrit.md
@@ -8,8 +8,17 @@
 it to ensure you only submit well-tested code**.
 
 To see code coverage for a Chromium CL, **trigger a CQ dry run**, and once the
-builds finish and code coverage data is processed successfully, **look
-at the right column of the side by side diff view to see coverage information**:
+builds finish and code coverage data is processed successfully, **look at the
+change view to see absolute and incremental code coverage percentages**:
+
+![code_coverage_percentages]
+
+Absolute coverage percentage is the percentage of lines covered by tests
+out of **all the lines** in the file, while incremental coverage percentage only
+accounts for **newly added or modified lines**.
+
+To further dig into specific lines that are not covered by tests, **look at the
+right column of the side by side diff view**:
 
 ![code_coverage_annotations]
 
@@ -51,6 +60,7 @@
 [choose_tryjobs]: images/code_coverage_choose_tryjobs.png
 [linux_coverage_rel]: images/code_coverage_linux_coverage_rel.png
 [code_coverage_annotations]: images/code_coverage_annotations.png
+[code_coverage_percentages]: images/code_coverage_percentages.png
 [file a bug]: https://bugs.chromium.org/p/chromium/issues/entry?components=Infra%3ETest%3ECodeCoverage
 [code-coverage group]: https://groups.google.com/a/chromium.org/forum/#!forum/code-coverage
 [code_coverage.md]: code_coverage.md
diff --git a/docs/testing/images/code_coverage_percentages.png b/docs/testing/images/code_coverage_percentages.png
new file mode 100644
index 0000000..e5c3f88
--- /dev/null
+++ b/docs/testing/images/code_coverage_percentages.png
Binary files differ
diff --git a/extensions/browser/api/declarative_net_request/BUILD.gn b/extensions/browser/api/declarative_net_request/BUILD.gn
index 61100fd6..1630351 100644
--- a/extensions/browser/api/declarative_net_request/BUILD.gn
+++ b/extensions/browser/api/declarative_net_request/BUILD.gn
@@ -47,6 +47,7 @@
     "//extensions/common",
     "//extensions/common/api",
     "//net",
+    "//third_party/re2",
     "//tools/json_schema_compiler:generated_api_util",
     "//url",
   ]
diff --git a/extensions/browser/api/declarative_net_request/constants.cc b/extensions/browser/api/declarative_net_request/constants.cc
index 3b8e29d..23d238d 100644
--- a/extensions/browser/api/declarative_net_request/constants.cc
+++ b/extensions/browser/api/declarative_net_request/constants.cc
@@ -29,7 +29,7 @@
     "Rule with id * is not applicable to any resource type.";
 const char kErrorEmptyList[] =
     "Rule with id * cannot have an empty list as the value for * key.";
-const char kErrorEmptyUrlFilter[] =
+const char kErrorEmptyKey[] =
     "Rule with id * cannot have an empty value for * key.";
 const char kErrorInvalidRedirectUrl[] =
     "Rule with id * does not provide a valid URL for * key.";
@@ -52,6 +52,9 @@
 const char kErrorJavascriptRedirect[] =
     "Rule with id * specifies an incorrect value for the \"*\" key. Redirects "
     "to javascript urls are not supported.";
+const char kErrorMultipleFilters[] =
+    "Rule with id * can only specify one of \"*\" or \"*\" keys.";
+
 const char kErrorListNotPassed[] = "Rules file must contain a list.";
 
 const char kRuleCountExceeded[] =
diff --git a/extensions/browser/api/declarative_net_request/constants.h b/extensions/browser/api/declarative_net_request/constants.h
index 9528258..8a7c9a0 100644
--- a/extensions/browser/api/declarative_net_request/constants.h
+++ b/extensions/browser/api/declarative_net_request/constants.h
@@ -46,6 +46,10 @@
   ERROR_INVALID_TRANSFORM_FRAGMENT,
   ERROR_QUERY_AND_TRANSFORM_BOTH_SPECIFIED,
   ERROR_JAVASCRIPT_REDIRECT,
+  ERROR_EMPTY_REGEX_FILTER,
+  ERROR_NON_ASCII_REGEX_FILTER,
+  ERROR_INVALID_REGEX_FILTER,
+  ERROR_MULTIPLE_FILTERS_SPECIFIED,
 };
 
 // Describes the ways in which updating dynamic rules can fail.
@@ -100,7 +104,7 @@
 extern const char kErrorInvalidRuleKey[];
 extern const char kErrorNoApplicableResourceTypes[];
 extern const char kErrorEmptyList[];
-extern const char kErrorEmptyUrlFilter[];
+extern const char kErrorEmptyKey[];
 extern const char kErrorInvalidRedirectUrl[];
 extern const char kErrorDuplicateIDs[];
 extern const char kErrorPersisting[];
@@ -110,6 +114,7 @@
 extern const char kErrorInvalidTransformScheme[];
 extern const char kErrorQueryAndTransformBothSpecified[];
 extern const char kErrorJavascriptRedirect[];
+extern const char kErrorMultipleFilters[];
 
 extern const char kErrorListNotPassed[];
 
diff --git a/extensions/browser/api/declarative_net_request/flat/extension_ruleset.fbs b/extensions/browser/api/declarative_net_request/flat/extension_ruleset.fbs
index 8eafd6c6..2f20c7f2 100644
--- a/extensions/browser/api/declarative_net_request/flat/extension_ruleset.fbs
+++ b/extensions/browser/api/declarative_net_request/flat/extension_ruleset.fbs
@@ -62,6 +62,8 @@
 
 /// This provides a mapping from an action to its index within the |index_list|
 /// vector.
+/// TODO(crbug.com/1017868): This should be unified with ActionType once generic
+/// priorities are implemented.
 enum ActionIndex : ubyte {
   block = 0,
   allow,
@@ -74,6 +76,36 @@
   count
 }
 
+/// The type of an action. Corresponds to
+/// extensions::api::declarative_net_request::RuleActionType.
+enum ActionType : ubyte {
+  block,
+  allow,
+  redirect,
+  upgrade_scheme,
+  remove_headers
+}
+
+/// The type of header to remove. Corresponds to
+/// extensions::api::declarative_net_request::RemoveHeaderType.
+enum RemoveHeaderType : ubyte (bit_flags) {
+  cookie,
+  referer,
+  set_cookie
+}
+
+/// Completely represents a rule with a regex filter.
+table RegexRule {
+  /// The underlying UrlRule.
+  url_rule: url_pattern_index.flat.UrlRule;
+
+  /// The action to take.
+  action_type: ActionType;
+
+  /// The headers to be removed. Mask of RemoveHeaderType.
+  remove_headers_mask: ubyte;
+}
+
 /// The top-level data structure used to store extensions URL rules for the
 /// Declarative Net Request API.
 table ExtensionIndexedRuleset {
@@ -81,6 +113,10 @@
   /// of ActionIndex_count indices.
   index_list : [url_pattern_index.flat.UrlPatternIndex];
 
+  // Regex rules are not matched by UrlPatternIndex and so we don't build an
+  // index for them.
+  regex_rules: [RegexRule];
+
   /// Extension related metadata. Sorted by id, to support fast lookup.
   /// Currently this is only used for redirect rules.
   extension_metadata : [UrlRuleMetadata];
diff --git a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc
index ee2b2d0..b7c7e6cc 100644
--- a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc
+++ b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.cc
@@ -177,10 +177,19 @@
       domains_included_offset, domains_excluded_offset, url_pattern_offset,
       indexed_rule.id, indexed_rule.priority);
 
-  std::vector<UrlPatternIndexBuilder*> builders = GetBuilders(indexed_rule);
-  DCHECK(!builders.empty());
-  for (UrlPatternIndexBuilder* builder : builders)
-    builder->IndexUrlRule(offset);
+  if (indexed_rule.url_pattern_type !=
+      url_pattern_index::flat::UrlPatternType_REGEXP) {
+    std::vector<UrlPatternIndexBuilder*> builders = GetBuilders(indexed_rule);
+    DCHECK(!builders.empty());
+    for (UrlPatternIndexBuilder* builder : builders)
+      builder->IndexUrlRule(offset);
+  } else {
+    // A UrlPatternIndex is not built for regex rules. These are stored
+    // separately.
+    regex_rules_.push_back(
+        flat::CreateRegexRule(builder_, offset, GetActionType(indexed_rule),
+                              GetRemoveHeadersMask(indexed_rule)));
+  }
 
   // Store additional metadata required for a redirect rule.
   if (indexed_rule.action_type == dnr_api::RULE_ACTION_TYPE_REDIRECT) {
@@ -220,8 +229,12 @@
   FlatVectorOffset<flat::UrlRuleMetadata> extension_metadata_offset =
       builder_.CreateVectorOfSortedTables(&metadata_);
 
+  FlatVectorOffset<flat::RegexRule> regex_rules_offset =
+      builder_.CreateVector(regex_rules_);
+
   FlatOffset<flat::ExtensionIndexedRuleset> root_offset =
       flat::CreateExtensionIndexedRuleset(builder_, index_vector_offset,
+                                          regex_rules_offset,
                                           extension_metadata_offset);
   flat::FinishExtensionIndexedRulesetBuffer(builder_, root_offset);
 }
@@ -231,6 +244,48 @@
   return base::make_span(builder_.GetBufferPointer(), builder_.GetSize());
 }
 
+flat::ActionType FlatRulesetIndexer::GetActionType(
+    const IndexedRule& indexed_rule) const {
+  switch (indexed_rule.action_type) {
+    case dnr_api::RULE_ACTION_TYPE_BLOCK:
+      return flat::ActionType_block;
+    case dnr_api::RULE_ACTION_TYPE_ALLOW:
+      return flat::ActionType_allow;
+    case dnr_api::RULE_ACTION_TYPE_REDIRECT:
+      return flat::ActionType_redirect;
+    case dnr_api::RULE_ACTION_TYPE_REMOVEHEADERS:
+      return flat::ActionType_remove_headers;
+    case dnr_api::RULE_ACTION_TYPE_UPGRADESCHEME:
+      return flat::ActionType_upgrade_scheme;
+    case dnr_api::RULE_ACTION_TYPE_NONE:
+      break;
+  }
+  NOTREACHED();
+  return flat::ActionType_block;
+}
+
+uint8_t FlatRulesetIndexer::GetRemoveHeadersMask(
+    const IndexedRule& indexed_rule) const {
+  uint8_t mask = 0;
+  for (const dnr_api::RemoveHeaderType type : indexed_rule.remove_headers_set) {
+    switch (type) {
+      case dnr_api::REMOVE_HEADER_TYPE_NONE:
+        NOTREACHED();
+        break;
+      case dnr_api::REMOVE_HEADER_TYPE_COOKIE:
+        mask |= flat::RemoveHeaderType_cookie;
+        break;
+      case dnr_api::REMOVE_HEADER_TYPE_REFERER:
+        mask |= flat::RemoveHeaderType_referer;
+        break;
+      case dnr_api::REMOVE_HEADER_TYPE_SETCOOKIE:
+        mask |= flat::RemoveHeaderType_set_cookie;
+        break;
+    }
+  }
+  return mask;
+}
+
 std::vector<FlatRulesetIndexer::UrlPatternIndexBuilder*>
 FlatRulesetIndexer::GetBuilders(const IndexedRule& indexed_rule) {
   switch (indexed_rule.action_type) {
diff --git a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.h b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.h
index fb0d97c..7a617ff 100644
--- a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.h
+++ b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer.h
@@ -45,6 +45,8 @@
  private:
   using UrlPatternIndexBuilder = url_pattern_index::UrlPatternIndexBuilder;
 
+  flat::ActionType GetActionType(const IndexedRule& indexed_rule) const;
+  uint8_t GetRemoveHeadersMask(const IndexedRule& indexed_rule) const;
   std::vector<UrlPatternIndexBuilder*> GetBuilders(
       const IndexedRule& indexed_rule);
   std::vector<UrlPatternIndexBuilder*> GetRemoveHeaderBuilders(
@@ -58,6 +60,8 @@
 
   std::vector<flatbuffers::Offset<flat::UrlRuleMetadata>> metadata_;
 
+  std::vector<flatbuffers::Offset<flat::RegexRule>> regex_rules_;
+
   size_t indexed_rules_count_ = 0;  // Number of rules indexed till now.
   bool finished_ = false;           // Whether Finish() has been called.
 
diff --git a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer_unittest.cc b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer_unittest.cc
index 8c77a14..1943c9b6 100644
--- a/extensions/browser/api/declarative_net_request/flat_ruleset_indexer_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/flat_ruleset_indexer_unittest.cc
@@ -270,25 +270,34 @@
   }
 }
 
+const flat::ExtensionIndexedRuleset* AddRuleAndGetRuleset(
+    const std::vector<IndexedRule>& rules_to_index,
+    FlatRulesetIndexer* indexer) {
+  for (const auto& rule : rules_to_index)
+    indexer->AddUrlRule(rule);
+  indexer->Finish();
+
+  base::span<const uint8_t> data = indexer->GetData();
+  EXPECT_EQ(rules_to_index.size(), indexer->indexed_rules_count());
+  flatbuffers::Verifier verifier(data.data(), data.size());
+  if (!flat::VerifyExtensionIndexedRulesetBuffer(verifier))
+    return nullptr;
+
+  return flat::GetExtensionIndexedRuleset(data.data());
+}
+
 // Helper which:
 //    - Constructs an ExtensionIndexedRuleset flatbuffer from the passed
 //      IndexedRule(s) using FlatRulesetIndexer.
 //    - Verifies that the ExtensionIndexedRuleset created is valid.
+// Note: this does not test regex rules which are part of the
+// ExtensionIndexedRuleset.
 void AddRulesAndVerifyIndex(const std::vector<IndexedRule>& rules_to_index,
                             const std::vector<const IndexedRule*>
                                 expected_index_lists[flat::ActionIndex_count]) {
   FlatRulesetIndexer indexer;
-  for (const auto& rule : rules_to_index)
-    indexer.AddUrlRule(rule);
-  indexer.Finish();
-
-  base::span<const uint8_t> data = indexer.GetData();
-  EXPECT_EQ(rules_to_index.size(), indexer.indexed_rules_count());
-  flatbuffers::Verifier verifier(data.data(), data.size());
-  ASSERT_TRUE(flat::VerifyExtensionIndexedRulesetBuffer(verifier));
-
   const flat::ExtensionIndexedRuleset* ruleset =
-      flat::GetExtensionIndexedRuleset(data.data());
+      AddRuleAndGetRuleset(rules_to_index, &indexer);
   ASSERT_TRUE(ruleset);
 
   for (size_t i = 0; i < flat::ActionIndex_count; ++i) {
@@ -412,6 +421,82 @@
   AddRulesAndVerifyIndex(rules_to_index, expected_index_lists);
 }
 
+// Verify that the serialized flatbuffer data is valid for regex rules.
+TEST_F(FlatRulesetIndexerTest, RegexRules) {
+  std::vector<IndexedRule> rules_to_index;
+
+  // Blocking rule.
+  rules_to_index.push_back(CreateIndexedRule(
+      7, kMinValidPriority, flat_rule::OptionFlag_NONE,
+      flat_rule::ElementType_OBJECT, flat_rule::ActivationType_NONE,
+      flat_rule::UrlPatternType_REGEXP, flat_rule::AnchorType_NONE,
+      flat_rule::AnchorType_NONE, R"(^https://(abc|def))", {"a.com"},
+      {"x.a.com"}, base::nullopt, dnr_api::RULE_ACTION_TYPE_BLOCK, {}));
+  // Redirect rule.
+  rules_to_index.push_back(CreateIndexedRule(
+      15, 2, flat_rule::OptionFlag_APPLIES_TO_FIRST_PARTY,
+      flat_rule::ElementType_IMAGE, flat_rule::ActivationType_NONE,
+      flat_rule::UrlPatternType_REGEXP, flat_rule::AnchorType_NONE,
+      flat_rule::AnchorType_NONE, R"(^(http|https))", {}, {},
+      "http://example1.com", dnr_api::RULE_ACTION_TYPE_REDIRECT, {}));
+  // Remove headers rule.
+  rules_to_index.push_back(CreateIndexedRule(
+      20, kMinValidPriority, flat_rule::OptionFlag_IS_CASE_INSENSITIVE,
+      flat_rule::ElementType_SUBDOCUMENT, flat_rule::ActivationType_NONE,
+      flat_rule::UrlPatternType_REGEXP, flat_rule::AnchorType_NONE,
+      flat_rule::AnchorType_NONE, "*", {}, {}, base::nullopt,
+      dnr_api::RULE_ACTION_TYPE_REMOVEHEADERS,
+      {dnr_api::REMOVE_HEADER_TYPE_COOKIE,
+       dnr_api::REMOVE_HEADER_TYPE_SETCOOKIE}));
+
+  FlatRulesetIndexer indexer;
+  const flat::ExtensionIndexedRuleset* ruleset =
+      AddRuleAndGetRuleset(rules_to_index, &indexer);
+  ASSERT_TRUE(ruleset);
+
+  // All the indices should be empty, since we only have regex rules.
+  for (size_t i = 0; i < flat::ActionIndex_count; ++i) {
+    SCOPED_TRACE(base::StringPrintf("Testing index %" PRIuS, i));
+    VerifyIndexEquality({}, ruleset->index_list()->Get(i));
+  }
+
+  // We should have metadata for the redirect rule.
+  {
+    SCOPED_TRACE("Testing extension metadata");
+    VerifyExtensionMetadata({&rules_to_index[1]},
+                            ruleset->extension_metadata());
+  }
+
+  ASSERT_TRUE(ruleset->regex_rules());
+  ASSERT_EQ(3u, ruleset->regex_rules()->size());
+
+  const flat::RegexRule* blocking_rule = nullptr;
+  const flat::RegexRule* redirect_rule = nullptr;
+  const flat::RegexRule* remove_header_rule = nullptr;
+  for (const auto* regex_rule : *ruleset->regex_rules()) {
+    if (regex_rule->action_type() == flat::ActionType_block)
+      blocking_rule = regex_rule;
+    else if (regex_rule->action_type() == flat::ActionType_redirect)
+      redirect_rule = regex_rule;
+    else if (regex_rule->action_type() == flat::ActionType_remove_headers)
+      remove_header_rule = regex_rule;
+  }
+
+  ASSERT_TRUE(blocking_rule);
+  EXPECT_TRUE(AreRulesEqual(&rules_to_index[0], blocking_rule->url_rule()));
+  EXPECT_EQ(0u, blocking_rule->remove_headers_mask());
+
+  ASSERT_TRUE(redirect_rule);
+  EXPECT_TRUE(AreRulesEqual(&rules_to_index[1], redirect_rule->url_rule()));
+  EXPECT_EQ(0u, redirect_rule->remove_headers_mask());
+
+  ASSERT_TRUE(remove_header_rule);
+  EXPECT_TRUE(
+      AreRulesEqual(&rules_to_index[2], remove_header_rule->url_rule()));
+  EXPECT_EQ(flat::RemoveHeaderType_cookie | flat::RemoveHeaderType_set_cookie,
+            remove_header_rule->remove_headers_mask());
+}
+
 }  // namespace
 }  // namespace declarative_net_request
 }  // namespace extensions
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule.cc b/extensions/browser/api/declarative_net_request/indexed_rule.cc
index aa20994..c17d60b 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_rule.cc
@@ -15,6 +15,7 @@
 #include "extensions/browser/api/declarative_net_request/constants.h"
 #include "extensions/common/api/declarative_net_request.h"
 #include "extensions/common/api/declarative_net_request/utils.h"
+#include "third_party/re2/src/re2/re2.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
@@ -123,16 +124,24 @@
   DISALLOW_COPY_AND_ASSIGN(UrlFilterParser);
 };
 
+bool IsCaseSensitive(const dnr_api::Rule& parsed_rule) {
+  // If case sensitivity is not explicitly specified, rules are considered case
+  // sensitive by default.
+  if (!parsed_rule.condition.is_url_filter_case_sensitive)
+    return true;
+
+  return *parsed_rule.condition.is_url_filter_case_sensitive;
+}
+
 // Returns a bitmask of flat_rule::OptionFlag corresponding to |parsed_rule|.
 uint8_t GetOptionsMask(const dnr_api::Rule& parsed_rule) {
   uint8_t mask = flat_rule::OptionFlag_NONE;
 
   if (parsed_rule.action.type == dnr_api::RULE_ACTION_TYPE_ALLOW)
     mask |= flat_rule::OptionFlag_IS_WHITELIST;
-  if (parsed_rule.condition.is_url_filter_case_sensitive &&
-      !*parsed_rule.condition.is_url_filter_case_sensitive) {
+
+  if (!IsCaseSensitive(parsed_rule))
     mask |= flat_rule::OptionFlag_IS_CASE_INSENSITIVE;
-  }
 
   switch (parsed_rule.condition.domain_type) {
     case dnr_api::DOMAIN_TYPE_FIRSTPARTY:
@@ -350,6 +359,30 @@
   return ParseResult::ERROR_INVALID_REDIRECT;
 }
 
+bool IsValidRegex(const dnr_api::Rule& parsed_rule) {
+  DCHECK(parsed_rule.condition.regex_filter);
+
+  re2::RE2::Options options;
+
+  // RE2 supports UTF-8 and Latin1 encoding. We only need to support ASCII, so
+  // use Latin1 encoding. This should also be more efficient than UTF-8.
+  // Note: Latin1 is an 8 bit extension to ASCII.
+  options.set_encoding(re2::RE2::Options::EncodingLatin1);
+
+  options.set_case_sensitive(IsCaseSensitive(parsed_rule));
+
+  // Don't capture unless needed, for efficiency.
+  // TODO(crbug.com/974391): Capturing should be supported for regex based
+  // substitutions which are not implemented yet.
+  options.set_never_capture(true);
+
+  // TODO(crbug.com/974391): Regex compilation can be expensive. Also, these
+  // need to be compiled again once the ruleset is loaded, which means duplicate
+  // work. We should maintain a global cache of compiled regexes.
+  re2::RE2 regex(*parsed_rule.condition.regex_filter, options);
+  return regex.ok();
+}
+
 }  // namespace
 
 IndexedRule::IndexedRule() = default;
@@ -399,9 +432,27 @@
     return ParseResult::ERROR_EMPTY_RESOURCE_TYPES_LIST;
   }
 
+  if (parsed_rule.condition.url_filter && parsed_rule.condition.regex_filter)
+    return ParseResult::ERROR_MULTIPLE_FILTERS_SPECIFIED;
+
+  // TODO(crbug.com/974391): Implement limits on the number of regex rules an
+  // extension can specify.
+  const bool is_regex_rule = !!parsed_rule.condition.regex_filter;
+  if (is_regex_rule) {
+    if (parsed_rule.condition.regex_filter->empty())
+      return ParseResult::ERROR_EMPTY_REGEX_FILTER;
+
+    if (!base::IsStringASCII(*parsed_rule.condition.regex_filter))
+      return ParseResult::ERROR_NON_ASCII_REGEX_FILTER;
+
+    if (!IsValidRegex(parsed_rule))
+      return ParseResult::ERROR_INVALID_REGEX_FILTER;
+  }
+
   if (parsed_rule.condition.url_filter) {
     if (parsed_rule.condition.url_filter->empty())
       return ParseResult::ERROR_EMPTY_URL_FILTER;
+
     if (!base::IsStringASCII(*parsed_rule.condition.url_filter))
       return ParseResult::ERROR_NON_ASCII_URL_FILTER;
   }
@@ -431,10 +482,16 @@
     return ParseResult::ERROR_NON_ASCII_EXCLUDED_DOMAIN;
   }
 
-  // Parse the |anchor_left|, |anchor_right|, |url_pattern_type| and
-  // |url_pattern| fields.
-  UrlFilterParser::Parse(std::move(parsed_rule.condition.url_filter),
-                         indexed_rule);
+  if (is_regex_rule) {
+    indexed_rule->url_pattern_type =
+        url_pattern_index::flat::UrlPatternType_REGEXP;
+    indexed_rule->url_pattern = std::move(*parsed_rule.condition.regex_filter);
+  } else {
+    // Parse the |anchor_left|, |anchor_right|, |url_pattern_type| and
+    // |url_pattern| fields.
+    UrlFilterParser::Parse(std::move(parsed_rule.condition.url_filter),
+                           indexed_rule);
+  }
 
   // url_pattern_index doesn't support patterns starting with a domain anchor
   // followed by a wildcard, e.g. ||*xyz.
@@ -465,7 +522,6 @@
   DCHECK(IsSubset(indexed_rule->options, flat_rule::OptionFlag_ANY));
   DCHECK(IsSubset(indexed_rule->element_types, flat_rule::ElementType_ANY));
   DCHECK_EQ(flat_rule::ActivationType_NONE, indexed_rule->activation_types);
-  DCHECK_NE(flat_rule::UrlPatternType_REGEXP, indexed_rule->url_pattern_type);
   DCHECK_NE(flat_rule::AnchorType_SUBDOMAIN, indexed_rule->anchor_right);
 
   return ParseResult::SUCCESS;
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule.h b/extensions/browser/api/declarative_net_request/indexed_rule.h
index 52afdb68..cb24d31 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule.h
+++ b/extensions/browser/api/declarative_net_request/indexed_rule.h
@@ -51,6 +51,7 @@
   url_pattern_index::flat::AnchorType anchor_right =
       url_pattern_index::flat::AnchorType_NONE;
   std::string url_pattern;
+
   // Lower-cased and sorted as required by the url_pattern_index component.
   std::vector<std::string> domains;
   std::vector<std::string> excluded_domains;
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc b/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
index 353465f..5ac8c19a 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_rule_unittest.cc
@@ -637,6 +637,42 @@
   }
 }
 
+TEST_F(IndexedRuleTest, RegexFilterParsing) {
+  struct {
+    std::string regex_filter;
+    ParseResult result;
+  } cases[] = {{"", ParseResult::ERROR_EMPTY_REGEX_FILTER},
+               // Filter with non-ascii characters.
+               {"αcd", ParseResult::ERROR_NON_ASCII_REGEX_FILTER},
+               // Invalid regex: Unterminated character class.
+               {"x[ab", ParseResult::ERROR_INVALID_REGEX_FILTER},
+               // Invalid regex: Incomplete capturing group.
+               {"x(", ParseResult::ERROR_INVALID_REGEX_FILTER},
+               // Invalid regex: Invalid escape sequence \x.
+               {R"(ij\x1)", ParseResult::ERROR_INVALID_REGEX_FILTER},
+               {R"(ij\\x1)", ParseResult::SUCCESS},
+               {R"(^http://www\.(abc|def)\.xyz\.com/)", ParseResult::SUCCESS}};
+
+  for (const auto& test_case : cases) {
+    SCOPED_TRACE(test_case.regex_filter);
+    dnr_api::Rule rule = CreateGenericParsedRule();
+    rule.condition.url_filter.reset();
+    rule.condition.regex_filter =
+        std::make_unique<std::string>(test_case.regex_filter);
+
+    IndexedRule indexed_rule;
+    ParseResult result = IndexedRule::CreateIndexedRule(
+        std::move(rule), GetBaseURL(), &indexed_rule);
+    EXPECT_EQ(result, test_case.result);
+
+    if (result == ParseResult::SUCCESS) {
+      EXPECT_EQ(indexed_rule.url_pattern, test_case.regex_filter);
+      EXPECT_EQ(flat_rule::UrlPatternType_REGEXP,
+                indexed_rule.url_pattern_type);
+    }
+  }
+}
+
 }  // namespace
 }  // namespace declarative_net_request
 }  // namespace extensions
diff --git a/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc b/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc
index 2667906b..22da59c3 100644
--- a/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_ruleset_format_version_unittest.cc
@@ -55,8 +55,26 @@
   remove_set_cookie_header,
   count
 }
+enum ActionType : ubyte {
+  block,
+  allow,
+  redirect,
+  upgrade_scheme,
+  remove_headers
+}
+enum RemoveHeaderType : ubyte (bit_flags) {
+  cookie,
+  referer,
+  set_cookie
+}
+table RegexRule {
+  url_rule: url_pattern_index.flat.UrlRule;
+  action_type: ActionType;
+  remove_headers_mask: ubyte;
+}
 table ExtensionIndexedRuleset {
   index_list : [url_pattern_index.flat.UrlPatternIndex];
+  regex_rules: [RegexRule];
   extension_metadata : [UrlRuleMetadata];
 }
 root_type ExtensionIndexedRuleset;
@@ -125,7 +143,7 @@
   EXPECT_EQ(StripCommentsAndWhitespace(kFlatbufferSchemaExpected),
             StripCommentsAndWhitespace(flatbuffer_schema))
       << "Schema change detected; update this test and the schema version.";
-  EXPECT_EQ(11, GetIndexedRulesetFormatVersionForTesting())
+  EXPECT_EQ(12, GetIndexedRulesetFormatVersionForTesting())
       << "Update this test if you update the schema version.";
 }
 
diff --git a/extensions/browser/api/declarative_net_request/parse_info.cc b/extensions/browser/api/declarative_net_request/parse_info.cc
index 802f3caa..2d8d5e6 100644
--- a/extensions/browser/api/declarative_net_request/parse_info.cc
+++ b/extensions/browser/api/declarative_net_request/parse_info.cc
@@ -81,7 +81,7 @@
       break;
     case ParseResult::ERROR_EMPTY_URL_FILTER:
       error = ErrorUtils::FormatErrorMessage(
-          kErrorEmptyUrlFilter, base::NumberToString(*rule_id_), kUrlFilterKey);
+          kErrorEmptyKey, base::NumberToString(*rule_id_), kUrlFilterKey);
       break;
     case ParseResult::ERROR_INVALID_REDIRECT_URL:
       error = ErrorUtils::FormatErrorMessage(kErrorInvalidRedirectUrl,
@@ -156,6 +156,23 @@
                                              base::NumberToString(*rule_id_),
                                              kRedirectUrlPath);
       break;
+    case ParseResult::ERROR_EMPTY_REGEX_FILTER:
+      error = ErrorUtils::FormatErrorMessage(
+          kErrorEmptyKey, base::NumberToString(*rule_id_), kRegexFilterKey);
+      break;
+    case ParseResult::ERROR_NON_ASCII_REGEX_FILTER:
+      error = ErrorUtils::FormatErrorMessage(
+          kErrorNonAscii, base::NumberToString(*rule_id_), kRegexFilterKey);
+      break;
+    case ParseResult::ERROR_INVALID_REGEX_FILTER:
+      error = ErrorUtils::FormatErrorMessage(
+          kErrorInvalidKey, base::NumberToString(*rule_id_), kRegexFilterKey);
+      break;
+    case ParseResult::ERROR_MULTIPLE_FILTERS_SPECIFIED:
+      error = ErrorUtils::FormatErrorMessage(kErrorMultipleFilters,
+                                             base::NumberToString(*rule_id_),
+                                             kUrlFilterKey, kRegexFilterKey);
+      break;
   }
   return error;
 }
diff --git a/extensions/browser/api/declarative_net_request/utils.cc b/extensions/browser/api/declarative_net_request/utils.cc
index 5da9751a..33aa729 100644
--- a/extensions/browser/api/declarative_net_request/utils.cc
+++ b/extensions/browser/api/declarative_net_request/utils.cc
@@ -33,7 +33,7 @@
 // url_pattern_index.fbs. Whenever an extension with an indexed ruleset format
 // version different from the one currently used by Chrome is loaded, the
 // extension ruleset will be reindexed.
-constexpr int kIndexedRulesetFormatVersion = 11;
+constexpr int kIndexedRulesetFormatVersion = 12;
 
 // This static assert is meant to catch cases where
 // url_pattern_index::kUrlPatternIndexFormatVersion is incremented without
diff --git a/extensions/common/api/declarative_net_request.idl b/extensions/common/api/declarative_net_request.idl
index 8f7e388..c4c928c 100644
--- a/extensions/common/api/declarative_net_request.idl
+++ b/extensions/common/api/declarative_net_request.idl
@@ -143,6 +143,11 @@
     // http://abc.xn--p1ai/?q=%D1%84.
     DOMString? urlFilter;
 
+    // TODO(crbug.com/974391): Add documentation once the implementation is
+    // complete.
+    [nodoc]
+    DOMString? regexFilter;
+
     // Whether the <code>urlFilter</code> is case sensitive. Default is true.
     boolean? isUrlFilterCaseSensitive;
 
diff --git a/extensions/common/api/declarative_net_request/constants.cc b/extensions/common/api/declarative_net_request/constants.cc
index 7e01c4f9..a52500d6 100644
--- a/extensions/common/api/declarative_net_request/constants.cc
+++ b/extensions/common/api/declarative_net_request/constants.cc
@@ -15,6 +15,7 @@
 const char kRuleConditionKey[] = "condition";
 const char kRuleActionKey[] = "action";
 const char kUrlFilterKey[] = "urlFilter";
+const char kRegexFilterKey[] = "regexFilter";
 const char kIsUrlFilterCaseSensitiveKey[] = "isUrlFilterCaseSensitive";
 const char kDomainsKey[] = "domains";
 const char kExcludedDomainsKey[] = "excludedDomains";
diff --git a/extensions/common/api/declarative_net_request/constants.h b/extensions/common/api/declarative_net_request/constants.h
index f86e3f2..27ae6d1 100644
--- a/extensions/common/api/declarative_net_request/constants.h
+++ b/extensions/common/api/declarative_net_request/constants.h
@@ -30,6 +30,7 @@
 extern const char kRuleConditionKey[];
 extern const char kRuleActionKey[];
 extern const char kUrlFilterKey[];
+extern const char kRegexFilterKey[];
 extern const char kIsUrlFilterCaseSensitiveKey[];
 extern const char kDomainsKey[];
 extern const char kExcludedDomainsKey[];
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn
index 873afb2c..78aec4c 100644
--- a/extensions/renderer/BUILD.gn
+++ b/extensions/renderer/BUILD.gn
@@ -87,8 +87,6 @@
     "content_watcher.h",
     "context_menus_custom_bindings.cc",
     "context_menus_custom_bindings.h",
-    "css_native_handler.cc",
-    "css_native_handler.h",
     "declarative_content_hooks_delegate.cc",
     "declarative_content_hooks_delegate.h",
     "dispatcher.cc",
diff --git a/extensions/renderer/css_native_handler.cc b/extensions/renderer/css_native_handler.cc
deleted file mode 100644
index db8e52a..0000000
--- a/extensions/renderer/css_native_handler.cc
+++ /dev/null
@@ -1,41 +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.
-
-#include "extensions/renderer/css_native_handler.h"
-
-#include "base/bind.h"
-#include "extensions/renderer/script_context.h"
-#include "extensions/renderer/v8_helpers.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/web/web_selector.h"
-
-namespace extensions {
-
-using blink::WebString;
-
-CssNativeHandler::CssNativeHandler(ScriptContext* context)
-    : ObjectBackedNativeHandler(context) {}
-
-void CssNativeHandler::AddRoutes() {
-  RouteHandlerFunction(
-      "CanonicalizeCompoundSelector", "declarativeContent",
-      base::BindRepeating(&CssNativeHandler::CanonicalizeCompoundSelector,
-                          base::Unretained(this)));
-}
-
-void CssNativeHandler::CanonicalizeCompoundSelector(
-    const v8::FunctionCallbackInfo<v8::Value>& args) {
-  CHECK_EQ(args.Length(), 1);
-  CHECK(args[0]->IsString());
-  v8::Isolate* isolate = args.GetIsolate();
-  std::string input_selector = *v8::String::Utf8Value(isolate, args[0]);
-  // TODO(esprehn): This API shouldn't exist, the extension code should be
-  // moved into blink.
-  WebString output_selector = blink::CanonicalizeSelector(
-      WebString::FromUTF8(input_selector), blink::kWebSelectorTypeCompound);
-  args.GetReturnValue().Set(
-      v8_helpers::ToV8StringUnsafe(isolate, output_selector.Utf8().c_str()));
-}
-
-}  // namespace extensions
diff --git a/extensions/renderer/css_native_handler.h b/extensions/renderer/css_native_handler.h
deleted file mode 100644
index afe0633..0000000
--- a/extensions/renderer/css_native_handler.h
+++ /dev/null
@@ -1,31 +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 EXTENSIONS_RENDERER_CSS_NATIVE_HANDLER_H_
-#define EXTENSIONS_RENDERER_CSS_NATIVE_HANDLER_H_
-
-#include "extensions/renderer/object_backed_native_handler.h"
-
-namespace extensions {
-class ScriptContext;
-
-class CssNativeHandler : public ObjectBackedNativeHandler {
- public:
-  explicit CssNativeHandler(ScriptContext* context);
-
-  // ObjectBackedNativeHandler:
-  void AddRoutes() override;
-
- private:
-  // Expects one string argument that's a comma-separated list of compound CSS
-  // selectors (http://dev.w3.org/csswg/selectors4/#compound), and returns its
-  // Blink-canonicalized form. If the selector is invalid, returns an empty
-  // string.
-  void CanonicalizeCompoundSelector(
-      const v8::FunctionCallbackInfo<v8::Value>& args);
-};
-
-}  // namespace extensions
-
-#endif  // EXTENSIONS_RENDERER_CSS_NATIVE_HANDLER_H_
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 547ad19..2325d01c 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3377,6 +3377,19 @@
       "features": [
         "max_msaa_sample_count_2"
       ]
+    },
+    {
+      "id": 318,
+      "cr_bugs": [995396],
+      "description": "Direct composition caused performance issues on AMD GPUs",
+      "os": {
+        "type": "win"
+      },
+      "vendor_id": "0x1002",
+      "device_id": ["0x694c"],
+      "features": [
+        "disable_direct_composition"
+      ]
     }
   ]
 }
diff --git a/infra/config/buckets/ci.star b/infra/config/buckets/ci.star
index 71df7b5..d8d2540 100644
--- a/infra/config/buckets/ci.star
+++ b/infra/config/buckets/ci.star
@@ -1243,6 +1243,8 @@
 fyi_coverage_builder(
     name = 'win10-code-coverage',
     builderless = True,
+    goma_backend = goma.backend.RBE_PROD,
+    goma_enable_ats = True,
     os = os.WINDOWS_DEFAULT,
     ssd = True,
     use_clang_coverage = True,
@@ -1614,6 +1616,14 @@
 )
 
 gpu_fyi_linux_ci_tester(
+    name = 'Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)',
+)
+
+gpu_fyi_linux_ci_tester(
+    name = 'Win10 FYI x64 DX12 Vulkan Release (NVIDIA)',
+)
+
+gpu_fyi_linux_ci_tester(
     name = 'Win10 FYI x64 Exp Release (Intel HD 630)',
 )
 
@@ -1753,6 +1763,14 @@
 )
 
 gpu_fyi_windows_builder(
+    name = 'GPU FYI Win x64 DX12 Vulkan Builder',
+)
+
+gpu_fyi_windows_builder(
+    name = 'GPU FYI Win x64 DX12 Vulkan Builder (dbg)',
+)
+
+gpu_fyi_windows_builder(
     name = 'GPU FYI XR Win x64 Builder',
 )
 
diff --git a/infra/config/buckets/try.star b/infra/config/buckets/try.star
index 1e1e84ba..3b0a26e 100644
--- a/infra/config/buckets/try.star
+++ b/infra/config/buckets/try.star
@@ -1035,6 +1035,14 @@
 )
 
 gpu_win_builder(
+    name = 'gpu-fyi-try-win10-nvidia-dx12vk-dbg-64',
+)
+
+gpu_win_builder(
+    name = 'gpu-fyi-try-win10-nvidia-dx12vk-rel-64',
+)
+
+gpu_win_builder(
     name = 'gpu-fyi-try-win10-nvidia-exp-64',
 )
 
diff --git a/infra/config/consoles/chromium.goma.migration.star b/infra/config/consoles/chromium.goma.migration.star
index f47628e..58337d2 100644
--- a/infra/config/consoles/chromium.goma.migration.star
+++ b/infra/config/consoles/chromium.goma.migration.star
@@ -700,5 +700,10 @@
             category = 'win|week1|asan',
             short_name = 'media',
         ),
+        luci.console_view_entry(
+            builder = 'ci/win10-code-coverage',
+            category = 'win|week1.1',
+            short_name = 'code',
+        ),
     ],
 )
diff --git a/infra/config/consoles/chromium.gpu.fyi.star b/infra/config/consoles/chromium.gpu.fyi.star
index 9c1312a..67e1af9 100644
--- a/infra/config/consoles/chromium.gpu.fyi.star
+++ b/infra/config/consoles/chromium.gpu.fyi.star
@@ -24,6 +24,16 @@
             short_name = 'x64',
         ),
         luci.console_view_entry(
+            builder = 'ci/GPU FYI Win x64 DX12 Vulkan Builder',
+            category = 'Windows|Builder|dx12vk',
+            short_name = 'rel',
+        ),
+        luci.console_view_entry(
+            builder = 'ci/GPU FYI Win x64 DX12 Vulkan Builder (dbg)',
+            category = 'Windows|Builder|dx12vk',
+            short_name = 'dbg',
+        ),
+        luci.console_view_entry(
             builder = 'ci/GPU FYI Win Builder (dbg)',
             category = 'Windows|Builder|Debug',
             short_name = 'x86',
@@ -44,6 +54,16 @@
             short_name = 'dbg',
         ),
         luci.console_view_entry(
+            builder = 'ci/Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)',
+            category = 'Windows|10|x64|Nvidia|dx12vk',
+            short_name = 'dbg',
+        ),
+        luci.console_view_entry(
+            builder = 'ci/Win10 FYI x64 DX12 Vulkan Release (NVIDIA)',
+            category = 'Windows|10|x64|Nvidia|dx12vk',
+            short_name = 'rel',
+        ),
+        luci.console_view_entry(
             builder = 'ci/Win10 FYI x64 Release (Intel HD 630)',
             category = 'Windows|10|x64|Intel',
             short_name = 'rel',
diff --git a/infra/config/consoles/tryserver.chromium.win.star b/infra/config/consoles/tryserver.chromium.win.star
index 0ed185a..79a7415a 100644
--- a/infra/config/consoles/tryserver.chromium.win.star
+++ b/infra/config/consoles/tryserver.chromium.win.star
@@ -13,6 +13,8 @@
         'try/gpu-fyi-try-win10-intel-rel-64',
         'try/gpu-fyi-try-win10-nvidia-dbg-64',
         'try/gpu-fyi-try-win10-nvidia-dqp-64',
+        'try/gpu-fyi-try-win10-nvidia-dx12vk-dbg-64',
+        'try/gpu-fyi-try-win10-nvidia-dx12vk-rel-64',
         'try/gpu-fyi-try-win10-nvidia-exp-64',
         'try/gpu-fyi-try-win10-nvidia-rel-32',
         'try/gpu-fyi-try-win10-nvidia-rel-64',
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index bf423371..a1c4218e 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -2489,6 +2489,46 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
     >
     builders: <
+      name: "GPU FYI Win x64 DX12 Vulkan Builder"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows"
+      dimensions: "ssd:0"
+      recipe: <
+        name: "chromium"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "mastername:\"chromium.gpu.fyi\""
+      >
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
+      name: "GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows"
+      dimensions: "ssd:0"
+      recipe: <
+        name: "chromium"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "mastername:\"chromium.gpu.fyi\""
+      >
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
       name: "GPU FYI Win x64 dEQP Builder"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -6007,6 +6047,46 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
     >
     builders: <
+      name: "Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:2"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "ssd:0"
+      recipe: <
+        name: "chromium"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "mastername:\"chromium.gpu.fyi\""
+      >
+      execution_timeout_secs: 21600
+      build_numbers: YES
+      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
+      name: "Win10 FYI x64 DX12 Vulkan Release (NVIDIA)"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:2"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-16.04"
+      dimensions: "ssd:0"
+      recipe: <
+        name: "chromium"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "mastername:\"chromium.gpu.fyi\""
+      >
+      execution_timeout_secs: 21600
+      build_numbers: YES
+      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+    >
+    builders: <
       name: "Win10 FYI x64 Debug (NVIDIA)"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -9210,6 +9290,7 @@
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
         properties_j: "$build/code_coverage:{\"use_clang_coverage\":true}"
+        properties_j: "$build/goma:{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.fyi\""
       >
@@ -12669,6 +12750,60 @@
       >
     >
     builders: <
+      name: "gpu-fyi-try-win10-nvidia-dx12vk-dbg-64"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builder:gpu-fyi-try-win10-nvidia-dx12vk-dbg-64"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows"
+      recipe: <
+        name: "chromium_trybot"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "mastername:\"tryserver.chromium.win\""
+      >
+      execution_timeout_secs: 21600
+      expiration_secs: 7200
+      caches: <
+        name: "win_toolchain"
+        path: "win_toolchain"
+      >
+      build_numbers: YES
+      service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage: <
+        value: 5
+      >
+    >
+    builders: <
+      name: "gpu-fyi-try-win10-nvidia-dx12vk-rel-64"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builder:gpu-fyi-try-win10-nvidia-dx12vk-rel-64"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows"
+      recipe: <
+        name: "chromium_trybot"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "mastername:\"tryserver.chromium.win\""
+      >
+      execution_timeout_secs: 21600
+      expiration_secs: 7200
+      caches: <
+        name: "win_toolchain"
+        path: "win_toolchain"
+      >
+      build_numbers: YES
+      service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage: <
+        value: 5
+      >
+    >
+    builders: <
       name: "gpu-fyi-try-win10-nvidia-exp-64"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index 4d201baa..2213483 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -5081,6 +5081,11 @@
     category: "win|week1|asan"
     short_name: "media"
   >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/win10-code-coverage"
+    category: "win|week1.1"
+    short_name: "code"
+  >
   header: <
     oncalls: <
       name: "Chromium"
@@ -5651,6 +5656,16 @@
     short_name: "x64"
   >
   builders: <
+    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder"
+    category: "Windows|Builder|dx12vk"
+    short_name: "rel"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
+    category: "Windows|Builder|dx12vk"
+    short_name: "dbg"
+  >
+  builders: <
     name: "buildbucket/luci.chromium.ci/GPU FYI Win Builder (dbg)"
     category: "Windows|Builder|Debug"
     short_name: "x86"
@@ -5671,6 +5686,16 @@
     short_name: "dbg"
   >
   builders: <
+    name: "buildbucket/luci.chromium.ci/Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)"
+    category: "Windows|10|x64|Nvidia|dx12vk"
+    short_name: "dbg"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/Win10 FYI x64 DX12 Vulkan Release (NVIDIA)"
+    category: "Windows|10|x64|Nvidia|dx12vk"
+    short_name: "rel"
+  >
+  builders: <
     name: "buildbucket/luci.chromium.ci/Win10 FYI x64 Release (Intel HD 630)"
     category: "Windows|10|x64|Intel"
     short_name: "rel"
@@ -11454,6 +11479,12 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-dqp-64"
   >
   builders: <
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-dx12vk-dbg-64"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-dx12vk-rel-64"
+  >
+  builders: <
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-exp-64"
   >
   builders: <
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index eca8972..875ed5e4 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -171,6 +171,8 @@
   triggers: "GPU FYI Win x64 Builder (dbg)"
   triggers: "GPU FYI Win x64 Builder"
   triggers: "GPU FYI Win x64 dEQP Builder"
+  triggers: "GPU FYI Win x64 DX12 Vulkan Builder"
+  triggers: "GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
   triggers: "GPU FYI XR Win x64 Builder"
   triggers: "GPU Linux Builder (dbg)"
   triggers: "GPU Mac Builder (dbg)"
@@ -3075,6 +3077,28 @@
 }
 
 job {
+  id: "Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)"
+  # Triggered by "GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
+  acl_sets: "triggered-by-parent-builders"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)"
+  }
+}
+
+job {
+  id: "Win10 FYI x64 DX12 Vulkan Release (NVIDIA)"
+  # Triggered by "GPU FYI Win x64 DX12 Vulkan Builder"
+  acl_sets: "triggered-by-parent-builders"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Win10 FYI x64 DX12 Vulkan Release (NVIDIA)"
+  }
+}
+
+job {
   id: "Win7 FYI Debug (AMD)"
   # Triggered by "GPU FYI Win Builder (dbg)"
   acl_sets: "triggered-by-parent-builders"
@@ -3159,6 +3183,26 @@
 }
 
 job {
+  id: "GPU FYI Win x64 DX12 Vulkan Builder"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "GPU FYI Win x64 DX12 Vulkan Builder"
+  }
+}
+
+job {
+  id: "GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
+  }
+}
+
+job {
   id: "WebRTC Chromium Win Builder"
   acl_sets: "default"
   acl_sets: "webrtc"
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg
index eca8972..875ed5e4 100644
--- a/infra/config/luci-scheduler.cfg
+++ b/infra/config/luci-scheduler.cfg
@@ -171,6 +171,8 @@
   triggers: "GPU FYI Win x64 Builder (dbg)"
   triggers: "GPU FYI Win x64 Builder"
   triggers: "GPU FYI Win x64 dEQP Builder"
+  triggers: "GPU FYI Win x64 DX12 Vulkan Builder"
+  triggers: "GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
   triggers: "GPU FYI XR Win x64 Builder"
   triggers: "GPU Linux Builder (dbg)"
   triggers: "GPU Mac Builder (dbg)"
@@ -3075,6 +3077,28 @@
 }
 
 job {
+  id: "Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)"
+  # Triggered by "GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
+  acl_sets: "triggered-by-parent-builders"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)"
+  }
+}
+
+job {
+  id: "Win10 FYI x64 DX12 Vulkan Release (NVIDIA)"
+  # Triggered by "GPU FYI Win x64 DX12 Vulkan Builder"
+  acl_sets: "triggered-by-parent-builders"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Win10 FYI x64 DX12 Vulkan Release (NVIDIA)"
+  }
+}
+
+job {
   id: "Win7 FYI Debug (AMD)"
   # Triggered by "GPU FYI Win Builder (dbg)"
   acl_sets: "triggered-by-parent-builders"
@@ -3159,6 +3183,26 @@
 }
 
 job {
+  id: "GPU FYI Win x64 DX12 Vulkan Builder"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "GPU FYI Win x64 DX12 Vulkan Builder"
+  }
+}
+
+job {
+  id: "GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "GPU FYI Win x64 DX12 Vulkan Builder (dbg)"
+  }
+}
+
+job {
   id: "WebRTC Chromium Win Builder"
   acl_sets: "default"
   acl_sets: "webrtc"
diff --git a/ios/build/bots/chromium.clang/ToTiOS.json b/ios/build/bots/chromium.clang/ToTiOS.json
index 5bb84836..845e158 100644
--- a/ios/build/bots/chromium.clang/ToTiOS.json
+++ b/ios/build/bots/chromium.clang/ToTiOS.json
@@ -23,119 +23,119 @@
       "app": "base_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "boringssl_crypto_tests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "boringssl_ssl_tests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "components_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "crypto_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "gfx_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "google_apis_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "ios_chrome_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "ios_net_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "ios_web_inttests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "ios_web_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "ios_web_view_inttests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "net_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "skia_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "sql_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "ui_base_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     },
     {
       "app": "url_unittests",
       "device type": "iPhone 6s",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "os": "12.2"
     }
   ]
diff --git a/ios/build/bots/chromium.fyi/ios-simulator-cronet.json b/ios/build/bots/chromium.fyi/ios-simulator-cronet.json
index 7867ea4..473c13a 100644
--- a/ios/build/bots/chromium.fyi/ios-simulator-cronet.json
+++ b/ios/build/bots/chromium.fyi/ios-simulator-cronet.json
@@ -28,14 +28,14 @@
       "device type": "iPhone X",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "app": "cronet_test",
       "device type": "iPhone X",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     }
   ]
 }
diff --git a/ios/build/bots/chromium.fyi/ios-simulator.json b/ios/build/bots/chromium.fyi/ios-simulator.json
index 144feb2..18e22e1 100644
--- a/ios/build/bots/chromium.fyi/ios-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios-simulator.json
@@ -22,7 +22,7 @@
       "device type": "iPhone 6s",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "app": "ios_chrome_integration_egtests",
@@ -33,7 +33,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "app": "ios_chrome_integration_egtests",
@@ -44,7 +44,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "app": "ios_chrome_integration_egtests",
@@ -55,7 +55,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "app": "ios_chrome_smoke_egtests",
@@ -66,7 +66,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "app": "ios_chrome_web_egtests",
@@ -77,7 +77,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "app": "ios_web_shell_egtests",
@@ -88,7 +88,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "app": "ios_chrome_ui_egtests",
@@ -99,7 +99,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     }
   ]
 }
diff --git a/ios/build/bots/chromium.fyi/ios-webkit-tot.json b/ios/build/bots/chromium.fyi/ios-webkit-tot.json
index 6ca32bf..6b8a0029 100644
--- a/ios/build/bots/chromium.fyi/ios-webkit-tot.json
+++ b/ios/build/bots/chromium.fyi/ios-webkit-tot.json
@@ -20,7 +20,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -31,7 +31,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -42,7 +42,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -53,7 +53,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -64,7 +64,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -75,7 +75,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -86,7 +86,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "test args": [
         "--run-with-custom-webkit"
       ]
@@ -97,7 +97,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "test args": [
         "--run-with-custom-webkit"
       ]
diff --git a/ios/build/bots/chromium.fyi/ios13-beta-simulator.json b/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
index 2d9f589..6f61a64 100644
--- a/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
@@ -25,7 +25,7 @@
       "device type": "iPhone X",
       "os": "13.1",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -34,7 +34,7 @@
       "device type": "iPhone 7",
       "os": "13.1",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -43,7 +43,7 @@
       "device type": "iPad (6th generation)",
       "os": "13.1",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -52,7 +52,7 @@
       "device type": "iPhone X",
       "os": "12.2",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -61,7 +61,7 @@
       "device type": "iPad (6th generation)",
       "os": "12.2",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -70,7 +70,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "eg_cq_tests.json",
@@ -78,7 +78,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "eg_cq_tests.json",
@@ -86,7 +86,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "eg_tests.json",
@@ -94,7 +94,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "eg_tests.json",
@@ -102,7 +102,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -110,7 +110,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -118,7 +118,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -126,7 +126,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     }
   ]
 }
diff --git a/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json b/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json
index dc1d025..f318845d 100644
--- a/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json
@@ -23,7 +23,7 @@
       "device type": "iPhone 6s Plus",
       "xcode build version": "11a1027",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -32,7 +32,7 @@
       "device type": "iPhone 6s",
       "xcode build version": "11a1027",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -41,7 +41,7 @@
       "device type": "iPhone 6s",
       "xcode build version": "11a1027",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -50,7 +50,7 @@
       "device type": "iPhone 7",
       "xcode build version": "11a1027",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -59,7 +59,7 @@
       "device type": "iPad Air 2",
       "xcode build version": "11a1027",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -68,7 +68,7 @@
       "device type": "iPhone X",
       "xcode build version": "11a1027",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -77,7 +77,7 @@
       "device type": "iPhone X",
       "xcode build version": "11a1027",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -86,7 +86,7 @@
       "device type": "iPad Air 2",
       "xcode build version": "11a1027",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     }
diff --git a/ios/build/bots/chromium.mac/ios-simulator-cronet.json b/ios/build/bots/chromium.mac/ios-simulator-cronet.json
index d17a13382..82854ef27 100644
--- a/ios/build/bots/chromium.mac/ios-simulator-cronet.json
+++ b/ios/build/bots/chromium.mac/ios-simulator-cronet.json
@@ -26,7 +26,7 @@
       "device type": "iPhone 6s",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "priority": 30
     },
     {
@@ -34,7 +34,7 @@
       "device type": "iPhone 6s",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "priority": 30
     },
     {
@@ -42,7 +42,7 @@
       "device type": "iPad Air 2",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "priority": 30
     },
     {
@@ -50,7 +50,7 @@
       "device type": "iPad Air 2",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome",
+      "pool": "chromium.tests",
       "priority": 30
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios-simulator-full-configs.json b/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
index 13c334d..cd729b9 100644
--- a/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
+++ b/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
@@ -23,7 +23,7 @@
       "device type": "iPad Air 2",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -31,7 +31,7 @@
       "device type": "iPhone X",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -39,7 +39,7 @@
       "device type": "iPad Air 2",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -47,7 +47,7 @@
       "device type": "iPhone 7",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -55,7 +55,7 @@
       "device type": "iPad Air 2",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -63,7 +63,7 @@
       "device type": "iPhone X",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -71,7 +71,7 @@
       "device type": "iPhone X",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -79,7 +79,7 @@
       "device type": "iPad Air 2",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios-simulator-noncq.json b/ios/build/bots/chromium.mac/ios-simulator-noncq.json
index 48468a1..42ffedd1 100644
--- a/ios/build/bots/chromium.mac/ios-simulator-noncq.json
+++ b/ios/build/bots/chromium.mac/ios-simulator-noncq.json
@@ -24,7 +24,7 @@
       "device type": "iPhone X",
       "os": "13.1",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -33,7 +33,7 @@
       "device type": "iPhone 7",
       "os": "13.1",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -42,7 +42,7 @@
       "device type": "iPad (6th generation)",
       "os": "13.1",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -51,7 +51,7 @@
       "device type": "iPad Air 2",
       "os": "13.1",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -60,7 +60,7 @@
       "device type": "iPhone X",
       "os": "12.2",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -69,7 +69,7 @@
       "device type": "iPhone 7",
       "os": "12.2",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -78,7 +78,7 @@
       "device type": "iPad (6th generation)",
       "os": "12.2",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -87,7 +87,7 @@
       "device type": "iPad Air 2",
       "os": "12.2",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios-simulator.json b/ios/build/bots/chromium.mac/ios-simulator.json
index 29d12d1..8fac72fe 100644
--- a/ios/build/bots/chromium.mac/ios-simulator.json
+++ b/ios/build/bots/chromium.mac/ios-simulator.json
@@ -24,7 +24,7 @@
       "device type": "iPhone 6s",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -32,7 +32,7 @@
       "device type": "iPhone 6s",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -40,7 +40,7 @@
       "device type": "iPhone 6s Plus",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -48,7 +48,7 @@
       "device type": "iPhone 6s",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -56,7 +56,7 @@
       "device type": "iPhone SE",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -64,7 +64,7 @@
       "device type": "iPhone 6s",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -72,7 +72,7 @@
       "device type": "iPad Air 2",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -80,7 +80,7 @@
       "device type": "iPhone 6s",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios-slimnav.json b/ios/build/bots/chromium.mac/ios-slimnav.json
index 3280d561..18f797d 100644
--- a/ios/build/bots/chromium.mac/ios-slimnav.json
+++ b/ios/build/bots/chromium.mac/ios-slimnav.json
@@ -22,7 +22,7 @@
       "device type": "iPhone 6s Plus",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -33,7 +33,7 @@
       "device type": "iPhone 6s",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -44,7 +44,7 @@
       "device type": "iPhone 6s",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -55,7 +55,7 @@
       "device type": "iPad Air 2",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -66,7 +66,7 @@
       "device type": "iPhone 6s",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -80,7 +80,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -92,7 +92,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -104,7 +104,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -116,7 +116,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -128,7 +128,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -140,7 +140,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -154,7 +154,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -166,7 +166,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -178,7 +178,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -190,7 +190,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -202,7 +202,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -214,7 +214,7 @@
       "os": "12.2",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -225,7 +225,7 @@
       "device type": "iPhone X",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -236,7 +236,7 @@
       "device type": "iPhone 6s Plus",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -247,7 +247,7 @@
       "device type": "iPhone 6s",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -258,7 +258,7 @@
       "device type": "iPhone 6s",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -269,7 +269,7 @@
       "device type": "iPad Air 2",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -280,7 +280,7 @@
       "device type": "iPhone 6s",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -294,7 +294,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -306,7 +306,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -318,7 +318,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -330,7 +330,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -342,7 +342,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -354,7 +354,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -368,7 +368,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -380,7 +380,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -392,7 +392,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -404,7 +404,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -416,7 +416,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -428,7 +428,7 @@
       "os": "13.1",
       "xctest": true,
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     },
     {
@@ -439,7 +439,7 @@
       "device type": "iPhone X",
       "os": "13.1",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "priority": 30
     }
   ]
diff --git a/ios/build/bots/chromium.mac/ios13-beta-simulator.json b/ios/build/bots/chromium.mac/ios13-beta-simulator.json
index d11b6e90..dfce1a2 100644
--- a/ios/build/bots/chromium.mac/ios13-beta-simulator.json
+++ b/ios/build/bots/chromium.mac/ios13-beta-simulator.json
@@ -25,7 +25,7 @@
       "device type": "iPhone X",
       "os": "13.1",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -34,7 +34,7 @@
       "device type": "iPhone 7",
       "os": "13.1",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -43,7 +43,7 @@
       "device type": "iPad (6th generation)",
       "os": "13.1",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -52,7 +52,7 @@
       "device type": "iPhone X",
       "os": "12.2",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -61,7 +61,7 @@
       "device type": "iPad (6th generation)",
       "os": "12.2",
       "xcode build version": "11a1027",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6"
     },
     {
@@ -70,7 +70,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "eg_cq_tests.json",
@@ -78,7 +78,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "eg_cq_tests.json",
@@ -86,7 +86,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "eg_tests.json",
@@ -94,7 +94,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "eg_tests.json",
@@ -102,7 +102,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -110,7 +110,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -118,7 +118,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     },
     {
       "include": "screen_size_dependent_tests.json",
@@ -126,7 +126,7 @@
       "os": "13.1",
       "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
-      "pool":"Chrome"
+      "pool":"chromium.tests"
     }
   ]
 }
diff --git a/ios/build/bots/chromium.mac/ios13-sdk-simulator.json b/ios/build/bots/chromium.mac/ios13-sdk-simulator.json
index 39c028e..658290d 100644
--- a/ios/build/bots/chromium.mac/ios13-sdk-simulator.json
+++ b/ios/build/bots/chromium.mac/ios13-sdk-simulator.json
@@ -25,7 +25,7 @@
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s Plus",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -33,7 +33,7 @@
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -41,7 +41,7 @@
       "include": "common_tests.json",
       "device type": "iPhone 6s",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -49,7 +49,7 @@
       "include": "eg_tests.json",
       "device type": "iPhone 7",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -57,7 +57,7 @@
       "include": "eg_tests.json",
       "device type": "iPad Air 2",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -65,7 +65,7 @@
       "include": "eg_tests.json",
       "device type": "iPhone X",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -73,7 +73,7 @@
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     },
@@ -81,7 +81,7 @@
       "include": "eg_cq_tests.json",
       "device type": "iPad Air 2",
       "os": "13.1",
-      "pool":"Chrome",
+      "pool":"chromium.tests",
       "host os": "Mac-10.14.6",
       "priority": 30
     }
diff --git a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
index 69b5da6..5dfc21a 100644
--- a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
+++ b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
@@ -24,21 +24,21 @@
       "device type": "iPhone 6s Plus",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome"
+      "pool": "chromium.tests"
     },
     {
       "include": "webrtc_tests.json",
       "device type": "iPhone 6s",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome"
+      "pool": "chromium.tests"
     },
     {
       "include": "webrtc_tests.json",
       "device type": "iPad Air 2",
       "os": "12.2",
       "host os": "Mac-10.14.6",
-      "pool": "Chrome"
+      "pool": "chromium.tests"
     }
   ]
 }
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
index fcde5cac..66485f1 100644
--- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -33,7 +33,6 @@
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #import "ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h"
 #import "ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.h"
 #include "ios/chrome/browser/ui/settings/personal_data_manager_finished_profile_tasks_waiter.h"
@@ -249,8 +248,6 @@
   // default.
   chrome_browser_state_->CreateWebDataService();
 
-  IOSSecurityStateTabHelper::CreateForWebState(web_state());
-
   autofill_agent_ = [[AutofillAgent alloc]
       initWithPrefService:chrome_browser_state_->GetPrefs()
                  webState:web_state()];
diff --git a/ios/chrome/browser/autofill/form_structure_browsertest.mm b/ios/chrome/browser/autofill/form_structure_browsertest.mm
index 62032b81..4eafa4d 100644
--- a/ios/chrome/browser/autofill/form_structure_browsertest.mm
+++ b/ios/chrome/browser/autofill/form_structure_browsertest.mm
@@ -25,7 +25,6 @@
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_paths.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #import "ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h"
 #include "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/chrome/browser/web/chrome_web_test.h"
@@ -141,8 +140,6 @@
   // Initialize it now as it may DCHECK if it is initialized during the test.
   AddressNormalizerFactory::GetInstance();
 
-  IOSSecurityStateTabHelper::CreateForWebState(web_state());
-
   autofill_agent_ = [[AutofillAgent alloc]
       initWithPrefService:chrome_browser_state_->GetPrefs()
                  webState:web_state()];
diff --git a/ios/chrome/browser/chrome_url_util.mm b/ios/chrome/browser/chrome_url_util.mm
index ca5b44ab..084d819 100644
--- a/ios/chrome/browser/chrome_url_util.mm
+++ b/ios/chrome/browser/chrome_url_util.mm
@@ -13,7 +13,6 @@
 #include "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/net/url_scheme_util.h"
-#include "net/url_request/url_request.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
@@ -40,13 +39,9 @@
 
 bool IsHandledProtocol(const std::string& scheme) {
   DCHECK_EQ(scheme, base::ToLowerASCII(scheme));
-  if (scheme == url::kAboutScheme)
-    return true;
-  if (scheme == url::kDataScheme)
-    return true;
-  if (scheme == kChromeUIScheme)
-    return true;
-  return net::URLRequest::IsHandledProtocol(scheme);
+  return (scheme == url::kHttpScheme || scheme == url::kHttpsScheme ||
+          scheme == url::kAboutScheme || scheme == url::kDataScheme ||
+          scheme == kChromeUIScheme);
 }
 
 @implementation ChromeAppConstants {
diff --git a/ios/chrome/browser/passwords/BUILD.gn b/ios/chrome/browser/passwords/BUILD.gn
index 6f3415a0..b4e8e9db 100644
--- a/ios/chrome/browser/passwords/BUILD.gn
+++ b/ios/chrome/browser/passwords/BUILD.gn
@@ -9,8 +9,6 @@
   sources = [
     "credential_manager.h",
     "credential_manager.mm",
-    "credential_manager_util.h",
-    "credential_manager_util.mm",
     "ios_chrome_password_manager_client.h",
     "ios_chrome_password_manager_client.mm",
     "ios_chrome_password_manager_driver.h",
@@ -164,7 +162,6 @@
   testonly = true
   sources = [
     "credential_manager_unittest.mm",
-    "credential_manager_util_unittest.cc",
     "js_credential_manager_unittest.mm",
     "password_controller_js_unittest.mm",
     "password_controller_unittest.mm",
diff --git a/ios/chrome/browser/passwords/credential_manager.mm b/ios/chrome/browser/passwords/credential_manager.mm
index 48d81c3..5ba5a4c 100644
--- a/ios/chrome/browser/passwords/credential_manager.mm
+++ b/ios/chrome/browser/passwords/credential_manager.mm
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
-#include "ios/chrome/browser/passwords/credential_manager_util.h"
+#include "components/password_manager/ios/credential_manager_util.h"
 #include "ios/chrome/browser/passwords/js_credential_manager.h"
 #include "ios/web/public/js_messaging/web_frame.h"
 
@@ -58,7 +58,7 @@
   }
   int promise_id = static_cast<int>(promise_id_double);
 
-  if (!WebStateContentIsSecureHtml(web_state_)) {
+  if (!password_manager::WebStateContentIsSecureHtml(web_state_)) {
     RejectCredentialPromiseWithInvalidStateError(
         web_state_, promise_id,
         base::ASCIIToUTF16(
@@ -82,7 +82,7 @@
       return;
     }
     bool include_passwords;
-    if (!ParseIncludePasswords(json, &include_passwords)) {
+    if (!password_manager::ParseIncludePasswords(json, &include_passwords)) {
       RejectCredentialPromiseWithTypeError(
           web_state_, promise_id,
           base::ASCIIToUTF16(
@@ -90,7 +90,7 @@
       return;
     }
     std::vector<GURL> federations;
-    if (!ParseFederations(json, &federations)) {
+    if (!password_manager::ParseFederations(json, &federations)) {
       RejectCredentialPromiseWithTypeError(
           web_state_, promise_id,
           base::ASCIIToUTF16(
diff --git a/ios/chrome/browser/passwords/credential_manager_unittest.mm b/ios/chrome/browser/passwords/credential_manager_unittest.mm
index 1f2dc8c5..4b245a2 100644
--- a/ios/chrome/browser/passwords/credential_manager_unittest.mm
+++ b/ios/chrome/browser/passwords/credential_manager_unittest.mm
@@ -12,9 +12,8 @@
 #include "components/password_manager/core/browser/leak_detection/leak_detection_check.h"
 #include "components/password_manager/core/browser/leak_detection/leak_detection_check_factory.h"
 #include "components/password_manager/core/browser/test_password_store.h"
-#include "ios/chrome/browser/passwords/credential_manager_util.h"
+#include "components/password_manager/ios/credential_manager_util.h"
 #import "ios/chrome/browser/passwords/test/test_password_manager_client.h"
-#include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #include "ios/web/public/navigation/navigation_item.h"
 #include "ios/web/public/navigation/navigation_manager.h"
 #include "ios/web/public/security/ssl_status.h"
@@ -77,9 +76,6 @@
 
   void SetUp() override {
     WebTestWithWebState::SetUp();
-
-    // Used indirectly by WebStateContentIsSecureHtml function.
-    IOSSecurityStateTabHelper::CreateForWebState(web_state());
   }
 
   // Updates SSLStatus on web_state()->GetNavigationManager()->GetVisibleItem()
@@ -674,13 +670,13 @@
   LoadHtml(@"<html></html>", GURL(kHttpsWebOrigin));
   UpdateSslStatus(net::CERT_STATUS_IS_EV, web::SECURITY_STYLE_AUTHENTICATED,
                   web::SSLStatus::NORMAL_CONTENT);
-  EXPECT_TRUE(WebStateContentIsSecureHtml(web_state()));
+  EXPECT_TRUE(password_manager::WebStateContentIsSecureHtml(web_state()));
 }
 
 // Tests that WebStateContentIsSecureHtml returns false for HTTP origin.
 TEST_F(WebStateContentIsSecureHtmlTest, HttpIsNotSecureContext) {
   LoadHtml(@"<html></html>", GURL(kHttpWebOrigin));
-  EXPECT_FALSE(WebStateContentIsSecureHtml(web_state()));
+  EXPECT_FALSE(password_manager::WebStateContentIsSecureHtml(web_state()));
 }
 
 // Tests that WebStateContentIsSecureHtml returns false for HTTPS origin with
@@ -689,7 +685,7 @@
   LoadHtml(@"<html></html>", GURL(kHttpsWebOrigin));
   UpdateSslStatus(net::CERT_STATUS_IS_EV, web::SECURITY_STYLE_AUTHENTICATED,
                   web::SSLStatus::DISPLAYED_INSECURE_CONTENT);
-  EXPECT_FALSE(WebStateContentIsSecureHtml(web_state()));
+  EXPECT_FALSE(password_manager::WebStateContentIsSecureHtml(web_state()));
 }
 
 // Tests that WebStateContentIsSecureHtml returns false for HTTPS origin with
@@ -698,30 +694,30 @@
   LoadHtml(@"<html></html>", GURL(kHttpsWebOrigin));
   UpdateSslStatus(net::CERT_STATUS_INVALID, web::SECURITY_STYLE_UNAUTHENTICATED,
                   web::SSLStatus::NORMAL_CONTENT);
-  EXPECT_FALSE(WebStateContentIsSecureHtml(web_state()));
+  EXPECT_FALSE(password_manager::WebStateContentIsSecureHtml(web_state()));
 }
 
 // Tests that data:// URI scheme is not accepted as secure context.
 TEST_F(WebStateContentIsSecureHtmlTest, DataUriSchemeIsNotSecureContext) {
   LoadHtml(@"<html></html>", GURL(kDataUriSchemeOrigin));
-  EXPECT_FALSE(WebStateContentIsSecureHtml(web_state()));
+  EXPECT_FALSE(password_manager::WebStateContentIsSecureHtml(web_state()));
 }
 
 // Tests that localhost is accepted as secure context.
 TEST_F(WebStateContentIsSecureHtmlTest, LocalhostIsSecureContext) {
   LoadHtml(@"<html></html>", GURL(kLocalhostOrigin));
-  EXPECT_TRUE(WebStateContentIsSecureHtml(web_state()));
+  EXPECT_TRUE(password_manager::WebStateContentIsSecureHtml(web_state()));
 }
 
 // Tests that file origin is accepted as secure context.
 TEST_F(WebStateContentIsSecureHtmlTest, FileIsSecureContext) {
   LoadHtml(@"<html></html>", GURL(kFileOrigin));
-  EXPECT_TRUE(WebStateContentIsSecureHtml(web_state()));
+  EXPECT_TRUE(password_manager::WebStateContentIsSecureHtml(web_state()));
 }
 
 // Tests that content must be HTML.
 TEST_F(WebStateContentIsSecureHtmlTest, ContentMustBeHtml) {
   // No HTML is loaded on purpose, so that web_state()->ContentIsHTML() will
   // return false.
-  EXPECT_FALSE(WebStateContentIsSecureHtml(web_state()));
+  EXPECT_FALSE(password_manager::WebStateContentIsSecureHtml(web_state()));
 }
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm b/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm
index b450db0..759b86b 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_password_manager_client.mm
@@ -21,11 +21,11 @@
 #include "components/password_manager/core/browser/password_requirements_service.h"
 #include "components/password_manager/core/browser/store_metrics_reporter.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/password_manager/ios/credential_manager_util.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/translate/core/browser/translate_manager.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/passwords/credential_manager_util.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #include "ios/chrome/browser/passwords/ios_password_requirements_service_factory.h"
 #include "ios/chrome/browser/passwords/password_manager_log_router_factory.h"
@@ -148,7 +148,7 @@
 }
 
 bool IOSChromePasswordManagerClient::IsMainFrameSecure() const {
-  return WebStateContentIsSecureHtml(delegate_.webState);
+  return password_manager::WebStateContentIsSecureHtml(delegate_.webState);
 }
 
 PrefService* IOSChromePasswordManagerClient::GetPrefs() const {
diff --git a/ios/chrome/browser/ssl/BUILD.gn b/ios/chrome/browser/ssl/BUILD.gn
index 850bff8..35a37322 100644
--- a/ios/chrome/browser/ssl/BUILD.gn
+++ b/ios/chrome/browser/ssl/BUILD.gn
@@ -11,12 +11,8 @@
     "captive_portal_metrics.h",
     "captive_portal_metrics_tab_helper.h",
     "captive_portal_metrics_tab_helper.mm",
-    "insecure_input_tab_helper.h",
-    "insecure_input_tab_helper.mm",
     "ios_captive_portal_blocking_page.h",
     "ios_captive_portal_blocking_page.mm",
-    "ios_security_state_tab_helper.h",
-    "ios_security_state_tab_helper.mm",
     "ios_ssl_blocking_page.h",
     "ios_ssl_blocking_page.mm",
     "ios_ssl_error_handler.h",
@@ -66,7 +62,6 @@
   testonly = true
   sources = [
     "ios_captive_portal_blocking_page_unittest.mm",
-    "ios_security_state_tab_helper_unittest.mm",
     "ios_ssl_error_handler_unittest.mm",
     "ios_ssl_error_tab_helper_unittest.mm",
   ]
diff --git a/ios/chrome/browser/ssl/ios_security_state_tab_helper.h b/ios/chrome/browser/ssl/ios_security_state_tab_helper.h
deleted file mode 100644
index cd518d8..0000000
--- a/ios/chrome/browser/ssl/ios_security_state_tab_helper.h
+++ /dev/null
@@ -1,41 +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 IOS_CHROME_BROWSER_SSL_IOS_SECURITY_STATE_TAB_HELPER_H_
-#define IOS_CHROME_BROWSER_SSL_IOS_SECURITY_STATE_TAB_HELPER_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "components/security_state/core/security_state.h"
-#import "ios/web/public/web_state_user_data.h"
-
-namespace web {
-class WebState;
-}  // namespace web
-
-// Tab helper that provides the page's security status. Uses a WebState to
-// provide a security_state::GetSecurityLevel() with the relevant
-// VisibleSecurityState information.
-class IOSSecurityStateTabHelper
-    : public web::WebStateUserData<IOSSecurityStateTabHelper> {
- public:
-  ~IOSSecurityStateTabHelper() override;
-
-  security_state::SecurityLevel GetSecurityLevel() const;
-  std::unique_ptr<security_state::VisibleSecurityState>
-  GetVisibleSecurityState() const;
-
- private:
-  explicit IOSSecurityStateTabHelper(web::WebState* web_state);
-  friend class web::WebStateUserData<IOSSecurityStateTabHelper>;
-
-  web::WebState* web_state_;
-
-  WEB_STATE_USER_DATA_KEY_DECL();
-
-  DISALLOW_COPY_AND_ASSIGN(IOSSecurityStateTabHelper);
-};
-
-#endif  // IOS_CHROME_BROWSER_SSL_IOS_SECURITY_STATE_TAB_HELPER_H_
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn
index e07ce23..a5e3fe1 100644
--- a/ios/chrome/browser/tabs/BUILD.gn
+++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -53,6 +53,7 @@
     "//components/language/ios/browser",
     "//components/navigation_metrics",
     "//components/profile_metrics",
+    "//components/security_state/ios",
     "//components/sessions",
     "//components/strings",
     "//ios/chrome/app/strings",
diff --git a/ios/chrome/browser/tabs/tab_helper_util.mm b/ios/chrome/browser/tabs/tab_helper_util.mm
index 55032fb..90a5e2f 100644
--- a/ios/chrome/browser/tabs/tab_helper_util.mm
+++ b/ios/chrome/browser/tabs/tab_helper_util.mm
@@ -14,6 +14,7 @@
 #import "components/history/ios/browser/web_state_top_sites_observer.h"
 #include "components/keyed_service/core/service_access_type.h"
 #import "components/language/ios/browser/ios_language_detection_tab_helper.h"
+#import "components/security_state/ios/insecure_input_tab_helper.h"
 #import "ios/chrome/browser/autofill/autofill_tab_helper.h"
 #import "ios/chrome/browser/autofill/form_suggestion_tab_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
@@ -40,8 +41,6 @@
 #import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
 #include "ios/chrome/browser/ssl/captive_portal_features.h"
 #import "ios/chrome/browser/ssl/captive_portal_metrics_tab_helper.h"
-#import "ios/chrome/browser/ssl/insecure_input_tab_helper.h"
-#import "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #import "ios/chrome/browser/store_kit/store_kit_tab_helper.h"
 #import "ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h"
 #import "ios/chrome/browser/translate/chrome_ios_translate_client.h"
@@ -81,7 +80,6 @@
   VoiceSearchNavigationTabHelper::CreateForWebState(web_state);
   IOSChromeSyncedTabDelegate::CreateForWebState(web_state);
   InfoBarManagerImpl::CreateForWebState(web_state);
-  IOSSecurityStateTabHelper::CreateForWebState(web_state);
   BlockedPopupTabHelper::CreateForWebState(web_state);
   FindTabHelper::CreateForWebState(web_state);
   U2FTabHelper::CreateForWebState(web_state);
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn
index ed367076..e13c4fe7 100644
--- a/ios/chrome/browser/ui/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -24,6 +24,7 @@
     "//components/leveldb_proto",
     "//components/password_manager/core/browser",
     "//components/prefs",
+    "//components/security_state/ios",
     "//components/strings",
     "//components/translate/core/browser:browser",
     "//google_apis",
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
index e88470f..603c3e8 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -22,6 +22,7 @@
 #include "components/autofill/ios/browser/autofill_util.h"
 #include "components/infobars/core/infobar.h"
 #include "components/keyed_service/core/service_access_type.h"
+#include "components/security_state/ios/security_state_utils.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/translate/core/browser/translate_manager.h"
 #include "ios/chrome/browser/application_context.h"
@@ -34,7 +35,6 @@
 #include "ios/chrome/browser/infobars/infobar_utils.h"
 #include "ios/chrome/browser/metrics/ukm_url_recorder.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
-#include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
 #include "ios/chrome/browser/ui/autofill/card_name_fix_flow_view_bridge.h"
@@ -177,14 +177,7 @@
 
 security_state::SecurityLevel
 ChromeAutofillClientIOS::GetSecurityLevelForUmaHistograms() {
-  auto* ios_security_state_tab_helper =
-      IOSSecurityStateTabHelper::FromWebState(web_state_);
-
-  // If there is no helper, return SECURITY_LEVEL_COUNT which won't be logged.
-  if (!ios_security_state_tab_helper)
-    return security_state::SecurityLevel::SECURITY_LEVEL_COUNT;
-
-  return ios_security_state_tab_helper->GetSecurityLevel();
+  return security_state::GetSecurityLevelForWebState(web_state_);
 }
 
 std::string ChromeAutofillClientIOS::GetPageLanguage() const {
diff --git a/ios/chrome/browser/ui/location_bar/BUILD.gn b/ios/chrome/browser/ui/location_bar/BUILD.gn
index a9317e7..2d4b9b5 100644
--- a/ios/chrome/browser/ui/location_bar/BUILD.gn
+++ b/ios/chrome/browser/ui/location_bar/BUILD.gn
@@ -30,6 +30,7 @@
     "//components/omnibox/browser",
     "//components/open_from_clipboard:",
     "//components/search_engines",
+    "//components/security_state/ios",
     "//components/strings",
     "//components/variations/net",
     "//ios/chrome/app/strings",
@@ -108,6 +109,7 @@
     "//base",
     "//components/omnibox/browser",
     "//components/prefs",
+    "//components/security_state/ios",
     "//ios/chrome/browser",
     "//ios/chrome/browser/autocomplete",
     "//ios/chrome/browser/browser_state",
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
index 4b7fb18a..e4485f95 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
@@ -7,6 +7,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/omnibox/browser/location_bar_model.h"
+#include "components/security_state/ios/security_state_utils.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/overlays/public/overlay_presenter.h"
 #import "ios/chrome/browser/overlays/public/overlay_presenter_observer_bridge.h"
@@ -15,7 +16,6 @@
 #import "ios/chrome/browser/overlays/public/web_content_area/http_auth_overlay.h"
 #import "ios/chrome/browser/search_engines/search_engine_observer_bridge.h"
 #import "ios/chrome/browser/search_engines/search_engines_util.h"
-#include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_consumer.h"
 #import "ios/chrome/browser/ui/ntp/ntp_util.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_util.h"
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_model_delegate_ios.mm b/ios/chrome/browser/ui/location_bar/location_bar_model_delegate_ios.mm
index 108b7a8..7c425ab 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_model_delegate_ios.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_model_delegate_ios.mm
@@ -9,13 +9,13 @@
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/prefs/pref_service.h"
+#include "components/security_state/ios/security_state_utils.h"
 #include "ios/chrome/browser/autocomplete/autocomplete_scheme_classifier_impl.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/reading_list/features.h"
 #import "ios/chrome/browser/reading_list/offline_page_tab_helper.h"
-#include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/web/public/navigation/navigation_item.h"
 #import "ios/web/public/navigation/navigation_manager.h"
@@ -84,25 +84,13 @@
 security_state::SecurityLevel LocationBarModelDelegateIOS::GetSecurityLevel()
     const {
   web::WebState* web_state = GetActiveWebState();
-  // If there is no active WebState (which can happen during toolbar
-  // initialization), assume no security style.
-  if (!web_state) {
-    return security_state::NONE;
-  }
-  auto* client = IOSSecurityStateTabHelper::FromWebState(web_state);
-  return client->GetSecurityLevel();
+  return security_state::GetSecurityLevelForWebState(web_state);
 }
 
 std::unique_ptr<security_state::VisibleSecurityState>
 LocationBarModelDelegateIOS::GetVisibleSecurityState() const {
   web::WebState* web_state = GetActiveWebState();
-  // If there is no active WebState (which can happen during toolbar
-  // initialization), assume no security style.
-  if (!web_state) {
-    return std::make_unique<security_state::VisibleSecurityState>();
-  }
-  auto* client = IOSSecurityStateTabHelper::FromWebState(web_state);
-  return client->GetVisibleSecurityState();
+  return security_state::GetVisibleSecurityStateForWebState(web_state);
 }
 
 scoped_refptr<net::X509Certificate>
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 2426085..7002767 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -319,10 +319,32 @@
   ]
 }
 
-source_set("eg_tests") {
+source_set("eg_test_support") {
+  defines = [ "CHROME_EARL_GREY_1" ]
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
+    "block_popups_app_interface.h",
+    "block_popups_app_interface.mm",
+  ]
+  deps = [
+    "//base",
+    "//components/content_settings/core/browser",
+    "//components/content_settings/core/common",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/content_settings",
+    "//ios/chrome/test/app:test_support",
+  ]
+}
+
+source_set("eg_tests") {
+  defines = [ "CHROME_EARL_GREY_1" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+  sources = [
     "block_popups_egtest.mm",
     "settings_egtest.mm",
     "signin_settings_egtest.mm",
@@ -330,6 +352,7 @@
   ]
 
   deps = [
+    ":eg_test_support",
     ":settings",
     "//base",
     "//base/test:test_support",
@@ -375,8 +398,60 @@
     "//ui/base",
     "//url",
   ]
-  libs = [
-    "UIKit.framework",
-    "XCTest.framework",
+  libs = [ "UIKit.framework" ]
+}
+
+source_set("eg_app_support+eg2") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "block_popups_app_interface.h",
+    "block_popups_app_interface.mm",
+  ]
+  deps = [
+    "//base",
+    "//components/content_settings/core/browser",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/content_settings",
+    "//ios/chrome/test/app:test_support",
+  ]
+  public_deps = [
+    "//components/content_settings/core/common",
+  ]
+}
+
+source_set("eg_test_support+eg2") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "block_popups_app_interface.h",
+  ]
+  public_deps = [
+    "//components/content_settings/core/common",
+  ]
+}
+
+source_set("eg2_tests") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+  sources = [
+    "block_popups_egtest.mm",
+  ]
+  deps = [
+    ":eg_test_support+eg2",
+    "//base",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
+    "//ios/testing/earl_grey:eg_test_support+eg2",
+    "//ios/third_party/earl_grey2:test_lib",
+    "//ios/web/public/test/http_server",
+    "//ui/base",
+    "//url",
   ]
 }
diff --git a/ios/chrome/browser/ui/settings/block_popups_app_interface.h b/ios/chrome/browser/ui/settings/block_popups_app_interface.h
new file mode 100644
index 0000000..cd35e8bb
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/block_popups_app_interface.h
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_BLOCK_POPUPS_APP_INTERFACE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_BLOCK_POPUPS_APP_INTERFACE_H_
+
+#import <Foundation/Foundation.h>
+
+#include "components/content_settings/core/common/content_settings.h"
+
+// BlockPopupsAppInterface provides app-side helpers for BlockPopupsTest.
+@interface BlockPopupsAppInterface : NSObject
+
+// Sets the popup content setting policy for the given |pattern|.
++ (void)setPopupPolicy:(ContentSetting)policy forPattern:(NSString*)pattern;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_BLOCK_POPUPS_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/ui/settings/block_popups_app_interface.mm b/ios/chrome/browser/ui/settings/block_popups_app_interface.mm
new file mode 100644
index 0000000..f4ded76
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/block_popups_app_interface.mm
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/settings/block_popups_app_interface.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
+#import "ios/chrome/test/app/chrome_test_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation BlockPopupsAppInterface
+
++ (void)setPopupPolicy:(ContentSetting)policy forPattern:(NSString*)pattern {
+  ios::ChromeBrowserState* browserState =
+      chrome_test_util::GetOriginalBrowserState();
+
+  ContentSettingsPattern exceptionPattern =
+      ContentSettingsPattern::FromString(base::SysNSStringToUTF8(pattern));
+  ios::HostContentSettingsMapFactory::GetForBrowserState(browserState)
+      ->SetContentSettingCustomScope(
+          exceptionPattern, ContentSettingsPattern::Wildcard(),
+          CONTENT_SETTINGS_TYPE_POPUPS, std::string(), policy);
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/block_popups_egtest.mm b/ios/chrome/browser/ui/settings/block_popups_egtest.mm
index 26e9aaf..57ef265 100644
--- a/ios/chrome/browser/ui/settings/block_popups_egtest.mm
+++ b/ios/chrome/browser/ui/settings/block_popups_egtest.mm
@@ -2,22 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import <EarlGrey/EarlGrey.h>
 #import <UIKit/UIKit.h>
 #import <XCTest/XCTest.h>
 
 #include "base/strings/sys_string_conversions.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
+#import "ios/chrome/browser/ui/settings/block_popups_app_interface.h"
 #include "ios/chrome/grit/ios_strings.h"
-#import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_actions.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "ios/chrome/test/earl_grey/scoped_block_popups_pref.h"
+#import "ios/testing/earl_grey/earl_grey_test.h"
 #import "ios/web/public/test/http_server/http_server.h"
 #include "ios/web/public/test/http_server/http_server_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
@@ -27,8 +24,11 @@
 #error "This file requires ARC support."
 #endif
 
+#if defined(CHROME_EARL_GREY_2)
+GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(BlockPopupsAppInterface)
+#endif  // defined(CHROME_EARL_GREY_2)
+
 using chrome_test_util::ContentSettingsButton;
-using chrome_test_util::GetOriginalBrowserState;
 using chrome_test_util::SettingsDoneButton;
 using chrome_test_util::SettingsMenuBackButton;
 
@@ -57,30 +57,19 @@
 // list for as long as this object is in scope.
 class ScopedBlockPopupsException {
  public:
-  ScopedBlockPopupsException(const std::string& pattern) : pattern_(pattern) {
-    SetException(pattern_, CONTENT_SETTING_ALLOW);
+  ScopedBlockPopupsException(const std::string& pattern)
+      : pattern_(base::SysUTF8ToNSString(pattern)) {
+    [BlockPopupsAppInterface setPopupPolicy:CONTENT_SETTING_ALLOW
+                                 forPattern:pattern_];
   }
   ~ScopedBlockPopupsException() {
-    SetException(pattern_, CONTENT_SETTING_DEFAULT);
+    [BlockPopupsAppInterface setPopupPolicy:CONTENT_SETTING_DEFAULT
+                                 forPattern:pattern_];
   }
 
  private:
-  // Adds an exception for the given |pattern|.  If |setting| is
-  // CONTENT_SETTING_DEFAULT, removes the existing exception instead.
-  void SetException(const std::string& pattern, ContentSetting setting) {
-    ios::ChromeBrowserState* browserState =
-        chrome_test_util::GetOriginalBrowserState();
-
-    ContentSettingsPattern exception_pattern =
-        ContentSettingsPattern::FromString(pattern);
-    ios::HostContentSettingsMapFactory::GetForBrowserState(browserState)
-        ->SetContentSettingCustomScope(
-            exception_pattern, ContentSettingsPattern::Wildcard(),
-            CONTENT_SETTINGS_TYPE_POPUPS, std::string(), setting);
-  }
-
   // The exception pattern that this object is managing.
-  std::string pattern_;
+  NSString* pattern_;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedBlockPopupsException);
 };
@@ -164,7 +153,7 @@
   // via async JS, so the infobar may not open immediately.
   [ChromeEarlGrey executeJavaScript:kOpenPopupScript];
 
-  [[GREYCondition
+  BOOL infobarVisible = [[GREYCondition
       conditionWithName:@"Wait for blocked popups infobar to show"
                   block:^BOOL {
                     NSError* error = nil;
@@ -176,6 +165,7 @@
                                     error:&error];
                     return error == nil;
                   }] waitWithTimeout:4.0];
+  GREYAssertTrue(infobarVisible, @"Infobar did not appear");
   [ChromeEarlGrey waitForMainTabCount:1];
 }
 
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 5df8852..4eeb3bc 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -384,6 +384,7 @@
     "//ios/chrome/browser/ui/recent_tabs:recent_tabs_ui_constants",
     "//ios/chrome/browser/ui/safe_mode",
     "//ios/chrome/browser/ui/safe_mode:eg_app_support+eg2",
+    "//ios/chrome/browser/ui/settings:eg_app_support+eg2",
     "//ios/chrome/browser/ui/settings:settings",
     "//ios/chrome/browser/ui/settings/autofill",
     "//ios/chrome/browser/ui/settings/autofill:feature_flags",
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index f3a7a0c..3d45283 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -53,6 +53,7 @@
   xcode_test_application_name = "ios_chrome_eg2tests"
 
   deps = [
+    "//ios/chrome/browser/ui/settings:eg2_tests",
     "//ios/chrome/browser/ui/settings/autofill:eg2_tests",
   ]
 }
diff --git a/ios/web_view/internal/passwords/cwv_password_controller.mm b/ios/web_view/internal/passwords/cwv_password_controller.mm
index dbf4ff8..e177d44 100644
--- a/ios/web_view/internal/passwords/cwv_password_controller.mm
+++ b/ios/web_view/internal/passwords/cwv_password_controller.mm
@@ -190,6 +190,10 @@
                    : nullptr;
 }
 
+- (web::WebState*)webState {
+  return _webState;
+}
+
 - (password_manager::PasswordManager*)passwordManager {
   return _passwordManager.get();
 }
diff --git a/ios/web_view/internal/passwords/web_view_password_manager_client.h b/ios/web_view/internal/passwords/web_view_password_manager_client.h
index 267cee6..6119c5d89 100644
--- a/ios/web_view/internal/passwords/web_view_password_manager_client.h
+++ b/ios/web_view/internal/passwords/web_view_password_manager_client.h
@@ -18,12 +18,16 @@
 
 namespace ios_web_view {
 class WebViewBrowserState;
-}
+}  // namespace ios_web_view
 
 namespace password_manager {
 class PasswordFormManagerForUI;
 class PasswordManagerDriver;
-}
+}  // namespace password_manager
+
+namespace web {
+class WebState;
+}  // namespace web
 
 @protocol CWVPasswordManagerClientDelegate
 
@@ -40,6 +44,7 @@
     (std::unique_ptr<autofill::PasswordForm>)formSignedIn;
 
 @property(readonly, nonatomic) ios_web_view::WebViewBrowserState* browserState;
+@property(readonly, nonatomic) web::WebState* webState;
 
 @property(readonly, nonatomic)
     password_manager::PasswordManager* passwordManager;
@@ -86,6 +91,7 @@
   const password_manager::PasswordManager* GetPasswordManager() const override;
   const password_manager::PasswordFeatureManager* GetPasswordFeatureManager()
       const override;
+  bool IsMainFrameSecure() const override;
   PrefService* GetPrefs() const override;
   password_manager::PasswordStore* GetProfilePasswordStore() const override;
   password_manager::PasswordStore* GetAccountPasswordStore() const override;
diff --git a/ios/web_view/internal/passwords/web_view_password_manager_client.mm b/ios/web_view/internal/passwords/web_view_password_manager_client.mm
index 4dbd447..f861e386 100644
--- a/ios/web_view/internal/passwords/web_view_password_manager_client.mm
+++ b/ios/web_view/internal/passwords/web_view_password_manager_client.mm
@@ -16,6 +16,8 @@
 #include "components/password_manager/core/browser/password_manager_driver.h"
 #include "components/password_manager/core/browser/password_manager_util.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/password_manager/ios/credential_manager_util.h"
+#import "ios/web/public/web_state.h"
 #include "ios/web_view/internal/app/application_context.h"
 #import "ios/web_view/internal/passwords/web_view_password_manager_log_router_factory.h"
 #include "ios/web_view/internal/passwords/web_view_password_store_factory.h"
@@ -123,6 +125,10 @@
   return &password_feature_manager_;
 }
 
+bool WebViewPasswordManagerClient::IsMainFrameSecure() const {
+  return password_manager::WebStateContentIsSecureHtml(delegate_.webState);
+}
+
 PrefService* WebViewPasswordManagerClient::GetPrefs() const {
   return delegate_.browserState->GetPrefs();
 }
diff --git a/ios/web_view/internal/web_view_web_client.mm b/ios/web_view/internal/web_view_web_client.mm
index d308a05..9b08b49 100644
--- a/ios/web_view/internal/web_view_web_client.mm
+++ b/ios/web_view/internal/web_view_web_client.mm
@@ -98,7 +98,7 @@
 
 void WebViewWebClient::AllowCertificateError(
     web::WebState* web_state,
-    int cert_error,
+    int net_error,
     const net::SSLInfo& ssl_info,
     const GURL& request_url,
     bool overridable,
@@ -110,10 +110,10 @@
   SEL selector = @selector
       (webView:didFailNavigationWithSSLError:overridable:decisionHandler:);
   if ([web_view.navigationDelegate respondsToSelector:selector]) {
-    CWVCertStatus cert_status = CWVCertStatusFromNetCertStatus(
-        net::MapNetErrorToCertStatus(cert_error));
+    CWVCertStatus cert_status =
+        CWVCertStatusFromNetCertStatus(ssl_info.cert_status);
     ssl_errors::ErrorInfo error_info = ssl_errors::ErrorInfo::CreateError(
-        ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error),
+        ssl_errors::ErrorInfo::NetErrorToErrorType(net_error),
         ssl_info.cert.get(), request_url);
     NSString* error_description =
         base::SysUTF16ToNSString(error_info.short_description());
diff --git a/media/blink/watch_time_component_unittest.cc b/media/blink/watch_time_component_unittest.cc
index 26b93c9..3c627dfc 100644
--- a/media/blink/watch_time_component_unittest.cc
+++ b/media/blink/watch_time_component_unittest.cc
@@ -21,12 +21,13 @@
   MOCK_METHOD2(RecordWatchTime, void(WatchTimeKey, base::TimeDelta));
   MOCK_METHOD1(FinalizeWatchTime, void(const std::vector<WatchTimeKey>&));
   MOCK_METHOD1(OnError, void(PipelineStatus));
-  MOCK_METHOD1(UpdateUnderflowCount, void(int32_t count));
-  MOCK_METHOD1(
-      UpdateSecondaryProperties,
-      void(mojom::SecondaryPlaybackPropertiesPtr secondary_properties));
   MOCK_METHOD1(SetAutoplayInitiated, void(bool));
   MOCK_METHOD1(OnDurationChanged, void(base::TimeDelta));
+  MOCK_METHOD2(UpdateVideoDecodeStats, void(uint32_t, uint32_t));
+  MOCK_METHOD1(UpdateUnderflowCount, void(int32_t));
+  MOCK_METHOD2(UpdateUnderflowDuration, void(int32_t, base::TimeDelta));
+  MOCK_METHOD1(UpdateSecondaryProperties,
+               void(mojom::SecondaryPlaybackPropertiesPtr));
 };
 
 class WatchTimeComponentTest : public testing::Test {
diff --git a/media/blink/watch_time_reporter.cc b/media/blink/watch_time_reporter.cc
index 70d81fb..018546f6 100644
--- a/media/blink/watch_time_reporter.cc
+++ b/media/blink/watch_time_reporter.cc
@@ -4,8 +4,13 @@
 
 #include "media/blink/watch_time_reporter.h"
 
+#include <numeric>
+
 #include "base/bind.h"
 #include "base/power_monitor/power_monitor.h"
+#include "base/time/time.h"
+#include "media/base/pipeline_status.h"
+#include "media/base/timestamp_constants.h"
 #include "media/base/watch_time_keys.h"
 
 namespace media {
@@ -43,6 +48,7 @@
     mojom::PlaybackPropertiesPtr properties,
     const gfx::Size& natural_size,
     GetMediaTimeCB get_media_time_cb,
+    GetPipelineStatsCB get_pipeline_stats_cb,
     mojom::MediaMetricsProvider* provider,
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     const base::TickClock* tick_clock)
@@ -51,6 +57,7 @@
                         false /* is_muted */,
                         natural_size,
                         std::move(get_media_time_cb),
+                        std::move(get_pipeline_stats_cb),
                         provider,
                         task_runner,
                         tick_clock) {}
@@ -61,6 +68,7 @@
     bool is_muted,
     const gfx::Size& natural_size,
     GetMediaTimeCB get_media_time_cb,
+    GetPipelineStatsCB get_pipeline_stats_cb,
     mojom::MediaMetricsProvider* provider,
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     const base::TickClock* tick_clock)
@@ -68,9 +76,11 @@
       is_background_(is_background),
       is_muted_(is_muted),
       get_media_time_cb_(std::move(get_media_time_cb)),
+      get_pipeline_stats_cb_(std::move(get_pipeline_stats_cb)),
       reporting_timer_(tick_clock),
       natural_size_(natural_size) {
   DCHECK(get_media_time_cb_);
+  DCHECK(get_pipeline_stats_cb_);
   DCHECK(properties_->has_audio || properties_->has_video);
   DCHECK_EQ(is_background, properties_->is_background);
 
@@ -110,7 +120,8 @@
   prop_copy->is_background = true;
   background_reporter_.reset(new WatchTimeReporter(
       std::move(prop_copy), true /* is_background */, false /* is_muted */,
-      natural_size_, get_media_time_cb_, provider, task_runner, tick_clock));
+      natural_size_, get_media_time_cb_, get_pipeline_stats_cb_, provider,
+      task_runner, tick_clock));
 
   // Muted watch time is only reported for audio+video playback.
   if (!properties_->has_video || !properties_->has_audio)
@@ -122,7 +133,8 @@
   prop_copy->is_muted = true;
   muted_reporter_.reset(new WatchTimeReporter(
       std::move(prop_copy), false /* is_background */, true /* is_muted */,
-      natural_size_, get_media_time_cb_, provider, task_runner, tick_clock));
+      natural_size_, get_media_time_cb_, get_pipeline_stats_cb_, provider,
+      task_runner, tick_clock));
 }
 
 WatchTimeReporter::~WatchTimeReporter() {
@@ -236,10 +248,35 @@
   if (!reporting_timer_.IsRunning())
     return;
 
+  if (!pending_underflow_events_.empty())
+    DCHECK_NE(pending_underflow_events_.back().duration, kNoTimestamp);
+
   // In the event of a pending finalize, we don't want to count underflow events
   // that occurred after the finalize time. Yet if the finalize is canceled we
   // want to ensure they are all recorded.
-  pending_underflow_events_.push_back(get_media_time_cb_.Run());
+  pending_underflow_events_.push_back(
+      {false, get_media_time_cb_.Run(), kNoTimestamp});
+}
+
+void WatchTimeReporter::OnUnderflowComplete(base::TimeDelta elapsed) {
+  if (background_reporter_)
+    background_reporter_->OnUnderflowComplete(elapsed);
+  if (muted_reporter_)
+    muted_reporter_->OnUnderflowComplete(elapsed);
+
+  if (!reporting_timer_.IsRunning())
+    return;
+
+  // Drop this underflow completion if we don't have a corresponding underflow
+  // start event; this can happen if a finalize occurs between the underflow and
+  // the completion.
+  if (pending_underflow_events_.empty())
+    return;
+
+  // There should only ever be one outstanding underflow, so stick the duration
+  // in the last underflow event.
+  DCHECK_EQ(pending_underflow_events_.back().duration, kNoTimestamp);
+  pending_underflow_events_.back().duration = elapsed;
 }
 
 void WatchTimeReporter::OnNativeControlsEnabled() {
@@ -375,9 +412,12 @@
   if (!should_start)
     return;
 
-  underflow_count_ = 0;
-  pending_underflow_events_.clear();
+  if (properties_->has_video) {
+    initial_stats_ = get_pipeline_stats_cb_.Run();
+    last_stats_ = PipelineStatistics();
+  }
 
+  ResetUnderflowState();
   base_component_->OnReportingStarted(start_timestamp);
   power_component_->OnReportingStarted(start_timestamp);
 
@@ -423,19 +463,64 @@
 
   // Pass along any underflow events which have occurred since the last report.
   if (!pending_underflow_events_.empty()) {
-    if (!base_component_->NeedsFinalize()) {
-      // The maximum value here per period is ~5 events, so int cast is okay.
-      underflow_count_ += static_cast<int>(pending_underflow_events_.size());
-    } else {
-      // Only count underflow events prior to finalize.
-      for (auto& ts : pending_underflow_events_) {
-        if (ts <= base_component_->end_timestamp())
-          underflow_count_++;
+    const int last_underflow_count = total_underflow_count_;
+    const int last_completed_underflow_count = total_completed_underflow_count_;
+
+    for (auto& ufe : pending_underflow_events_) {
+      // Since the underflow occurred after finalize, ignore the event and mark
+      // it for deletion.
+      if (ufe.timestamp > current_timestamp) {
+        ufe.reported = true;
+        ufe.duration = base::TimeDelta();
+        continue;
+      }
+
+      if (!ufe.reported) {
+        ufe.reported = true;
+        ++total_underflow_count_;
+      }
+
+      // Drop any rebuffer completions that took more than a minute. For our
+      // purposes these are considered as timeouts. We want a maximum since
+      // rebuffer duration is in real time and not media time, which means if
+      // the rebuffer spans a suspend/resume the time can be arbitrarily long.
+      constexpr base::TimeDelta kMaximumRebufferDuration =
+          base::TimeDelta::FromMinutes(1);
+      if (ufe.duration != kNoTimestamp &&
+          ufe.duration <= kMaximumRebufferDuration) {
+        ++total_completed_underflow_count_;
+        total_underflow_duration_ += ufe.duration;
       }
     }
 
-    recorder_->UpdateUnderflowCount(underflow_count_);
-    pending_underflow_events_.clear();
+    base::EraseIf(pending_underflow_events_, [](const UnderflowEvent& ufe) {
+      return ufe.reported && ufe.duration != kNoTimestamp;
+    });
+
+    if (last_underflow_count != total_underflow_count_)
+      recorder_->UpdateUnderflowCount(total_underflow_count_);
+    if (last_completed_underflow_count != total_completed_underflow_count_) {
+      recorder_->UpdateUnderflowDuration(total_completed_underflow_count_,
+                                         total_underflow_duration_);
+    }
+  }
+
+  if (properties_->has_video) {
+    auto stats = get_pipeline_stats_cb_.Run();
+    DCHECK_GE(stats.video_frames_decoded, initial_stats_.video_frames_decoded);
+    DCHECK_GE(stats.video_frames_dropped, initial_stats_.video_frames_dropped);
+
+    // Offset the stats based on where they were when we started reporting.
+    stats.video_frames_decoded -= initial_stats_.video_frames_decoded;
+    stats.video_frames_dropped -= initial_stats_.video_frames_dropped;
+
+    // Only send updates.
+    if (last_stats_.video_frames_decoded != stats.video_frames_decoded ||
+        last_stats_.video_frames_dropped != stats.video_frames_dropped) {
+      recorder_->UpdateVideoDecodeStats(stats.video_frames_decoded,
+                                        stats.video_frames_dropped);
+      last_stats_ = stats;
+    }
   }
 
   // Record watch time for all components.
@@ -473,10 +558,16 @@
   recorder_->FinalizeWatchTime({});
 
   // Stop the timer if this is supposed to be our last tick.
-  underflow_count_ = 0;
+  ResetUnderflowState();
   reporting_timer_.Stop();
 }
 
+void WatchTimeReporter::ResetUnderflowState() {
+  total_underflow_count_ = total_completed_underflow_count_ = 0;
+  total_underflow_duration_ = base::TimeDelta();
+  pending_underflow_events_.clear();
+}
+
 #define NORMAL_KEY(key)                                                     \
   ((properties_->has_video && properties_->has_audio)                       \
        ? (is_background_ ? WatchTimeKey::kAudioVideoBackground##key         \
diff --git a/media/blink/watch_time_reporter.h b/media/blink/watch_time_reporter.h
index 122e94e..26d68491 100644
--- a/media/blink/watch_time_reporter.h
+++ b/media/blink/watch_time_reporter.h
@@ -59,6 +59,7 @@
  public:
   using DisplayType = blink::WebMediaPlayer::DisplayType;
   using GetMediaTimeCB = base::RepeatingCallback<base::TimeDelta(void)>;
+  using GetPipelineStatsCB = base::Callback<PipelineStatistics(void)>;
 
   // Constructor for the reporter; all requested metadata should be fully known
   // before attempting construction as incorrect values will result in the wrong
@@ -82,6 +83,7 @@
   WatchTimeReporter(mojom::PlaybackPropertiesPtr properties,
                     const gfx::Size& natural_size,
                     GetMediaTimeCB get_media_time_cb,
+                    GetPipelineStatsCB get_pipeline_stats_cb,
                     mojom::MediaMetricsProvider* provider,
                     scoped_refptr<base::SequencedTaskRunner> task_runner,
                     const base::TickClock* tick_clock = nullptr);
@@ -121,6 +123,7 @@
   // finalized the total watch time for a given category will be divided by the
   // number of rebuffering events. Reset to zero after a finalize event.
   void OnUnderflow();
+  void OnUnderflowComplete(base::TimeDelta elapsed);
 
   // These methods are used to ensure that the watch time is reported relative
   // to whether the media is using native controls.
@@ -159,6 +162,7 @@
                     bool is_muted,
                     const gfx::Size& natural_size,
                     GetMediaTimeCB get_media_time_cb,
+                    GetPipelineStatsCB get_pipeline_stats_cb,
                     mojom::MediaMetricsProvider* provider,
                     scoped_refptr<base::SequencedTaskRunner> task_runner,
                     const base::TickClock* tick_clock);
@@ -183,6 +187,8 @@
   void RecordWatchTime();
   void UpdateWatchTime();
 
+  void ResetUnderflowState();
+
   // Helper methods for creating the components that make up the watch time
   // report. All components except the base component require a creation method
   // and a conversion method to get the correct WatchTimeKey.
@@ -199,6 +205,7 @@
   const bool is_background_;
   const bool is_muted_;
   const GetMediaTimeCB get_media_time_cb_;
+  const GetPipelineStatsCB get_pipeline_stats_cb_;
   mojom::WatchTimeRecorderPtr recorder_;
 
   // The amount of time between each UpdateWatchTime(); this is the frequency by
@@ -220,8 +227,18 @@
   // transitioning above/below kMinimumVideoSize.
   gfx::Size natural_size_;
 
-  int underflow_count_ = 0;
-  std::vector<base::TimeDelta> pending_underflow_events_;
+  int total_underflow_count_ = 0;
+  int total_completed_underflow_count_ = 0;
+  base::TimeDelta total_underflow_duration_;
+  struct UnderflowEvent {
+    bool reported = false;
+    base::TimeDelta timestamp = kNoTimestamp;
+    base::TimeDelta duration = kNoTimestamp;
+  };
+  std::vector<UnderflowEvent> pending_underflow_events_;
+
+  PipelineStatistics initial_stats_;
+  PipelineStatistics last_stats_;
 
   // The various components making up WatchTime. If the |base_component_| is
   // finalized, all reporting will be stopped and finalized using its ending
diff --git a/media/blink/watch_time_reporter_unittest.cc b/media/blink/watch_time_reporter_unittest.cc
index 1ca88347..bad6fb1f 100644
--- a/media/blink/watch_time_reporter_unittest.cc
+++ b/media/blink/watch_time_reporter_unittest.cc
@@ -11,8 +11,9 @@
 #include "base/single_thread_task_runner.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "media/base/mock_media_log.h"
-#include "media/base/video_codecs.h"
+#include "media/base/pipeline_status.h"
 #include "media/base/watch_time_keys.h"
 #include "media/blink/watch_time_reporter.h"
 #include "media/mojo/mojom/media_metrics_provider.mojom.h"
@@ -209,6 +210,11 @@
       parent_->OnUnderflowUpdate(count);
     }
 
+    void UpdateUnderflowDuration(int32_t total_completed_count,
+                                 base::TimeDelta total_duration) override {
+      parent_->OnUnderflowDurationUpdate(total_completed_count, total_duration);
+    }
+
     void SetAutoplayInitiated(bool value) override {
       parent_->OnSetAutoplayInitiated(value);
     }
@@ -217,6 +223,12 @@
       parent_->OnDurationChanged(duration);
     }
 
+    void UpdateVideoDecodeStats(uint32_t video_frames_decoded,
+                                uint32_t video_frames_dropped) override {
+      parent_->OnUpdateVideoDecodeStats(video_frames_decoded,
+                                        video_frames_dropped);
+    }
+
    private:
     WatchTimeReporterTest* parent_;
 
@@ -293,10 +305,18 @@
         initial_video_size,
         base::BindRepeating(&WatchTimeReporterTest::GetCurrentMediaTime,
                             base::Unretained(this)),
+        base::BindRepeating(&WatchTimeReporterTest::GetPipelineStatistics,
+                            base::Unretained(this)),
         &fake_metrics_provider_,
         blink::scheduler::GetSequencedTaskRunnerForTesting(),
         task_runner_->GetMockTickClock()));
     reporting_interval_ = wtr_->reporting_interval_;
+
+    // Most tests don't care about this.
+    EXPECT_CALL(*this, GetPipelineStatistics())
+        .WillRepeatedly(testing::Return(PipelineStatistics()));
+    EXPECT_CALL(*this, OnUpdateVideoDecodeStats(_, _))
+        .Times(testing::AnyNumber());
   }
 
   void CycleReportingTimer() {
@@ -587,6 +607,7 @@
   }
 
   MOCK_METHOD0(GetCurrentMediaTime, base::TimeDelta());
+  MOCK_METHOD0(GetPipelineStatistics, PipelineStatistics());
 
   MOCK_METHOD0(OnWatchTimeFinalized, void(void));
   MOCK_METHOD0(OnPowerWatchTimeFinalized, void(void));
@@ -594,11 +615,13 @@
   MOCK_METHOD0(OnDisplayWatchTimeFinalized, void(void));
   MOCK_METHOD2(OnWatchTimeUpdate, void(WatchTimeKey, base::TimeDelta));
   MOCK_METHOD1(OnUnderflowUpdate, void(int));
+  MOCK_METHOD2(OnUnderflowDurationUpdate, void(int, base::TimeDelta));
   MOCK_METHOD1(OnError, void(PipelineStatus));
   MOCK_METHOD1(OnUpdateSecondaryProperties,
                void(mojom::SecondaryPlaybackPropertiesPtr));
   MOCK_METHOD1(OnSetAutoplayInitiated, void(bool));
   MOCK_METHOD1(OnDurationChanged, void(base::TimeDelta));
+  MOCK_METHOD2(OnUpdateVideoDecodeStats, void(uint32_t, uint32_t));
 
   const bool has_video_;
   const bool has_audio_;
@@ -686,6 +709,18 @@
       .WillOnce(testing::Return(kWatchTimeEarly))
       .WillRepeatedly(testing::Return(kWatchTimeLate));
   Initialize(true, true, kSizeJustRight);
+
+  PipelineStatistics stats;
+  stats.video_frames_decoded = 10;
+  stats.video_frames_dropped = 2;
+  if (has_video_) {
+    EXPECT_CALL(*this, GetPipelineStatistics())
+        .WillOnce(testing::Return(PipelineStatistics()))
+        .WillRepeatedly(testing::Return(stats));
+    EXPECT_CALL(*this, OnUpdateVideoDecodeStats(stats.video_frames_decoded,
+                                                stats.video_frames_dropped));
+  }
+
   wtr_->OnPlaying();
   EXPECT_TRUE(IsMonitoring());
 
@@ -698,6 +733,9 @@
   CycleReportingTimer();
 
   wtr_->OnUnderflow();
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMilliseconds(250);
+  wtr_->OnUnderflowComplete(kUnderflowDuration);
   wtr_->OnUnderflow();
   EXPECT_WATCH_TIME(Ac, kWatchTimeLate);
   EXPECT_WATCH_TIME(All, kWatchTimeLate);
@@ -706,6 +744,64 @@
   EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeLate);
   EXPECT_WATCH_TIME_IF_VIDEO(DisplayInline, kWatchTimeLate);
   EXPECT_CALL(*this, OnUnderflowUpdate(2));
+  EXPECT_CALL(*this, OnUnderflowDurationUpdate(1, kUnderflowDuration));
+  CycleReportingTimer();
+
+  EXPECT_WATCH_TIME_FINALIZED();
+  wtr_.reset();
+}
+
+TEST_P(WatchTimeReporterTest, WatchTimeReporterStatsOffsetCorrectly) {
+  constexpr base::TimeDelta kWatchTimeEarly = base::TimeDelta::FromSeconds(5);
+  constexpr base::TimeDelta kWatchTimeLate = base::TimeDelta::FromSeconds(10);
+  EXPECT_CALL(*this, GetCurrentMediaTime())
+      .WillOnce(testing::Return(base::TimeDelta()))
+      .WillOnce(testing::Return(kWatchTimeEarly))
+      .WillRepeatedly(testing::Return(kWatchTimeLate));
+  Initialize(true, true, kSizeJustRight);
+
+  PipelineStatistics initial_stats;
+  initial_stats.video_frames_decoded = 10;
+  initial_stats.video_frames_dropped = 2;
+
+  PipelineStatistics stats;
+  stats.video_frames_decoded = 17;
+  stats.video_frames_dropped = 7;
+  if (has_video_) {
+    EXPECT_CALL(*this, GetPipelineStatistics())
+        .WillOnce(testing::Return(initial_stats))
+        .WillRepeatedly(testing::Return(stats));
+    EXPECT_CALL(
+        *this,
+        OnUpdateVideoDecodeStats(
+            stats.video_frames_decoded - initial_stats.video_frames_decoded,
+            stats.video_frames_dropped - initial_stats.video_frames_dropped));
+  }
+
+  wtr_->OnPlaying();
+  EXPECT_TRUE(IsMonitoring());
+
+  EXPECT_WATCH_TIME(Ac, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(All, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(Eme, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(Mse, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeEarly);
+  EXPECT_WATCH_TIME_IF_VIDEO(DisplayInline, kWatchTimeEarly);
+  CycleReportingTimer();
+
+  wtr_->OnUnderflow();
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMilliseconds(250);
+  wtr_->OnUnderflowComplete(kUnderflowDuration);
+  wtr_->OnUnderflow();
+  EXPECT_WATCH_TIME(Ac, kWatchTimeLate);
+  EXPECT_WATCH_TIME(All, kWatchTimeLate);
+  EXPECT_WATCH_TIME(Eme, kWatchTimeLate);
+  EXPECT_WATCH_TIME(Mse, kWatchTimeLate);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeLate);
+  EXPECT_WATCH_TIME_IF_VIDEO(DisplayInline, kWatchTimeLate);
+  EXPECT_CALL(*this, OnUnderflowUpdate(2));
+  EXPECT_CALL(*this, OnUnderflowDurationUpdate(1, kUnderflowDuration));
   CycleReportingTimer();
 
   EXPECT_WATCH_TIME_FINALIZED();
@@ -765,6 +861,10 @@
   wtr_->OnUnderflow();
   wtr_->OnVolumeChange(0);
 
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMilliseconds(250);
+  wtr_->OnUnderflowComplete(kUnderflowDuration);
+
   // This underflow call should be ignored since it happens after the finalize.
   // Note: We use a muted call above to trigger finalize instead of say a pause
   // since media time will be the same in the event of a pause and no underflow
@@ -790,6 +890,7 @@
 
   EXPECT_CALL(*this, OnUnderflowUpdate(1))
       .Times((has_audio_ && has_video_) ? 2 : 1);
+  EXPECT_CALL(*this, OnUnderflowDurationUpdate(1, kUnderflowDuration));
   CycleReportingTimer();
 
   // Muted watch time shouldn't finalize until destruction.
@@ -798,6 +899,193 @@
   wtr_.reset();
 }
 
+TEST_P(WatchTimeReporterTest, WatchTimeReporterUnderflowSpansFinalize) {
+  constexpr base::TimeDelta kWatchTimeFirst = base::TimeDelta::FromSeconds(5);
+  constexpr base::TimeDelta kWatchTimeEarly = base::TimeDelta::FromSeconds(10);
+  constexpr base::TimeDelta kWatchTimeLate = base::TimeDelta::FromSeconds(15);
+  if (has_audio_ && has_video_) {
+    EXPECT_CALL(*this, GetCurrentMediaTime())
+        .WillOnce(testing::Return(base::TimeDelta()))
+        .WillOnce(testing::Return(kWatchTimeFirst))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillOnce(testing::Return(kWatchTimeEarly))  // Extra 2 for muted.
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillRepeatedly(testing::Return(kWatchTimeLate));
+  } else {
+    EXPECT_CALL(*this, GetCurrentMediaTime())
+        .WillOnce(testing::Return(base::TimeDelta()))
+        .WillOnce(testing::Return(kWatchTimeFirst))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillRepeatedly(testing::Return(kWatchTimeLate));
+  }
+  Initialize(true, true, kSizeJustRight);
+  wtr_->OnPlaying();
+  EXPECT_TRUE(IsMonitoring());
+
+  EXPECT_WATCH_TIME(Ac, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(All, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(Eme, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(Mse, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeFirst);
+  EXPECT_WATCH_TIME_IF_VIDEO(DisplayInline, kWatchTimeFirst);
+  CycleReportingTimer();
+
+  wtr_->OnUnderflow();
+  wtr_->OnVolumeChange(0);
+
+  EXPECT_WATCH_TIME(Ac, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(All, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(Eme, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(Mse, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeEarly);
+  EXPECT_WATCH_TIME_IF_VIDEO(DisplayInline, kWatchTimeEarly);
+  EXPECT_WATCH_TIME_FINALIZED();
+
+  // Since we're using a mute event above, we'll have some muted watch time.
+  const base::TimeDelta kWatchTime = kWatchTimeLate - kWatchTimeEarly;
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(Ac, kWatchTime);
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(All, kWatchTime);
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(Eme, kWatchTime);
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(Mse, kWatchTime);
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(NativeControlsOff, kWatchTime);
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(DisplayInline, kWatchTime);
+  EXPECT_CALL(*this, OnUnderflowUpdate(1));
+  CycleReportingTimer();
+
+  // Muted watch time shouldn't finalize until destruction.
+  if (has_audio_ && has_video_)
+    EXPECT_WATCH_TIME_FINALIZED();
+
+  // This underflow completion should be dropped since we've lost the original
+  // underflow it corresponded to in the finalize.
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMilliseconds(250);
+  wtr_->OnUnderflowComplete(kUnderflowDuration);
+  wtr_.reset();
+}
+
+TEST_P(WatchTimeReporterTest, WatchTimeReporterUnderflowTooLong) {
+  constexpr base::TimeDelta kWatchTimeFirst = base::TimeDelta::FromSeconds(5);
+  constexpr base::TimeDelta kWatchTimeEarly = base::TimeDelta::FromSeconds(10);
+  constexpr base::TimeDelta kWatchTimeLate = base::TimeDelta::FromSeconds(15);
+  if (has_audio_ && has_video_) {
+    EXPECT_CALL(*this, GetCurrentMediaTime())
+        .WillOnce(testing::Return(base::TimeDelta()))
+        .WillOnce(testing::Return(kWatchTimeFirst))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillOnce(testing::Return(kWatchTimeEarly))  // Extra 2 for muted.
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillRepeatedly(testing::Return(kWatchTimeLate));
+  } else {
+    EXPECT_CALL(*this, GetCurrentMediaTime())
+        .WillOnce(testing::Return(base::TimeDelta()))
+        .WillOnce(testing::Return(kWatchTimeFirst))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillRepeatedly(testing::Return(kWatchTimeLate));
+  }
+  Initialize(true, true, kSizeJustRight);
+  wtr_->OnPlaying();
+  EXPECT_TRUE(IsMonitoring());
+
+  EXPECT_WATCH_TIME(Ac, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(All, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(Eme, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(Mse, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeFirst);
+  EXPECT_WATCH_TIME_IF_VIDEO(DisplayInline, kWatchTimeFirst);
+  CycleReportingTimer();
+
+  wtr_->OnUnderflow();
+  wtr_->OnVolumeChange(0);
+
+  // This underflow took too long to complete so is dropped.
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMinutes(2);
+  wtr_->OnUnderflowComplete(kUnderflowDuration);
+
+  EXPECT_WATCH_TIME(Ac, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(All, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(Eme, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(Mse, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeEarly);
+  EXPECT_WATCH_TIME_IF_VIDEO(DisplayInline, kWatchTimeEarly);
+  EXPECT_WATCH_TIME_FINALIZED();
+
+  // Since we're using a mute event above, we'll have some muted watch time.
+  const base::TimeDelta kWatchTime = kWatchTimeLate - kWatchTimeEarly;
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(Ac, kWatchTime);
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(All, kWatchTime);
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(Eme, kWatchTime);
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(Mse, kWatchTime);
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(NativeControlsOff, kWatchTime);
+  EXPECT_MUTED_WATCH_TIME_IF_AUDIO_VIDEO(DisplayInline, kWatchTime);
+  EXPECT_CALL(*this, OnUnderflowUpdate(1));
+  CycleReportingTimer();
+
+  // Muted watch time shouldn't finalize until destruction.
+  if (has_audio_ && has_video_)
+    EXPECT_WATCH_TIME_FINALIZED();
+  wtr_.reset();
+}
+
+TEST_P(WatchTimeReporterTest, WatchTimeReporterNoUnderflowDoubleReport) {
+  constexpr base::TimeDelta kWatchTimeFirst = base::TimeDelta::FromSeconds(5);
+  constexpr base::TimeDelta kWatchTimeEarly = base::TimeDelta::FromSeconds(10);
+  constexpr base::TimeDelta kWatchTimeLate = base::TimeDelta::FromSeconds(15);
+  if (has_audio_ && has_video_) {
+    EXPECT_CALL(*this, GetCurrentMediaTime())
+        .WillOnce(testing::Return(base::TimeDelta()))
+        .WillOnce(testing::Return(kWatchTimeFirst))
+        .WillOnce(testing::Return(kWatchTimeFirst))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillRepeatedly(testing::Return(kWatchTimeLate));
+  } else {
+    EXPECT_CALL(*this, GetCurrentMediaTime())
+        .WillOnce(testing::Return(base::TimeDelta()))
+        .WillOnce(testing::Return(kWatchTimeFirst))
+        .WillOnce(testing::Return(kWatchTimeFirst))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillOnce(testing::Return(kWatchTimeEarly))
+        .WillRepeatedly(testing::Return(kWatchTimeLate));
+  }
+  Initialize(true, true, kSizeJustRight);
+  wtr_->OnPlaying();
+  EXPECT_TRUE(IsMonitoring());
+
+  EXPECT_WATCH_TIME(Ac, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(All, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(Eme, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(Mse, kWatchTimeFirst);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeFirst);
+  EXPECT_WATCH_TIME_IF_VIDEO(DisplayInline, kWatchTimeFirst);
+  EXPECT_CALL(*this, OnUnderflowUpdate(1));
+  wtr_->OnUnderflow();
+  CycleReportingTimer();
+
+  EXPECT_WATCH_TIME(Ac, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(All, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(Eme, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(Mse, kWatchTimeEarly);
+  EXPECT_WATCH_TIME(NativeControlsOff, kWatchTimeEarly);
+  EXPECT_WATCH_TIME_IF_VIDEO(DisplayInline, kWatchTimeEarly);
+
+  // This cycle should not report another underflow.
+  CycleReportingTimer();
+
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMilliseconds(250);
+  wtr_->OnUnderflowComplete(kUnderflowDuration);
+  EXPECT_CALL(*this, OnUnderflowDurationUpdate(1, kUnderflowDuration));
+
+  EXPECT_WATCH_TIME_FINALIZED();
+  wtr_.reset();
+}
+
 // Verify secondary properties pass through correctly.
 TEST_P(WatchTimeReporterTest, WatchTimeReporterSecondaryProperties) {
   Initialize(true, true, kSizeJustRight);
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index a062afb..36a6cadd5 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -2102,7 +2102,9 @@
 
     // Report the amount of time it took to leave the underflow state.
     if (underflow_timer_) {
-      RecordUnderflowDuration(underflow_timer_->Elapsed());
+      auto elapsed = underflow_timer_->Elapsed();
+      RecordUnderflowDuration(elapsed);
+      watch_time_reporter_->OnUnderflowComplete(elapsed);
       underflow_timer_.reset();
     }
   } else {
@@ -3147,6 +3149,8 @@
       pipeline_metadata_.natural_size,
       base::BindRepeating(&WebMediaPlayerImpl::GetCurrentTimeInternal,
                           base::Unretained(this)),
+      base::BindRepeating(&WebMediaPlayerImpl::GetPipelineStatistics,
+                          base::Unretained(this)),
       media_metrics_provider_.get(),
       frame_->GetTaskRunner(blink::TaskType::kInternalMedia)));
   watch_time_reporter_->OnVolumeChange(volume_);
diff --git a/media/mojo/mojom/watch_time_recorder.mojom b/media/mojo/mojom/watch_time_recorder.mojom
index 06e0de7f..52311c2 100644
--- a/media/mojo/mojom/watch_time_recorder.mojom
+++ b/media/mojo/mojom/watch_time_recorder.mojom
@@ -83,8 +83,14 @@
   // 10s, and 15s will become 20s. May be called any number of times.
   OnDurationChanged(mojo_base.mojom.TimeDelta duration);
 
+  // Updates the total number of frames decoded and dropped. As with other
+  // values, these are absolute and not relative since the last call.
+  UpdateVideoDecodeStats(uint32 frames_decoded, uint32 frames_dropped);
+
   // Indicates that an underflow event has occurred while collecting watch time.
   // Used to report mean values for rebuffering metrics. As with watch time,
   // this is an absolute count and not relative since the last call.
-  UpdateUnderflowCount(int32 count);
+  UpdateUnderflowCount(int32 total_count);
+  UpdateUnderflowDuration(int32 total_completed_count,
+                          mojo_base.mojom.TimeDelta total_duration);
 };
diff --git a/media/mojo/services/watch_time_recorder.cc b/media/mojo/services/watch_time_recorder.cc
index dc03069..bbe298e 100644
--- a/media/mojo/services/watch_time_recorder.cc
+++ b/media/mojo/services/watch_time_recorder.cc
@@ -238,9 +238,18 @@
   }
 
   // Ensure values are cleared in case the reporter is reused.
-  if (!ukm_records_.empty())
-    ukm_records_.back().total_underflow_count += underflow_count_;
-  underflow_count_ = 0;
+  if (!ukm_records_.empty()) {
+    auto& last_record = ukm_records_.back();
+    last_record.total_underflow_count += underflow_count_;
+    last_record.total_completed_underflow_count += completed_underflow_count_;
+    last_record.total_underflow_duration += underflow_duration_;
+    last_record.total_video_frames_decoded += video_frames_decoded_;
+    last_record.total_video_frames_dropped += video_frames_dropped_;
+  }
+
+  video_frames_decoded_ = video_frames_dropped_ = 0;
+  underflow_count_ = completed_underflow_count_ = 0;
+  underflow_duration_ = base::TimeDelta();
   watch_time_info_.clear();
 }
 
@@ -298,11 +307,18 @@
     for (auto& kv : watch_time_info_)
       last_record.aggregate_watch_time_info[kv.first] += kv.second;
     last_record.total_underflow_count += underflow_count_;
+    last_record.total_completed_underflow_count += completed_underflow_count_;
+    last_record.total_underflow_duration += underflow_duration_;
+    last_record.total_video_frames_decoded += video_frames_decoded_;
+    last_record.total_video_frames_dropped += video_frames_dropped_;
 
     // If we flushed any watch time or underflow counts which hadn't been
     // finalized we'll need to ensure the eventual Finalize() correctly accounts
     // for those values at the time of the secondary property update.
-    last_record_was_unfinalized = !watch_time_info_.empty() || underflow_count_;
+    last_record_was_unfinalized =
+        !watch_time_info_.empty() || underflow_count_ ||
+        completed_underflow_count_ || !underflow_duration_.is_zero() ||
+        video_frames_decoded_ || video_frames_dropped_;
   }
   ukm_records_.emplace_back(std::move(secondary_properties));
 
@@ -324,6 +340,10 @@
   if (last_record_was_unfinalized) {
     auto& last_record = ukm_records_.back();
     last_record.total_underflow_count = -underflow_count_;
+    last_record.total_completed_underflow_count = -completed_underflow_count_;
+    last_record.total_underflow_duration = -underflow_duration_;
+    last_record.total_video_frames_decoded = -video_frames_decoded_;
+    last_record.total_video_frames_dropped = -video_frames_dropped_;
     for (auto& kv : watch_time_info_)
       last_record.aggregate_watch_time_info[kv.first] = -kv.second;
   }
@@ -338,8 +358,21 @@
   duration_ = duration;
 }
 
-void WatchTimeRecorder::UpdateUnderflowCount(int32_t count) {
-  underflow_count_ = count;
+void WatchTimeRecorder::UpdateVideoDecodeStats(uint32_t video_frames_decoded,
+                                               uint32_t video_frames_dropped) {
+  video_frames_decoded_ = video_frames_decoded;
+  video_frames_dropped_ = video_frames_dropped;
+}
+
+void WatchTimeRecorder::UpdateUnderflowCount(int32_t total_count) {
+  underflow_count_ = total_count;
+}
+
+void WatchTimeRecorder::UpdateUnderflowDuration(
+    int32_t total_completed_count,
+    base::TimeDelta total_duration) {
+  completed_underflow_count_ = total_completed_count;
+  underflow_duration_ = total_duration;
 }
 
 void WatchTimeRecorder::RecordUkmPlaybackData() {
@@ -473,6 +506,12 @@
     builder.SetIsMSE(properties_->is_mse);
     builder.SetLastPipelineStatus(pipeline_status_);
     builder.SetRebuffersCount(ukm_record.total_underflow_count);
+    builder.SetCompletedRebuffersCount(
+        ukm_record.total_completed_underflow_count);
+    builder.SetCompletedRebuffersDuration(
+        ukm_record.total_underflow_duration.InMilliseconds());
+    builder.SetVideoFramesDecoded(ukm_record.total_video_frames_decoded);
+    builder.SetVideoFramesDropped(ukm_record.total_video_frames_dropped);
     builder.SetVideoNaturalWidth(
         ukm_record.secondary_properties->natural_size.width());
     builder.SetVideoNaturalHeight(
diff --git a/media/mojo/services/watch_time_recorder.h b/media/mojo/services/watch_time_recorder.h
index 9c843f2..3776ad38 100644
--- a/media/mojo/services/watch_time_recorder.h
+++ b/media/mojo/services/watch_time_recorder.h
@@ -38,7 +38,11 @@
       mojom::SecondaryPlaybackPropertiesPtr secondary_properties) override;
   void SetAutoplayInitiated(bool value) override;
   void OnDurationChanged(base::TimeDelta duration) override;
-  void UpdateUnderflowCount(int32_t count) override;
+  void UpdateVideoDecodeStats(uint32_t video_frames_decoded,
+                              uint32_t video_frames_dropped) override;
+  void UpdateUnderflowCount(int32_t total_count) override;
+  void UpdateUnderflowDuration(int32_t total_completed_count,
+                               base::TimeDelta total_duration) override;
 
  private:
   // Records a UKM event based on |aggregate_watch_time_info_|; only recorded
@@ -89,15 +93,26 @@
     // Sum of all watch time data since the last complete finalize.
     WatchTimeInfo aggregate_watch_time_info;
 
-    // Total underflow count for this segment of UKM watch time.
+    // Total underflow count and duration for this segment of UKM watch time.
     int total_underflow_count = 0;
+    int total_completed_underflow_count = 0;
+    base::TimeDelta total_underflow_duration;
+
+    uint32_t total_video_frames_decoded = 0;
+    uint32_t total_video_frames_dropped = 0;
   };
 
   // List of all watch time segments. A new entry is added for every secondary
   // property update.
   std::vector<WatchTimeUkmRecord> ukm_records_;
 
+  uint32_t video_frames_decoded_ = 0;
+  uint32_t video_frames_dropped_ = 0;
+
   int underflow_count_ = 0;
+  int completed_underflow_count_ = 0;
+  base::TimeDelta underflow_duration_;
+
   PipelineStatus pipeline_status_ = PIPELINE_OK;
   base::TimeDelta duration_ = kNoTimestamp;
 
diff --git a/media/mojo/services/watch_time_recorder_unittest.cc b/media/mojo/services/watch_time_recorder_unittest.cc
index c62826d..7352b79f 100644
--- a/media/mojo/services/watch_time_recorder_unittest.cc
+++ b/media/mojo/services/watch_time_recorder_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_message_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "media/base/video_codecs.h"
 #include "media/base/watch_time_keys.h"
@@ -466,6 +467,8 @@
     EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
     EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
     EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
     EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
                secondary_properties->natural_size.width());
     EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
@@ -539,6 +542,8 @@
     EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
     EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
     EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
     EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
                secondary_properties->natural_size.width());
     EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
@@ -599,6 +604,8 @@
     EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
     EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
     EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
     EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
                secondary_properties->natural_size.width());
     EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
@@ -650,6 +657,10 @@
   wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoDisplayPictureInPicture,
                         kWatchTime3);
   wtr_->UpdateUnderflowCount(3);
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMilliseconds(500);
+  wtr_->UpdateUnderflowDuration(2, kUnderflowDuration);
+  wtr_->UpdateVideoDecodeStats(10, 2);
   wtr_->OnError(PIPELINE_ERROR_DECODE);
 
   secondary_properties->audio_decoder_name = "MojoAudioDecoder";
@@ -709,6 +720,11 @@
     EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
     EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_ERROR_DECODE);
     EXPECT_UKM(UkmEntry::kRebuffersCountName, 3);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 2);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
+               kUnderflowDuration.InMilliseconds());
+    EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, 10);
+    EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, 2);
     EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
                secondary_properties->natural_size.width());
     EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
@@ -754,6 +770,8 @@
     EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
     EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
     EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
     EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
                secondary_properties->natural_size.width());
     EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
@@ -810,6 +828,8 @@
     EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
     EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
     EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
     EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
                secondary_properties->natural_size.width());
     EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
@@ -868,6 +888,8 @@
     EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
     EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_OK);
     EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
     EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
                secondary_properties->natural_size.width());
     EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
@@ -948,6 +970,8 @@
     EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 1);
     EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 2);
     EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
+    EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
     EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties2->audio_codec);
     EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties2->video_codec);
     EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
@@ -977,10 +1001,17 @@
   Initialize(properties.Clone());
   wtr_->UpdateSecondaryProperties(secondary_properties1.Clone());
 
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMilliseconds(250);
   constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(54);
   const int kUnderflowCount1 = 2;
   wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime1);
   wtr_->UpdateUnderflowCount(kUnderflowCount1);
+  wtr_->UpdateUnderflowDuration(kUnderflowCount1, kUnderflowDuration);
+
+  constexpr int kDecodedFrameCount1 = 10;
+  constexpr int kDroppedFrameCount1 = 2;
+  wtr_->UpdateVideoDecodeStats(kDecodedFrameCount1, kDroppedFrameCount1);
 
   mojom::SecondaryPlaybackPropertiesPtr secondary_properties2 =
       mojom::SecondaryPlaybackProperties::New(
@@ -1000,6 +1031,11 @@
   wtr_->OnError(PIPELINE_ERROR_DECODE);
   wtr_->OnDurationChanged(base::TimeDelta::FromSeconds(5125));
 
+  constexpr int kDecodedFrameCount2 = 20;
+  constexpr int kDroppedFrameCount2 = 10;
+  wtr_->UpdateVideoDecodeStats(kDecodedFrameCount1 + kDecodedFrameCount2,
+                               kDroppedFrameCount1 + kDroppedFrameCount2);
+
   wtr_.reset();
   base::RunLoop().RunUntilIdle();
 
@@ -1030,6 +1066,11 @@
   EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 2);
   EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 5);
   EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount1);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, kUnderflowCount1);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
+             kUnderflowDuration.InMilliseconds());
+  EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, kDecodedFrameCount1);
+  EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, kDroppedFrameCount1);
   EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties1->audio_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties1->video_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
@@ -1053,6 +1094,11 @@
   EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 1);
   EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 2);
   EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount2);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
+  EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, kDecodedFrameCount2);
+  EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, kDroppedFrameCount2);
+
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
   EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties2->audio_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties2->video_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
@@ -1080,10 +1126,17 @@
   Initialize(properties.Clone());
   wtr_->UpdateSecondaryProperties(secondary_properties1.Clone());
 
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMilliseconds(250);
   constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(54);
   const int kUnderflowCount1 = 2;
   wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime1);
   wtr_->UpdateUnderflowCount(kUnderflowCount1);
+  wtr_->UpdateUnderflowDuration(kUnderflowCount1, kUnderflowDuration);
+
+  constexpr int kDecodedFrameCount1 = 10;
+  constexpr int kDroppedFrameCount1 = 2;
+  wtr_->UpdateVideoDecodeStats(kDecodedFrameCount1, kDroppedFrameCount1);
 
   mojom::SecondaryPlaybackPropertiesPtr secondary_properties2 =
       mojom::SecondaryPlaybackProperties::New(
@@ -1123,6 +1176,11 @@
   EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 2);
   EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 5);
   EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount1);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, kUnderflowCount1);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
+             kUnderflowDuration.InMilliseconds());
+  EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, kDecodedFrameCount1);
+  EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, kDroppedFrameCount1);
   EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties1->audio_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties1->video_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
@@ -1144,6 +1202,10 @@
   EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 1);
   EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 2);
   EXPECT_UKM(UkmEntry::kRebuffersCountName, 0);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
+  EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, 0);
+  EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, 0);
   EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties2->audio_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties2->video_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
@@ -1171,10 +1233,17 @@
   Initialize(properties.Clone());
   wtr_->UpdateSecondaryProperties(secondary_properties1.Clone());
 
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMilliseconds(250);
   constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(54);
   const int kUnderflowCount1 = 2;
   wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime1);
   wtr_->UpdateUnderflowCount(kUnderflowCount1);
+  wtr_->UpdateUnderflowDuration(kUnderflowCount1, kUnderflowDuration);
+
+  constexpr int kDecodedFrameCount1 = 10;
+  constexpr int kDroppedFrameCount1 = 2;
+  wtr_->UpdateVideoDecodeStats(kDecodedFrameCount1, kDroppedFrameCount1);
 
   // Force a finalize here so that the there is no unfinalized watch time at the
   // time of the secondary property update.
@@ -1224,6 +1293,11 @@
   EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 2);
   EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 5);
   EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount1);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, kUnderflowCount1);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
+             kUnderflowDuration.InMilliseconds());
+  EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, kDecodedFrameCount1);
+  EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, kDroppedFrameCount1);
   EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties1->audio_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties1->video_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
@@ -1247,6 +1321,130 @@
   EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 1);
   EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 2);
   EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount2);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 0);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName, 0);
+  EXPECT_UKM(UkmEntry::kVideoFramesDecodedName, 0);
+  EXPECT_UKM(UkmEntry::kVideoFramesDroppedName, 0);
+  EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties2->audio_codec);
+  EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties2->video_codec);
+  EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
+             secondary_properties2->video_codec_profile);
+  EXPECT_UKM(
+      UkmEntry::kAudioEncryptionSchemeName,
+      static_cast<int64_t>(secondary_properties2->audio_encryption_scheme));
+  EXPECT_UKM(
+      UkmEntry::kVideoEncryptionSchemeName,
+      static_cast<int64_t>(secondary_properties2->video_encryption_scheme));
+  EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
+             secondary_properties2->natural_size.width());
+  EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
+             secondary_properties2->natural_size.height());
+}
+
+TEST_F(WatchTimeRecorderTest, MultipleSecondaryPropertiesRebufferCarryover) {
+  mojom::PlaybackPropertiesPtr properties = mojom::PlaybackProperties::New(
+      true, true, false, false, true, true, false);
+  mojom::SecondaryPlaybackPropertiesPtr secondary_properties1 =
+      mojom::SecondaryPlaybackProperties::New(
+          kCodecOpus, kCodecVP9, VP9PROFILE_PROFILE0, "MojoAudioDecoder",
+          "MojoVideoDecoder", EncryptionMode::kCbcs, EncryptionMode::kCbcs,
+          gfx::Size(400, 300));
+  Initialize(properties.Clone());
+  wtr_->UpdateSecondaryProperties(secondary_properties1.Clone());
+
+  constexpr base::TimeDelta kUnderflowDuration =
+      base::TimeDelta::FromMilliseconds(250);
+  constexpr base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(54);
+  const int kUnderflowCount1 = 2;
+  wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll, kWatchTime1);
+  wtr_->UpdateUnderflowCount(kUnderflowCount1);
+
+  // Complete all but one of the rebuffers in this update.
+  wtr_->UpdateUnderflowDuration(kUnderflowCount1 - 1, kUnderflowDuration);
+
+  mojom::SecondaryPlaybackPropertiesPtr secondary_properties2 =
+      mojom::SecondaryPlaybackProperties::New(
+          kCodecAAC, kCodecH264, H264PROFILE_MAIN, "FFmpegAudioDecoder",
+          "FFmpegVideoDecoder", EncryptionMode::kUnencrypted,
+          EncryptionMode::kUnencrypted, gfx::Size(800, 600));
+  wtr_->UpdateSecondaryProperties(secondary_properties2.Clone());
+
+  constexpr base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(25);
+  const int kUnderflowCount2 = 3;
+
+  // Watch time and underflow counts continue to accumulate during property
+  // changes, so we report the sum here instead of just kWatchTime2.
+  wtr_->RecordWatchTime(WatchTimeKey::kAudioVideoAll,
+                        kWatchTime1 + kWatchTime2);
+  wtr_->UpdateUnderflowCount(kUnderflowCount1 + kUnderflowCount2);
+
+  // Complete the last underflow in the new property set. Unfortunately this
+  // means it will now be associated with this block of watch time. Use a non
+  // integer multiplier to avoid incorrect carry over being hidden.
+  wtr_->UpdateUnderflowDuration(kUnderflowCount1, kUnderflowDuration * 1.5);
+
+  wtr_->OnError(PIPELINE_ERROR_DECODE);
+  wtr_->OnDurationChanged(base::TimeDelta::FromSeconds(5125));
+
+  wtr_.reset();
+  base::RunLoop().RunUntilIdle();
+
+  // All records should have the following:
+  const auto& entries = test_recorder_->GetEntriesByName(UkmEntry::kEntryName);
+  EXPECT_EQ(2u, entries.size());
+  for (const auto* entry : entries) {
+    test_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestOrigin));
+    EXPECT_UKM(UkmEntry::kIsBackgroundName, properties->is_background);
+    EXPECT_UKM(UkmEntry::kIsMutedName, properties->is_muted);
+    EXPECT_UKM(UkmEntry::kHasAudioName, properties->has_audio);
+    EXPECT_UKM(UkmEntry::kHasVideoName, properties->has_video);
+    EXPECT_UKM(UkmEntry::kIsEMEName, properties->is_eme);
+    EXPECT_UKM(UkmEntry::kIsMSEName, properties->is_mse);
+    EXPECT_UKM(UkmEntry::kAutoplayInitiatedName, false);
+    EXPECT_UKM(UkmEntry::kDurationName, 5000000);
+    EXPECT_HAS_UKM(UkmEntry::kPlayerIDName);
+
+    // All records inherit the final pipeline status code.
+    EXPECT_UKM(UkmEntry::kLastPipelineStatusName, PIPELINE_ERROR_DECODE);
+  }
+
+  // The first record should have...
+  auto* entry = entries[0];
+  EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime1.InMilliseconds());
+  EXPECT_UKM(UkmEntry::kMeanTimeBetweenRebuffersName,
+             kWatchTime1.InMilliseconds() / kUnderflowCount1);
+  EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 2);
+  EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 5);
+  EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount1);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, kUnderflowCount1 - 1);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
+             kUnderflowDuration.InMilliseconds());
+  EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties1->audio_codec);
+  EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties1->video_codec);
+  EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
+             secondary_properties1->video_codec_profile);
+  EXPECT_UKM(
+      UkmEntry::kAudioEncryptionSchemeName,
+      static_cast<int64_t>(secondary_properties1->audio_encryption_scheme));
+  EXPECT_UKM(
+      UkmEntry::kVideoEncryptionSchemeName,
+      static_cast<int64_t>(secondary_properties1->video_encryption_scheme));
+  EXPECT_UKM(UkmEntry::kVideoNaturalWidthName,
+             secondary_properties1->natural_size.width());
+  EXPECT_UKM(UkmEntry::kVideoNaturalHeightName,
+             secondary_properties1->natural_size.height());
+
+  // The second record should have...
+  entry = entries[1];
+  EXPECT_UKM(UkmEntry::kWatchTimeName, kWatchTime2.InMilliseconds());
+  EXPECT_UKM(UkmEntry::kMeanTimeBetweenRebuffersName,
+             kWatchTime2.InMilliseconds() / kUnderflowCount2);
+  EXPECT_UKM(UkmEntry::kAudioDecoderNameName, 1);
+  EXPECT_UKM(UkmEntry::kVideoDecoderNameName, 2);
+  EXPECT_UKM(UkmEntry::kRebuffersCountName, kUnderflowCount2);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersCountName, 1);
+  EXPECT_UKM(UkmEntry::kCompletedRebuffersDurationName,
+             (kUnderflowDuration * 1.5 - kUnderflowDuration).InMilliseconds());
   EXPECT_UKM(UkmEntry::kAudioCodecName, secondary_properties2->audio_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecName, secondary_properties2->video_codec);
   EXPECT_UKM(UkmEntry::kVideoCodecProfileName,
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 61d1255..72de104 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1575,6 +1575,8 @@
       "third_party/quiche/src/quic/core/quic_buffer_allocator.h",
       "third_party/quiche/src/quic/core/quic_buffered_packet_store.cc",
       "third_party/quiche/src/quic/core/quic_buffered_packet_store.h",
+      "third_party/quiche/src/quic/core/quic_coalesced_packet.cc",
+      "third_party/quiche/src/quic/core/quic_coalesced_packet.h",
       "third_party/quiche/src/quic/core/quic_config.cc",
       "third_party/quiche/src/quic/core/quic_config.h",
       "third_party/quiche/src/quic/core/quic_connection.cc",
@@ -1811,8 +1813,6 @@
       "url_request/url_fetcher_impl.h",
       "url_request/url_fetcher_response_writer.cc",
       "url_request/url_fetcher_response_writer.h",
-      "url_request/url_range_request_job.cc",
-      "url_request/url_range_request_job.h",
       "url_request/url_request.cc",
       "url_request/url_request.h",
       "url_request/url_request_context.cc",
@@ -1824,8 +1824,6 @@
       "url_request/url_request_context_getter_observer.h",
       "url_request/url_request_context_storage.cc",
       "url_request/url_request_context_storage.h",
-      "url_request/url_request_data_job.cc",
-      "url_request/url_request_data_job.h",
       "url_request/url_request_error_job.cc",
       "url_request/url_request_error_job.h",
       "url_request/url_request_filter.cc",
@@ -1849,8 +1847,6 @@
       "url_request/url_request_netlog_params.h",
       "url_request/url_request_redirect_job.cc",
       "url_request/url_request_redirect_job.h",
-      "url_request/url_request_simple_job.cc",
-      "url_request/url_request_simple_job.h",
       "url_request/url_request_status.cc",
       "url_request/url_request_status.h",
       "url_request/url_request_test_job.cc",
@@ -2852,8 +2848,6 @@
     "test/url_request/url_request_mock_data_job.h",
     "test/url_request/url_request_slow_download_job.cc",
     "test/url_request/url_request_slow_download_job.h",
-    "url_request/data_protocol_handler.cc",
-    "url_request/data_protocol_handler.h",
     "url_request/test_url_fetcher_factory.cc",
     "url_request/test_url_fetcher_factory.h",
     "url_request/url_request_test_util.cc",
@@ -5589,6 +5583,7 @@
     "third_party/quiche/src/quic/core/quic_arena_scoped_ptr_test.cc",
     "third_party/quiche/src/quic/core/quic_bandwidth_test.cc",
     "third_party/quiche/src/quic/core/quic_buffered_packet_store_test.cc",
+    "third_party/quiche/src/quic/core/quic_coalesced_packet_test.cc",
     "third_party/quiche/src/quic/core/quic_config_test.cc",
     "third_party/quiche/src/quic/core/quic_connection_id_test.cc",
     "third_party/quiche/src/quic/core/quic_connection_test.cc",
@@ -5715,7 +5710,6 @@
     "url_request/url_request_job_factory_impl_unittest.cc",
     "url_request/url_request_job_unittest.cc",
     "url_request/url_request_quic_unittest.cc",
-    "url_request/url_request_simple_job_unittest.cc",
     "url_request/url_request_throttler_simulation_unittest.cc",
     "url_request/url_request_throttler_test_support.cc",
     "url_request/url_request_throttler_test_support.h",
diff --git a/net/base/data_url.cc b/net/base/data_url.cc
index 29a3965..ec051a85 100644
--- a/net/base/data_url.cc
+++ b/net/base/data_url.cc
@@ -30,6 +30,7 @@
 
   DCHECK(mime_type->empty());
   DCHECK(charset->empty());
+  DCHECK(!data || data->empty());
 
   std::string content = url.GetContent();
 
@@ -45,9 +46,12 @@
       base::SplitStringPiece(base::StringPiece(begin, comma), ";",
                              base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
 
+  // These are moved to |mime_type| and |charset| on success.
+  std::string mime_type_value;
+  std::string charset_value;
   auto iter = meta_data.cbegin();
   if (iter != meta_data.cend()) {
-    *mime_type = base::ToLowerASCII(*iter);
+    mime_type_value = base::ToLowerASCII(*iter);
     ++iter;
   }
 
@@ -59,83 +63,87 @@
     if (!base64_encoded &&
         base::EqualsCaseInsensitiveASCII(*iter, kBase64Tag)) {
       base64_encoded = true;
-    } else if (charset->empty() &&
+    } else if (charset_value.empty() &&
                base::StartsWith(*iter, kCharsetTag,
                                 base::CompareCase::INSENSITIVE_ASCII)) {
-      *charset = std::string(iter->substr(kCharsetTag.size()));
+      charset_value = iter->substr(kCharsetTag.size()).as_string();
       // The grammar for charset is not specially defined in RFC2045 and
       // RFC2397. It just needs to be a token.
-      if (!HttpUtil::IsToken(*charset))
+      if (!HttpUtil::IsToken(charset_value))
         return false;
     }
   }
 
-  if (mime_type->empty()) {
+  if (mime_type_value.empty()) {
     // Fallback to the default if nothing specified in the mediatype part as
     // specified in RFC2045. As specified in RFC2397, we use |charset| even if
     // |mime_type| is empty.
-    mime_type->assign("text/plain");
-    if (charset->empty())
-      charset->assign("US-ASCII");
-  } else if (!ParseMimeTypeWithoutParameter(*mime_type, nullptr, nullptr)) {
+    mime_type_value = "text/plain";
+    if (charset_value.empty())
+      charset_value = "US-ASCII";
+  } else if (!ParseMimeTypeWithoutParameter(mime_type_value, nullptr,
+                                            nullptr)) {
     // Fallback to the default as recommended in RFC2045 when the mediatype
     // value is invalid. For this case, we don't respect |charset| but force it
     // set to "US-ASCII".
-    mime_type->assign("text/plain");
-    charset->assign("US-ASCII");
+    mime_type_value = "text/plain";
+    charset_value = "US-ASCII";
   }
 
   // The caller may not be interested in receiving the data.
-  if (!data)
-    return true;
+  if (data) {
+    // Preserve spaces if dealing with text or xml input, same as mozilla:
+    //   https://bugzilla.mozilla.org/show_bug.cgi?id=138052
+    // but strip them otherwise:
+    //   https://bugzilla.mozilla.org/show_bug.cgi?id=37200
+    // (Spaces in a data URL should be escaped, which is handled below, so any
+    // spaces now are wrong. People expect to be able to enter them in the URL
+    // bar for text, and it can't hurt, so we allow it.)
+    //
+    // TODO(mmenke): Is removing all spaces reasonable? GURL removes trailing
+    // spaces itself, anyways. Should we just trim leading spaces instead?
+    // Allowing random intermediary spaces seems unnecessary.
 
-  // Preserve spaces if dealing with text or xml input, same as mozilla:
-  //   https://bugzilla.mozilla.org/show_bug.cgi?id=138052
-  // but strip them otherwise:
-  //   https://bugzilla.mozilla.org/show_bug.cgi?id=37200
-  // (Spaces in a data URL should be escaped, which is handled below, so any
-  // spaces now are wrong. People expect to be able to enter them in the URL
-  // bar for text, and it can't hurt, so we allow it.)
-  //
-  // TODO(mmenke): Is removing all spaces reasonable? GURL removes trailing
-  // spaces itself, anyways. Should we just trim leading spaces instead?
-  // Allowing random intermediary spaces seems unnecessary.
+    base::StringPiece raw_body(comma + 1, end);
 
-  base::StringPiece raw_body(comma + 1, end);
+    // For base64, we may have url-escaped whitespace which is not part
+    // of the data, and should be stripped. Otherwise, the escaped whitespace
+    // could be part of the payload, so don't strip it.
+    if (base64_encoded) {
+      std::string unescaped_body = UnescapeBinaryURLComponent(raw_body);
 
-  // For base64, we may have url-escaped whitespace which is not part
-  // of the data, and should be stripped. Otherwise, the escaped whitespace
-  // could be part of the payload, so don't strip it.
-  if (base64_encoded) {
-    std::string unescaped_body = UnescapeBinaryURLComponent(raw_body);
+      // Strip spaces, which aren't allowed in Base64 encoding.
+      base::EraseIf(unescaped_body, base::IsAsciiWhitespace<char>);
 
-    // Strip spaces, which aren't allowed in Base64 encoding.
-    base::EraseIf(unescaped_body, base::IsAsciiWhitespace<char>);
+      size_t length = unescaped_body.length();
+      size_t padding_needed = 4 - (length % 4);
+      // If the input wasn't padded, then we pad it as necessary until we have a
+      // length that is a multiple of 4 as required by our decoder. We don't
+      // correct if the input was incorrectly padded. If |padding_needed| == 3,
+      // then the input isn't well formed and decoding will fail with or without
+      // padding.
+      if ((padding_needed == 1 || padding_needed == 2) &&
+          unescaped_body[length - 1] != '=') {
+        unescaped_body.resize(length + padding_needed, '=');
+      }
+      if (!base::Base64Decode(unescaped_body, data))
+        return false;
+    } else {
+      // Strip whitespace for non-text MIME types.
+      std::string temp;
+      if (!(mime_type_value.compare(0, 5, "text/") == 0 ||
+            mime_type_value.find("xml") != std::string::npos)) {
+        temp = raw_body.as_string();
+        base::EraseIf(temp, base::IsAsciiWhitespace<char>);
+        raw_body = temp;
+      }
 
-    size_t length = unescaped_body.length();
-    size_t padding_needed = 4 - (length % 4);
-    // If the input wasn't padded, then we pad it as necessary until we have a
-    // length that is a multiple of 4 as required by our decoder. We don't
-    // correct if the input was incorrectly padded. If |padding_needed| == 3,
-    // then the input isn't well formed and decoding will fail with or without
-    // padding.
-    if ((padding_needed == 1 || padding_needed == 2) &&
-        unescaped_body[length - 1] != '=') {
-      unescaped_body.resize(length + padding_needed, '=');
+      *data = UnescapeBinaryURLComponent(raw_body);
     }
-    return base::Base64Decode(unescaped_body, data);
   }
 
-  // Strip whitespace for non-text MIME types.
-  std::string temp;
-  if (!(mime_type->compare(0, 5, "text/") == 0 ||
-        mime_type->find("xml") != std::string::npos)) {
-    temp = raw_body.as_string();
-    base::EraseIf(temp, base::IsAsciiWhitespace<char>);
-    raw_body = temp;
-  }
-
-  *data = UnescapeBinaryURLComponent(raw_body);
+  *mime_type = std::move(mime_type_value);
+  *charset = std::move(charset_value);
   return true;
 }
 
@@ -144,8 +152,9 @@
                              std::string* mime_type,
                              std::string* charset,
                              std::string* data,
-                             HttpResponseHeaders* headers) {
+                             scoped_refptr<HttpResponseHeaders>* headers) {
   DCHECK(data);
+  DCHECK(!*headers);
 
   if (!DataURL::Parse(url, mime_type, charset, data))
     return ERR_INVALID_URL;
@@ -155,17 +164,22 @@
   // form. |charset| can be an empty string.
   DCHECK(!mime_type->empty());
 
-  if (headers) {
-    headers->ReplaceStatusLine("HTTP/1.1 200 OK");
-    // "charset" in the Content-Type header is specified explicitly to follow
-    // the "token" ABNF in the HTTP spec. When the DataURL::Parse() call is
-    // successful, it's guaranteed that the string in |charset| follows the
-    // "token" ABNF.
-    std::string content_type_header = "Content-Type: " + *mime_type;
-    if (!charset->empty())
-      content_type_header.append(";charset=" + *charset);
-    headers->AddHeader(content_type_header);
-  }
+  // "charset" in the Content-Type header is specified explicitly to follow
+  // the "token" ABNF in the HTTP spec. When the DataURL::Parse() call is
+  // successful, it's guaranteed that the string in |charset| follows the
+  // "token" ABNF.
+  std::string content_type = *mime_type;
+  if (!charset->empty())
+    content_type.append(";charset=" + *charset);
+  // The terminal double CRLF isn't needed by TryToCreate().
+  *headers = HttpResponseHeaders::TryToCreate(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Type:" +
+      content_type);
+  // Above line should always succeed - TryToCreate() only fails when there are
+  // nulls in the string, and DataURL::Parse() can't return nulls in anything
+  // but the |data| argument.
+  DCHECK(*headers);
 
   if (base::EqualsCaseInsensitiveASCII(method, "HEAD"))
     data->clear();
diff --git a/net/base/data_url.h b/net/base/data_url.h
index b1bc783..2daf6de0 100644
--- a/net/base/data_url.h
+++ b/net/base/data_url.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/compiler_specific.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/strings/string_piece.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_export.h"
@@ -36,6 +37,11 @@
  public:
   // This method can be used to parse a 'data' URL into its component pieces.
   //
+  // |mime_type| and |charset| must be non-null and point to empty strings.
+  //
+  // If |data| is null, then the <data> section will not be parsed or validated.
+  // If non-null, it must point to an empty string.
+  //
   // The resulting mime_type is normalized to lowercase.  The data is the
   // decoded data (e.g.., if the data URL specifies base64 encoding, then the
   // returned data is base64 decoded, and any %-escaped bytes are unescaped).
@@ -55,11 +61,8 @@
   // false.
   //
   // If there's any other grammar violation in the URL, then this method will
-  // return false. Output variables may be changed and contain invalid data. On
-  // success, true is returned.
-  //
-  // OPTIONAL: If |data| is NULL, then the <data> section will not be parsed
-  //           or validated.
+  // return false, and all passed in pointers will be unmodified. On success,
+  // true is returned.
   static bool Parse(const GURL& url,
                     std::string* mime_type,
                     std::string* charset,
@@ -68,17 +71,15 @@
   // Similar to parse, except that it also generates a bogus set of response
   // headers, with Content-Type populated, and takes a method. Only the "HEAD"
   // method modifies the response, resulting in a 0-length body. All arguments
-  // except |headers| must be non-null. Return net::OK on success.
-  //
-  // TODO(mmenke): Make headers mandatory, once URLRequestDataJob has been
-  // removed. Also improve how it's returned, as requiring consumer to create
-  // an empty object is strange.
+  // except must be non-null. All std::string pointers must point to empty
+  // strings, and |*headers| must be nullptr. Returns net::OK on success.
   static Error BuildResponse(const GURL& url,
                              base::StringPiece method,
                              std::string* mime_type,
                              std::string* charset,
                              std::string* data,
-                             HttpResponseHeaders* headers) WARN_UNUSED_RESULT;
+                             scoped_refptr<HttpResponseHeaders>* headers)
+      WARN_UNUSED_RESULT;
 };
 
 }  // namespace net
diff --git a/net/base/data_url_fuzzer.cc b/net/base/data_url_fuzzer.cc
index 7743a47f..237c794 100644
--- a/net/base/data_url_fuzzer.cc
+++ b/net/base/data_url_fuzzer.cc
@@ -30,14 +30,12 @@
   std::string mime_type2;
   std::string charset2;
   std::string body2;
-  scoped_refptr<net::HttpResponseHeaders> headers(
-      base::MakeRefCounted<net::HttpResponseHeaders>(std::string()));
+  scoped_refptr<net::HttpResponseHeaders> headers;
 
   // Run the URL through DataURL::Parse() and DataURL::BuildResponse(). They
   // should succeed and fail in exactly the same cases.
-  CHECK_EQ(
-      net::DataURL::Parse(url, &mime_type, &charset, &body),
-      net::OK == net::DataURL::BuildResponse(url, method, &mime_type2,
-                                             &charset2, &body2, headers.get()));
+  CHECK_EQ(net::DataURL::Parse(url, &mime_type, &charset, &body),
+           net::OK == net::DataURL::BuildResponse(url, method, &mime_type2,
+                                                  &charset2, &body2, &headers));
   return 0;
 }
diff --git a/net/base/data_url_unittest.cc b/net/base/data_url_unittest.cc
index 44de113..7471393 100644
--- a/net/base/data_url_unittest.cc
+++ b/net/base/data_url_unittest.cc
@@ -149,16 +149,16 @@
   };
 
   for (const auto& test : tests) {
+    SCOPED_TRACE(test.url);
+
     std::string mime_type;
     std::string charset;
     std::string data;
     bool ok = DataURL::Parse(GURL(test.url), &mime_type, &charset, &data);
     EXPECT_EQ(ok, test.is_valid);
-    if (test.is_valid) {
-      EXPECT_EQ(test.mime_type, mime_type);
-      EXPECT_EQ(test.charset, charset);
-      EXPECT_EQ(test.data, data);
-    }
+    EXPECT_EQ(test.mime_type, mime_type);
+    EXPECT_EQ(test.charset, charset);
+    EXPECT_EQ(test.data, data);
   }
 }
 
@@ -166,16 +166,16 @@
   std::string mime_type;
   std::string charset;
   std::string data;
-  scoped_refptr<HttpResponseHeaders> headers(
-      new HttpResponseHeaders(std::string()));
+  scoped_refptr<HttpResponseHeaders> headers;
 
   ASSERT_EQ(OK, DataURL::BuildResponse(GURL("data:,Hello"), "GET", &mime_type,
-                                       &charset, &data, headers.get()));
+                                       &charset, &data, &headers));
 
   EXPECT_EQ("text/plain", mime_type);
   EXPECT_EQ("US-ASCII", charset);
   EXPECT_EQ("Hello", data);
 
+  ASSERT_TRUE(headers);
   const HttpVersion& version = headers->GetHttpVersion();
   EXPECT_EQ(1, version.major_value());
   EXPECT_EQ(1, version.minor_value());
@@ -193,16 +193,16 @@
     std::string mime_type;
     std::string charset;
     std::string data;
-    scoped_refptr<HttpResponseHeaders> headers =
-        HttpResponseHeaders::TryToCreate("");
+    scoped_refptr<HttpResponseHeaders> headers;
     ASSERT_EQ(OK,
               DataURL::BuildResponse(GURL("data:,Hello"), method, &mime_type,
-                                     &charset, &data, headers.get()));
+                                     &charset, &data, &headers));
 
     EXPECT_EQ("text/plain", mime_type);
     EXPECT_EQ("US-ASCII", charset);
     EXPECT_EQ("", data);
 
+    ASSERT_TRUE(headers);
     HttpVersion version = headers->GetHttpVersion();
     EXPECT_EQ(1, version.major_value());
     EXPECT_EQ(1, version.minor_value());
@@ -217,27 +217,29 @@
   std::string mime_type;
   std::string charset;
   std::string data;
-  scoped_refptr<HttpResponseHeaders> headers(
-      new HttpResponseHeaders(std::string()));
+  scoped_refptr<HttpResponseHeaders> headers;
 
   ASSERT_EQ(ERR_INVALID_URL,
             DataURL::BuildResponse(GURL("bogus"), "GET", &mime_type, &charset,
-                                   &data, headers.get()));
+                                   &data, &headers));
+  EXPECT_FALSE(headers);
+  EXPECT_TRUE(mime_type.empty());
+  EXPECT_TRUE(charset.empty());
+  EXPECT_TRUE(data.empty());
 }
 
 TEST(DataURLTest, BuildResponseInvalidMimeType) {
   std::string mime_type;
   std::string charset;
   std::string data;
-  scoped_refptr<HttpResponseHeaders> headers(
-      new HttpResponseHeaders(std::string()));
+  scoped_refptr<HttpResponseHeaders> headers;
 
   // MIME type contains delimiters. Must be accepted but Content-Type header
   // should be generated as if the mediatype was text/plain.
-  ASSERT_EQ(OK,
-            DataURL::BuildResponse(GURL("data:f(o/b)r,test"), "GET", &mime_type,
-                                   &charset, &data, headers.get()));
+  ASSERT_EQ(OK, DataURL::BuildResponse(GURL("data:f(o/b)r,test"), "GET",
+                                       &mime_type, &charset, &data, &headers));
 
+  ASSERT_TRUE(headers);
   std::string value;
   EXPECT_TRUE(headers->GetNormalizedHeader("Content-Type", &value));
   EXPECT_EQ(value, "text/plain;charset=US-ASCII");
@@ -247,13 +249,60 @@
   std::string mime_type;
   std::string charset;
   std::string data;
-  scoped_refptr<HttpResponseHeaders> headers(
-      new HttpResponseHeaders(std::string()));
+  scoped_refptr<HttpResponseHeaders> headers;
 
   // MIME type contains delimiters. Must be rejected.
   ASSERT_EQ(ERR_INVALID_URL, DataURL::BuildResponse(
                                  GURL("data:text/html;charset=(),test"), "GET",
-                                 &mime_type, &charset, &data, headers.get()));
+                                 &mime_type, &charset, &data, &headers));
+  EXPECT_FALSE(headers);
+  EXPECT_TRUE(mime_type.empty());
+  EXPECT_TRUE(charset.empty());
+  EXPECT_TRUE(data.empty());
+}
+
+// Test a slightly larger data URL.
+TEST(DataURLTest, Image) {
+  // Use our nice little Chrome logo.
+  GURL image_url(
+      "data:image/png;base64,"
+      "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADVklEQVQ4jX2TfUwUB"
+      "BjG3w1y+HGcd9dxhXR8T4awOccJGgOSWclHImznLkTlSw0DDQXkrmgYgbUYnlQTqQ"
+      "xIEVxitD5UMCATRA1CEEg+Qjw3bWDxIauJv/5oumqs39/P827vnucRmYN0gyF01GI"
+      "5MpCVdW0gO7tvNC+vqSEtbZefk5NuLv1jdJ46p/zw0HeH4+PHr3h7c1mjoV2t5rKz"
+      "Mx1+fg9bAgK6zHq9cU5z+LpA3xOtx34+vTeT21onRuzssC3zxbbSwC13d/pFuC7Ck"
+      "IMDxQpF7r/MWq12UctI1dWWm99ypqSYmRUBdKem8MkrO/kgaTt1O7YzlpzE5GIVd0"
+      "WYUqt57yWf2McHTObYPbVD+ZwbtlLTVMZ3BW+TnLyXLaWtmEq6WJVbT3HBh3Svj2H"
+      "QQcm43XwmtoYM6vVKleh0uoWvnzW3v3MpidruPTQPf0bia7sJOtBM0ufTWNvus/nk"
+      "DFHF9ZS+uYVjRUasMeHUmyLYtcklTvzWGFZnNOXczThvpKIzjcahSqIzkvDLayDq6"
+      "D3eOjtBbNUEIZYyqsvj4V4wY92eNJ4IoyhTbxXX1T5xsV9tm9r4TQwHLiZw/pdDZJ"
+      "ea8TKmsmR/K0uLh/GwnCHghTja6lPhphezPfO5/5MrVvMzNaI3+ERHfrFzPKQukrQ"
+      "GI4d/3EFD/3E2mVNYvi4at7CXWREaxZGD+3hg28zD3gVMd6q5c8GdosynKmSeRuGz"
+      "pjyl1/9UDGtPR5HeaKT8Wjo17WXk579BXVUhN64ehF9fhRtq/uxxZKzNiZFGD0wRC"
+      "3NFROZ5mwIPL/96K/rKMMLrIzF9uhHr+/sYH7DAbwlgC4J+R2Z7FUx1qLnV7MGF40"
+      "smVSoJ/jvHRfYhQeUJd/SnYtGWhPHR0Sz+GE2F2yth0B36Vcz2KpnufBJbsysjjW4"
+      "kblBUiIjiURUWqJY65zxbnTy57GQyH58zgy0QBtTQv5gH15XMdKkYu+TGaJMnlm2O"
+      "34uI4b9tflqp1+QEFGzoW/ulmcofcpkZCYJhDfSpme7QcrHa+Xfji8paEQkTkSfmm"
+      "oRWRNZr/F1KfVMjW+IKEnv2FwZfKdzt0BQR6lClcZR0EfEXEfv/G6W9iLiIyCoReV"
+      "5EnhORIBHx+ufPj/gLB/zGI/G4Bk0AAAAASUVORK5CYII=");
+
+  std::string mime_type;
+  std::string charset;
+  std::string data;
+  scoped_refptr<HttpResponseHeaders> headers;
+
+  EXPECT_EQ(OK, DataURL::BuildResponse(image_url, "GET", &mime_type, &charset,
+                                       &data, &headers));
+
+  EXPECT_EQ(911u, data.size());
+  EXPECT_EQ("image/png", mime_type);
+  EXPECT_TRUE(charset.empty());
+
+  ASSERT_TRUE(headers);
+  std::string value;
+  EXPECT_EQ(headers->GetStatusLine(), "HTTP/1.1 200 OK");
+  EXPECT_TRUE(headers->GetNormalizedHeader("Content-Type", &value));
+  EXPECT_EQ(value, "image/png");
 }
 
 }  // namespace net
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index b155b62..3581285 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -159,9 +159,6 @@
 // If true, enable QUIC version 47.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_47, false)
 
-// If true, enable QUIC version 48.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_48_2, true)
-
 // If true, disable QUIC version 39.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_39, true)
 
@@ -411,3 +408,9 @@
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_no_decrease_in_final_offset,
           false)
+
+// If true, connection will be closed if a stream receives stream frame or
+// RESET_STREAM frame with bad close offset.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_close_connection_on_wrong_offset,
+          false)
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 08adf81f..07b4562 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -5363,60 +5363,68 @@
 #endif  // OS_ANDROID
 }
 
-// Test downgrade enforcement behaves as expected.
-// Failed on macOS. See https://crbug.com/1017036
-#if defined(OS_MACOSX)
-#define MAYBE_TLS13DowngradeEnforced DISABLED_TLS13DowngradeEnforced
-#else
-#define MAYBE_TLS13DowngradeEnforced TLS13DowngradeEnforced
-#endif
-TEST_F(SSLClientSocketTest, MAYBE_TLS13DowngradeEnforced) {
-  for (auto tls_max_version :
-       {SpawnedTestServer::SSLOptions::TLS_MAX_VERSION_TLS1_0,
-        SpawnedTestServer::SSLOptions::TLS_MAX_VERSION_TLS1_1,
-        SpawnedTestServer::SSLOptions::TLS_MAX_VERSION_TLS1_2}) {
-    for (bool downgrade : {false, true}) {
-      SCOPED_TRACE(downgrade);
-      SCOPED_TRACE(tls_max_version);
-      SpawnedTestServer::SSLOptions ssl_options;
-      ssl_options.simulate_tls13_downgrade = downgrade;
-      ssl_options.tls_max_version = tls_max_version;
-      ASSERT_TRUE(StartTestServer(ssl_options));
-      scoped_refptr<X509Certificate> server_cert =
-          spawned_test_server()->GetCertificate();
+class TLS13DowngradeTest
+    : public SSLClientSocketTest,
+      public ::testing::WithParamInterface<
+          std::tuple<SpawnedTestServer::SSLOptions::TLSMaxVersion,
+                     /* simulate_tls13_downgrade */ bool,
+                     /* enable_for_local_anchors */ bool,
+                     /* known_root */ bool>> {
+ public:
+  TLS13DowngradeTest() {}
+  ~TLS13DowngradeTest() {}
 
-      for (bool enable_for_local_anchors : {false, true}) {
-        SCOPED_TRACE(enable_for_local_anchors);
-        SSLContextConfig config;
-        config.version_max = SSL_PROTOCOL_VERSION_TLS1_3;
-        config.tls13_hardening_for_local_anchors_enabled =
-            enable_for_local_anchors;
-        ssl_config_service_->UpdateSSLConfigAndNotify(config);
+  SpawnedTestServer::SSLOptions::TLSMaxVersion tls_max_version() const {
+    return std::get<0>(GetParam());
+  }
 
-        for (bool known_root : {false, true}) {
-          SCOPED_TRACE(known_root);
-          CertVerifyResult verify_result;
-          verify_result.is_issued_by_known_root = known_root;
-          verify_result.verified_cert = server_cert;
-          cert_verifier_->ClearRules();
-          cert_verifier_->AddResultForCert(server_cert.get(), verify_result,
-                                           OK);
+  bool simulate_tls13_downgrade() const { return std::get<1>(GetParam()); }
+  bool enable_for_local_anchors() const { return std::get<2>(GetParam()); }
+  bool known_root() const { return std::get<3>(GetParam()); }
+};
 
-          bool should_enforce = known_root || enable_for_local_anchors;
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    TLS13DowngradeTest,
+    ::testing::Combine(
+        ::testing::Values(
+            SpawnedTestServer::SSLOptions::TLS_MAX_VERSION_TLS1_0,
+            SpawnedTestServer::SSLOptions::TLS_MAX_VERSION_TLS1_1,
+            SpawnedTestServer::SSLOptions::TLS_MAX_VERSION_TLS1_2),
+        ::testing::Values(false, true),
+        ::testing::Values(false, true),
+        ::testing::Values(false, true)));
 
-          ssl_client_session_cache_->Flush();
-          int rv;
-          ASSERT_TRUE(CreateAndConnectSSLClientSocket(SSLConfig(), &rv));
-          if (should_enforce && downgrade) {
-            EXPECT_THAT(rv, IsError(ERR_TLS13_DOWNGRADE_DETECTED));
-            EXPECT_FALSE(sock_->IsConnected());
-          } else {
-            EXPECT_THAT(rv, IsOk());
-            EXPECT_TRUE(sock_->IsConnected());
-          }
-        }
-      }
-    }
+TEST_P(TLS13DowngradeTest, DowngradeEnforced) {
+  SpawnedTestServer::SSLOptions ssl_options;
+  ssl_options.simulate_tls13_downgrade = simulate_tls13_downgrade();
+  ssl_options.tls_max_version = tls_max_version();
+  ASSERT_TRUE(StartTestServer(ssl_options));
+  scoped_refptr<X509Certificate> server_cert =
+      spawned_test_server()->GetCertificate();
+
+  SSLContextConfig config;
+  config.version_max = SSL_PROTOCOL_VERSION_TLS1_3;
+  config.tls13_hardening_for_local_anchors_enabled = enable_for_local_anchors();
+  ssl_config_service_->UpdateSSLConfigAndNotify(config);
+
+  CertVerifyResult verify_result;
+  verify_result.is_issued_by_known_root = known_root();
+  verify_result.verified_cert = server_cert;
+  cert_verifier_->ClearRules();
+  cert_verifier_->AddResultForCert(server_cert.get(), verify_result, OK);
+
+  bool should_enforce = known_root() || enable_for_local_anchors();
+
+  ssl_client_session_cache_->Flush();
+  int rv;
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(SSLConfig(), &rv));
+  if (should_enforce && simulate_tls13_downgrade()) {
+    EXPECT_THAT(rv, IsError(ERR_TLS13_DOWNGRADE_DETECTED));
+    EXPECT_FALSE(sock_->IsConnected());
+  } else {
+    EXPECT_THAT(rv, IsOk());
+    EXPECT_TRUE(sock_->IsConnected());
   }
 }
 
diff --git a/net/ssl/ssl_info.cc b/net/ssl/ssl_info.cc
index 4c074fb..0ca220a 100644
--- a/net/ssl/ssl_info.cc
+++ b/net/ssl/ssl_info.cc
@@ -20,10 +20,6 @@
   *this = SSLInfo();
 }
 
-void SSLInfo::SetCertError(int error) {
-  cert_status |= MapNetErrorToCertStatus(error);
-}
-
 void SSLInfo::UpdateCertificateTransparencyInfo(
     const ct::CTVerifyResult& ct_verify_result) {
   signed_certificate_timestamps.insert(signed_certificate_timestamps.end(),
diff --git a/net/ssl/ssl_info.h b/net/ssl/ssl_info.h
index f68ece8d..b8fe946 100644
--- a/net/ssl/ssl_info.h
+++ b/net/ssl/ssl_info.h
@@ -45,9 +45,6 @@
 
   bool is_valid() const { return cert.get() != nullptr; }
 
-  // Adds the specified |error| to the cert status.
-  void SetCertError(int error);
-
   // Adds the SignedCertificateTimestamps and policy compliance details
   // from ct_verify_result to |signed_certificate_timestamps| and
   // |ct_policy_compliance_details|. SCTs are held in three separate
diff --git a/net/url_request/data_protocol_handler.cc b/net/url_request/data_protocol_handler.cc
deleted file mode 100644
index 3f4d318e..0000000
--- a/net/url_request/data_protocol_handler.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/url_request/data_protocol_handler.h"
-
-#include "net/url_request/url_request_data_job.h"
-
-namespace net {
-
-DataProtocolHandler::DataProtocolHandler() = default;
-
-URLRequestJob* DataProtocolHandler::MaybeCreateJob(
-    URLRequest* request, NetworkDelegate* network_delegate) const {
-  return new URLRequestDataJob(request, network_delegate);
-}
-
-bool DataProtocolHandler::IsSafeRedirectTarget(const GURL& location) const {
-  return false;
-}
-
-}  // namespace net
diff --git a/net/url_request/data_protocol_handler.h b/net/url_request/data_protocol_handler.h
deleted file mode 100644
index dcc9b99..0000000
--- a/net/url_request/data_protocol_handler.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_URL_REQUEST_DATA_PROTOCOL_HANDLER_H_
-#define NET_URL_REQUEST_DATA_PROTOCOL_HANDLER_H_
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "net/base/net_export.h"
-#include "net/url_request/url_request_job_factory.h"
-
-namespace net {
-
-class URLRequestJob;
-
-// Implements a ProtocolHandler for Data jobs.
-// TODO(mmenke): This class is now only used in tests. Remove it.
-class DataProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
- public:
-  DataProtocolHandler();
-  URLRequestJob* MaybeCreateJob(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override;
-  bool IsSafeRedirectTarget(const GURL& location) const override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DataProtocolHandler);
-};
-
-}  // namespace net
-
-#endif  // NET_URL_REQUEST_DATA_PROTOCOL_HANDLER_H_
diff --git a/net/url_request/url_range_request_job.cc b/net/url_request/url_range_request_job.cc
deleted file mode 100644
index 68cf156..0000000
--- a/net/url_request/url_range_request_job.cc
+++ /dev/null
@@ -1,30 +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.
-
-#include "net/url_request/url_range_request_job.h"
-
-#include "net/base/net_errors.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_util.h"
-
-namespace net {
-
-URLRangeRequestJob::URLRangeRequestJob(URLRequest* request,
-    NetworkDelegate* delegate)
-    : URLRequestJob(request, delegate), range_parse_result_(OK) {
-}
-
-URLRangeRequestJob::~URLRangeRequestJob() = default;
-
-void URLRangeRequestJob::SetExtraRequestHeaders(
-    const HttpRequestHeaders& headers) {
-  std::string range_header;
-  if (headers.GetHeader(HttpRequestHeaders::kRange, &range_header)) {
-    if (!HttpUtil::ParseRangeHeader(range_header, &ranges_)) {
-      range_parse_result_ = ERR_REQUEST_RANGE_NOT_SATISFIABLE;
-    }
-  }
-}
-
-}  // namespace net
diff --git a/net/url_request/url_range_request_job.h b/net/url_request/url_range_request_job.h
deleted file mode 100644
index 38a6802..0000000
--- a/net/url_request/url_range_request_job.h
+++ /dev/null
@@ -1,42 +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 NET_URL_REQUEST_URL_RANGE_REQUEST_JOB_H_
-#define NET_URL_REQUEST_URL_RANGE_REQUEST_JOB_H_
-
-#include <vector>
-
-#include "net/base/net_export.h"
-#include "net/http/http_byte_range.h"
-#include "net/url_request/url_request_job.h"
-
-namespace net {
-
-class HttpRequestHeaders;
-
-// URLRequestJob with support for parsing range requests.
-// It is up to subclasses to handle the response
-// and deal with an errors parsing the range request header.
-// This must be done after Start() has been called.
-class NET_EXPORT URLRangeRequestJob : public URLRequestJob {
- public:
-  URLRangeRequestJob(URLRequest* request,
-                     NetworkDelegate* delegate);
-
-  void SetExtraRequestHeaders(const HttpRequestHeaders& headers) override;
-
-  const std::vector<HttpByteRange>& ranges() const { return ranges_; }
-  int range_parse_result() const { return range_parse_result_; }
-
- protected:
-  ~URLRangeRequestJob() override;
-
- private:
-  std::vector<HttpByteRange> ranges_;
-  int range_parse_result_;
-};
-
-}  // namespace net
-
-#endif  // NET_URL_REQUEST_URL_RANGE_REQUEST_JOB_H_
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index dabcd7af..d478b42 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -442,21 +442,6 @@
   g_default_can_use_cookies = false;
 }
 
-// static
-bool URLRequest::IsHandledProtocol(const std::string& scheme) {
-  return URLRequestJobManager::SupportsScheme(scheme);
-}
-
-// static
-bool URLRequest::IsHandledURL(const GURL& url) {
-  if (!url.is_valid()) {
-    // We handle error cases.
-    return true;
-  }
-
-  return IsHandledProtocol(url.scheme());
-}
-
 void URLRequest::set_site_for_cookies(const GURL& site_for_cookies) {
   DCHECK(!is_pending_);
   site_for_cookies_ = site_for_cookies;
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index afbd582..b6680d8f 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -247,17 +247,6 @@
   // started. Once it was set to block all cookies, it cannot be changed back.
   static void SetDefaultCookiePolicyToBlock();
 
-  // Returns true if the scheme can be handled by URLRequest. False otherwise.
-  static bool IsHandledProtocol(const std::string& scheme);
-
-  // Returns true if the url can be handled by URLRequest. False otherwise.
-  // The function returns true for invalid urls because URLRequest knows how
-  // to handle those.
-  // NOTE: This will also return true for URLs that are handled by
-  // ProtocolFactories that only work for requests that are scoped to a
-  // Profile.
-  static bool IsHandledURL(const GURL& url);
-
   // The original url is the url used to initialize the request, and it may
   // differ from the url if the request was redirected.
   const GURL& original_url() const { return url_chain_.front(); }
diff --git a/net/url_request/url_request_data_job.h b/net/url_request/url_request_data_job.h
deleted file mode 100644
index cc3e7b66..0000000
--- a/net/url_request/url_request_data_job.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_URL_REQUEST_URL_REQUEST_DATA_JOB_H_
-#define NET_URL_REQUEST_URL_REQUEST_DATA_JOB_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "net/base/completion_once_callback.h"
-#include "net/base/net_export.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_simple_job.h"
-
-class GURL;
-
-namespace net {
-
-class HttpResponseHeaders;
-class URLRequest;
-
-class NET_EXPORT URLRequestDataJob : public URLRequestSimpleJob {
- public:
-  // Extracts info from a data scheme URL. Returns OK if successful. Returns
-  // ERR_INVALID_URL otherwise.
-  static int BuildResponse(const GURL& url,
-                           base::StringPiece method,
-                           std::string* mime_type,
-                           std::string* charset,
-                           std::string* data,
-                           HttpResponseHeaders* headers);
-
-  URLRequestDataJob(URLRequest* request, NetworkDelegate* network_delegate);
-
-  // URLRequestSimpleJob
-  int GetData(std::string* mime_type,
-              std::string* charset,
-              std::string* data,
-              CompletionOnceCallback callback) const override;
-
- private:
-  ~URLRequestDataJob() override;
-
-  DISALLOW_COPY_AND_ASSIGN(URLRequestDataJob);
-};
-
-}  // namespace net
-
-#endif  // NET_URL_REQUEST_URL_REQUEST_DATA_JOB_H_
diff --git a/net/url_request/url_request_simple_job.cc b/net/url_request/url_request_simple_job.cc
deleted file mode 100644
index 9d2316dd..0000000
--- a/net/url_request/url_request_simple_job.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/url_request/url_request_simple_job.h"
-
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/compiler_specific.h"
-#include "base/location.h"
-#include "base/memory/ref_counted_memory.h"
-#include "base/single_thread_task_runner.h"
-#include "base/task/post_task.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_util.h"
-#include "net/url_request/url_request_status.h"
-
-namespace net {
-
-namespace {
-
-void CopyData(const scoped_refptr<IOBuffer>& buf,
-              int buf_size,
-              const scoped_refptr<base::RefCountedMemory>& data,
-              int64_t data_offset) {
-  memcpy(buf->data(), data->front() + data_offset, buf_size);
-}
-
-}  // namespace
-
-URLRequestSimpleJob::URLRequestSimpleJob(URLRequest* request,
-                                         NetworkDelegate* network_delegate)
-    : URLRangeRequestJob(request, network_delegate), next_data_offset_(0) {}
-
-void URLRequestSimpleJob::Start() {
-  // Start reading asynchronously so that all error reporting and data
-  // callbacks happen as they would for network requests.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&URLRequestSimpleJob::StartAsync,
-                                weak_factory_.GetWeakPtr()));
-}
-
-void URLRequestSimpleJob::Kill() {
-  weak_factory_.InvalidateWeakPtrs();
-  URLRangeRequestJob::Kill();
-}
-
-bool URLRequestSimpleJob::GetMimeType(std::string* mime_type) const {
-  *mime_type = mime_type_;
-  return true;
-}
-
-bool URLRequestSimpleJob::GetCharset(std::string* charset) {
-  *charset = charset_;
-  return true;
-}
-
-URLRequestSimpleJob::~URLRequestSimpleJob() = default;
-
-int URLRequestSimpleJob::ReadRawData(IOBuffer* buf, int buf_size) {
-  buf_size = std::min(static_cast<int64_t>(buf_size),
-                      byte_range_.last_byte_position() - next_data_offset_ + 1);
-  if (buf_size == 0)
-    return 0;
-
-  // Do memory copy asynchronously on a thread that is not the network thread.
-  // See crbug.com/422489.
-  base::PostTaskAndReply(
-      FROM_HERE,
-      {base::ThreadPool(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-      base::BindOnce(&CopyData, base::WrapRefCounted(buf), buf_size, data_,
-                     next_data_offset_),
-      base::BindOnce(&URLRequestSimpleJob::ReadRawDataComplete,
-                     weak_factory_.GetWeakPtr(), buf_size));
-  next_data_offset_ += buf_size;
-  return ERR_IO_PENDING;
-}
-
-int URLRequestSimpleJob::GetData(std::string* mime_type,
-                                 std::string* charset,
-                                 std::string* data,
-                                 CompletionOnceCallback callback) const {
-  NOTREACHED();
-  return ERR_UNEXPECTED;
-}
-
-int URLRequestSimpleJob::GetRefCountedData(
-    std::string* mime_type,
-    std::string* charset,
-    scoped_refptr<base::RefCountedMemory>* data,
-    CompletionOnceCallback callback) const {
-  scoped_refptr<base::RefCountedString> str_data(new base::RefCountedString());
-  int result =
-      GetData(mime_type, charset, &str_data->data(), std::move(callback));
-  *data = str_data;
-  return result;
-}
-
-void URLRequestSimpleJob::StartAsync() {
-  if (!request_)
-    return;
-
-  if (ranges().size() > 1) {
-    NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED,
-                                      ERR_REQUEST_RANGE_NOT_SATISFIABLE));
-    return;
-  }
-
-  if (!ranges().empty() && range_parse_result() == OK)
-    byte_range_ = ranges().front();
-
-  const int result =
-      GetRefCountedData(&mime_type_, &charset_, &data_,
-                        base::BindOnce(&URLRequestSimpleJob::OnGetDataCompleted,
-                                       weak_factory_.GetWeakPtr()));
-
-  if (result != ERR_IO_PENDING)
-    OnGetDataCompleted(result);
-}
-
-void URLRequestSimpleJob::OnGetDataCompleted(int result) {
-  if (result == OK) {
-    // Notify that the headers are complete
-    if (!byte_range_.ComputeBounds(data_->size())) {
-      NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED,
-                                        ERR_REQUEST_RANGE_NOT_SATISFIABLE));
-      return;
-    }
-
-    next_data_offset_ = byte_range_.first_byte_position();
-    set_expected_content_size(byte_range_.last_byte_position() -
-                              next_data_offset_ + 1);
-    NotifyHeadersComplete();
-  } else {
-    NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, result));
-  }
-}
-
-}  // namespace net
diff --git a/net/url_request/url_request_simple_job.h b/net/url_request/url_request_simple_job.h
deleted file mode 100644
index 18ead7d..0000000
--- a/net/url_request/url_request_simple_job.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_URL_REQUEST_URL_REQUEST_SIMPLE_JOB_H_
-#define NET_URL_REQUEST_URL_REQUEST_SIMPLE_JOB_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/string_piece.h"
-#include "net/base/completion_once_callback.h"
-#include "net/base/net_export.h"
-#include "net/url_request/url_range_request_job.h"
-
-namespace base {
-class RefCountedMemory;
-}
-
-namespace net {
-
-class URLRequest;
-
-class NET_EXPORT URLRequestSimpleJob : public URLRangeRequestJob {
- public:
-  URLRequestSimpleJob(URLRequest* request, NetworkDelegate* network_delegate);
-
-  void Start() override;
-  void Kill() override;
-  int ReadRawData(IOBuffer* buf, int buf_size) override;
-  bool GetMimeType(std::string* mime_type) const override;
-  bool GetCharset(std::string* charset) override;
-
- protected:
-  ~URLRequestSimpleJob() override;
-
-  // Subclasses must override either GetData or GetRefCountedData to define the
-  // way response data is determined.
-  // The return value should be:
-  //  - OK if data is obtained;
-  //  - ERR_IO_PENDING if async processing is needed to finish obtaining data.
-  //    This is the only case when |callback| should be called after
-  //    completion of the operation. In other situations |callback| should
-  //    never be called;
-  //  - any other ERR_* code to indicate an error. This code will be used
-  //    as the error code in the URLRequestStatus when the URLRequest
-  //    is finished.
-  virtual int GetData(std::string* mime_type,
-                      std::string* charset,
-                      std::string* data,
-                      CompletionOnceCallback callback) const;
-
-  // Similar to GetData(), except |*data| can share ownership of the bytes
-  // instead of copying them into a std::string.
-  virtual int GetRefCountedData(std::string* mime_type,
-                                std::string* charset,
-                                scoped_refptr<base::RefCountedMemory>* data,
-                                CompletionOnceCallback callback) const;
-
-  void StartAsync();
-
- private:
-  void OnGetDataCompleted(int result);
-
-  HttpByteRange byte_range_;
-  std::string mime_type_;
-  std::string charset_;
-  scoped_refptr<base::RefCountedMemory> data_;
-  int64_t next_data_offset_;
-  base::WeakPtrFactory<URLRequestSimpleJob> weak_factory_{this};
-};
-
-}  // namespace net
-
-#endif  // NET_URL_REQUEST_URL_REQUEST_SIMPLE_JOB_H_
diff --git a/net/url_request/url_request_simple_job_unittest.cc b/net/url_request/url_request_simple_job_unittest.cc
deleted file mode 100644
index 81c2440..0000000
--- a/net/url_request/url_request_simple_job_unittest.cc
+++ /dev/null
@@ -1,229 +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.
-
-#include "net/url_request/url_request_simple_job.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind_helpers.h"
-#include "base/run_loop.h"
-#include "base/sequenced_task_runner.h"
-#include "base/stl_util.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/stringprintf.h"
-#include "net/base/request_priority.h"
-#include "net/test/gtest_util.h"
-#include "net/test/test_with_task_environment.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_request_job.h"
-#include "net/url_request/url_request_job_factory.h"
-#include "net/url_request/url_request_job_factory_impl.h"
-#include "net/url_request/url_request_test_util.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using net::test::IsError;
-using net::test::IsOk;
-
-namespace net {
-
-namespace {
-
-const char kTestData[] = "Huge data array";
-const int kRangeFirstPosition = 5;
-const int kRangeLastPosition = 8;
-static_assert(kRangeFirstPosition > 0 &&
-                  kRangeFirstPosition < kRangeLastPosition &&
-                  kRangeLastPosition <
-                      static_cast<int>(base::size(kTestData) - 1),
-              "invalid range");
-
-class MockSimpleJob : public URLRequestSimpleJob {
- public:
-  MockSimpleJob(URLRequest* request,
-                NetworkDelegate* network_delegate,
-                base::StringPiece data)
-      : URLRequestSimpleJob(request, network_delegate),
-        data_(data.as_string()) {}
-
- protected:
-  // URLRequestSimpleJob implementation:
-  int GetData(std::string* mime_type,
-              std::string* charset,
-              std::string* data,
-              CompletionOnceCallback callback) const override {
-    mime_type->assign("text/plain");
-    charset->assign("US-ASCII");
-    data->assign(data_);
-    return OK;
-  }
-
- private:
-  ~MockSimpleJob() override = default;
-
-  const std::string data_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockSimpleJob);
-};
-
-class CancelAfterFirstReadURLRequestDelegate : public TestDelegate {
- public:
-  CancelAfterFirstReadURLRequestDelegate() : run_loop_(new base::RunLoop) {}
-
-  ~CancelAfterFirstReadURLRequestDelegate() override = default;
-
-  void OnResponseStarted(URLRequest* request, int net_error) override {
-    DCHECK_NE(ERR_IO_PENDING, net_error);
-    // net::TestDelegate will start the first read.
-    TestDelegate::OnResponseStarted(request, net_error);
-    request->Cancel();
-    run_loop_->Quit();
-  }
-
-  void WaitUntilHeadersReceived() const { run_loop_->Run(); }
-
- private:
-  std::unique_ptr<base::RunLoop> run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(CancelAfterFirstReadURLRequestDelegate);
-};
-
-class SimpleJobProtocolHandler :
-    public URLRequestJobFactory::ProtocolHandler {
- public:
-  SimpleJobProtocolHandler() = default;
-  URLRequestJob* MaybeCreateJob(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    if (request->url().spec() == "data:empty")
-      return new MockSimpleJob(request, network_delegate, "");
-    return new MockSimpleJob(request, network_delegate, kTestData);
-  }
-
-  ~SimpleJobProtocolHandler() override = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SimpleJobProtocolHandler);
-};
-
-class URLRequestSimpleJobTest : public TestWithTaskEnvironment {
- public:
-  URLRequestSimpleJobTest() : context_(true) {
-    job_factory_.SetProtocolHandler(
-        "data", std::make_unique<SimpleJobProtocolHandler>());
-    context_.set_job_factory(&job_factory_);
-    context_.Init();
-
-    request_ = context_.CreateRequest(GURL("data:test"), DEFAULT_PRIORITY,
-                                      &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
-  }
-
-  void StartRequest(const HttpRequestHeaders* headers) {
-    if (headers)
-      request_->SetExtraRequestHeaders(*headers);
-    request_->Start();
-
-    EXPECT_TRUE(request_->is_pending());
-    delegate_.RunUntilComplete();
-    EXPECT_FALSE(request_->is_pending());
-  }
-
- protected:
-  TestURLRequestContext context_;
-  URLRequestJobFactoryImpl job_factory_;
-  TestDelegate delegate_;
-  std::unique_ptr<URLRequest> request_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(URLRequestSimpleJobTest);
-};
-
-}  // namespace
-
-TEST_F(URLRequestSimpleJobTest, SimpleRequest) {
-  StartRequest(nullptr);
-  EXPECT_THAT(delegate_.request_status(), IsOk());
-  EXPECT_EQ(kTestData, delegate_.data_received());
-}
-
-TEST_F(URLRequestSimpleJobTest, RangeRequest) {
-  const std::string kExpectedBody = std::string(
-      kTestData + kRangeFirstPosition, kTestData + kRangeLastPosition + 1);
-  HttpRequestHeaders headers;
-  headers.SetHeader(
-      HttpRequestHeaders::kRange,
-      HttpByteRange::Bounded(kRangeFirstPosition, kRangeLastPosition)
-          .GetHeaderValue());
-
-  StartRequest(&headers);
-
-  EXPECT_THAT(delegate_.request_status(), IsOk());
-  EXPECT_EQ(kExpectedBody, delegate_.data_received());
-}
-
-TEST_F(URLRequestSimpleJobTest, MultipleRangeRequest) {
-  HttpRequestHeaders headers;
-  int middle_pos = (kRangeFirstPosition + kRangeLastPosition)/2;
-  std::string range = base::StringPrintf("bytes=%d-%d,%d-%d",
-                                         kRangeFirstPosition,
-                                         middle_pos,
-                                         middle_pos + 1,
-                                         kRangeLastPosition);
-  headers.SetHeader(HttpRequestHeaders::kRange, range);
-
-  StartRequest(&headers);
-
-  EXPECT_TRUE(delegate_.request_failed());
-  EXPECT_EQ(ERR_REQUEST_RANGE_NOT_SATISFIABLE, delegate_.request_status());
-}
-
-TEST_F(URLRequestSimpleJobTest, InvalidRangeRequest) {
-  HttpRequestHeaders headers;
-  std::string range = base::StringPrintf(
-      "bytes=%d-%d", kRangeLastPosition, kRangeFirstPosition);
-  headers.SetHeader(HttpRequestHeaders::kRange, range);
-
-  StartRequest(&headers);
-
-  EXPECT_THAT(delegate_.request_status(), IsOk());
-  EXPECT_EQ(kTestData, delegate_.data_received());
-}
-
-TEST_F(URLRequestSimpleJobTest, EmptyDataRequest) {
-  request_ = context_.CreateRequest(GURL("data:empty"), DEFAULT_PRIORITY,
-                                    &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
-  StartRequest(nullptr);
-  EXPECT_THAT(delegate_.request_status(), IsOk());
-  EXPECT_EQ("", delegate_.data_received());
-}
-
-TEST_F(URLRequestSimpleJobTest, CancelBeforeResponseStarts) {
-  request_ = context_.CreateRequest(GURL("data:cancel"), DEFAULT_PRIORITY,
-                                    &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
-  request_->Start();
-  request_->Cancel();
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_THAT(delegate_.request_status(), IsError(ERR_ABORTED));
-  EXPECT_EQ(1, delegate_.response_started_count());
-}
-
-TEST_F(URLRequestSimpleJobTest, CancelAfterFirstReadStarted) {
-  CancelAfterFirstReadURLRequestDelegate cancel_delegate;
-  request_ =
-      context_.CreateRequest(GURL("data:cancel"), DEFAULT_PRIORITY,
-                             &cancel_delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
-  request_->Start();
-  cancel_delegate.WaitUntilHeadersReceived();
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_THAT(cancel_delegate.request_status(), IsError(ERR_ABORTED));
-  EXPECT_EQ(1, cancel_delegate.response_started_count());
-  EXPECT_EQ("", cancel_delegate.data_received());
-  // Destroy the request so it doesn't outlive its delegate.
-  request_.reset();
-}
-
-}  // namespace net
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 02e7af8..1ba8249 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -117,7 +117,6 @@
 #include "net/test/url_request/url_request_failed_job.h"
 #include "net/test/url_request/url_request_mock_http_job.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/data_protocol_handler.h"
 #include "net/url_request/static_http_user_agent_settings.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_filter.h"
@@ -314,6 +313,16 @@
 }
 #endif
 
+// Less verbose way of running a simple testserver for the tests below.
+class HttpTestServer : public EmbeddedTestServer {
+ public:
+  explicit HttpTestServer(const base::FilePath& document_root) {
+    AddDefaultHandlers(document_root);
+  }
+
+  HttpTestServer() { AddDefaultHandlers(base::FilePath()); }
+};
+
 // Job that allows monitoring of its priority.
 class PriorityMonitoringURLRequestJob : public URLRequestTestJob {
  public:
@@ -699,10 +708,7 @@
 
   void TearDown() override { default_context_.reset(); }
 
-  virtual void SetUpFactory() {
-    job_factory_impl_->SetProtocolHandler(
-        "data", std::make_unique<DataProtocolHandler>());
-  }
+  virtual void SetUpFactory() {}
 
   TestNetworkDelegate* default_network_delegate() {
     return &default_network_delegate_;
@@ -764,46 +770,6 @@
   }
 }
 
-TEST_F(URLRequestTest, DataURLImageTest) {
-  TestDelegate d;
-  {
-    // Use our nice little Chrome logo.
-    std::unique_ptr<URLRequest> r(default_context().CreateRequest(
-        GURL("data:image/png;base64,"
-             "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADVklEQVQ4jX2TfUwUB"
-             "BjG3w1y+HGcd9dxhXR8T4awOccJGgOSWclHImznLkTlSw0DDQXkrmgYgbUYnlQTqQ"
-             "xIEVxitD5UMCATRA1CEEg+Qjw3bWDxIauJv/5oumqs39/P827vnucRmYN0gyF01GI"
-             "5MpCVdW0gO7tvNC+vqSEtbZefk5NuLv1jdJ46p/zw0HeH4+PHr3h7c1mjoV2t5rKz"
-             "Mx1+fg9bAgK6zHq9cU5z+LpA3xOtx34+vTeT21onRuzssC3zxbbSwC13d/pFuC7Ck"
-             "IMDxQpF7r/MWq12UctI1dWWm99ypqSYmRUBdKem8MkrO/kgaTt1O7YzlpzE5GIVd0"
-             "WYUqt57yWf2McHTObYPbVD+ZwbtlLTVMZ3BW+TnLyXLaWtmEq6WJVbT3HBh3Svj2H"
-             "QQcm43XwmtoYM6vVKleh0uoWvnzW3v3MpidruPTQPf0bia7sJOtBM0ufTWNvus/nk"
-             "DFHF9ZS+uYVjRUasMeHUmyLYtcklTvzWGFZnNOXczThvpKIzjcahSqIzkvDLayDq6"
-             "D3eOjtBbNUEIZYyqsvj4V4wY92eNJ4IoyhTbxXX1T5xsV9tm9r4TQwHLiZw/pdDZJ"
-             "ea8TKmsmR/K0uLh/GwnCHghTja6lPhphezPfO5/5MrVvMzNaI3+ERHfrFzPKQukrQ"
-             "GI4d/3EFD/3E2mVNYvi4at7CXWREaxZGD+3hg28zD3gVMd6q5c8GdosynKmSeRuGz"
-             "pjyl1/9UDGtPR5HeaKT8Wjo17WXk579BXVUhN64ehF9fhRtq/uxxZKzNiZFGD0wRC"
-             "3NFROZ5mwIPL/96K/rKMMLrIzF9uhHr+/sYH7DAbwlgC4J+R2Z7FUx1qLnV7MGF40"
-             "smVSoJ/jvHRfYhQeUJd/SnYtGWhPHR0Sz+GE2F2yth0B36Vcz2KpnufBJbsysjjW4"
-             "kblBUiIjiURUWqJY65zxbnTy57GQyH58zgy0QBtTQv5gH15XMdKkYu+TGaJMnlm2O"
-             "34uI4b9tflqp1+QEFGzoW/ulmcofcpkZCYJhDfSpme7QcrHa+Xfji8paEQkTkSfmm"
-             "oRWRNZr/F1KfVMjW+IKEnv2FwZfKdzt0BQR6lClcZR0EfEXEfv/G6W9iLiIyCoReV"
-             "5EnhORIBHx+ufPj/gLB/zGI/G4Bk0AAAAASUVORK5CYII="),
-        DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
-
-    r->Start();
-    EXPECT_TRUE(r->is_pending());
-
-    d.RunUntilComplete();
-
-    EXPECT_TRUE(!r->is_pending());
-    EXPECT_FALSE(d.received_data_before_response());
-    EXPECT_EQ(d.bytes_received(), 911);
-    EXPECT_TRUE(r->GetResponseRemoteEndpoint().address().empty());
-    EXPECT_EQ(0, r->GetResponseRemoteEndpoint().port());
-  }
-}
-
 TEST_F(URLRequestTest, InvalidUrlTest) {
   TestDelegate d;
   {
@@ -1271,11 +1237,17 @@
 // Make sure that NetworkDelegate::NotifyCompleted is called if
 // content is empty.
 TEST_F(URLRequestTest, RequestCompletionForEmptyResponse) {
+  HttpTestServer test_server;
+  ASSERT_TRUE(test_server.Start());
+
   TestDelegate d;
   std::unique_ptr<URLRequest> req(default_context().CreateRequest(
-      GURL("data:,"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+      test_server.GetURL("/nocontent"), DEFAULT_PRIORITY, &d,
+      TRAFFIC_ANNOTATION_FOR_TESTS));
   req->Start();
   d.RunUntilComplete();
+  EXPECT_THAT(d.request_status(), IsOk());
+  EXPECT_EQ(204, req->GetResponseCode());
   EXPECT_EQ("", d.data_received());
   EXPECT_EQ(1, default_network_delegate_.completed_requests());
 }
@@ -1367,20 +1339,6 @@
   EXPECT_EQ(MAXIMUM_PRIORITY, job_priority);
 }
 
-namespace {
-
-// Less verbose way of running a simple testserver for the tests below.
-class HttpTestServer : public EmbeddedTestServer {
- public:
-  explicit HttpTestServer(const base::FilePath& document_root) {
-    AddDefaultHandlers(document_root);
-  }
-
-  HttpTestServer() { AddDefaultHandlers(base::FilePath()); }
-};
-
-}  // namespace
-
 TEST_F(URLRequestTest, DelayedCookieCallback) {
   HttpTestServer test_server;
   ASSERT_TRUE(test_server.Start());
@@ -3061,6 +3019,37 @@
   URLRequestTestHTTP() : test_server_(base::FilePath(kTestFilePath)) {}
 
  protected:
+  // ProtocolHandler for the scheme that's unsafe to redirect to.
+  class NET_EXPORT UnsafeRedirectProtocolHandler
+      : public URLRequestJobFactory::ProtocolHandler {
+   public:
+    UnsafeRedirectProtocolHandler() = default;
+    ~UnsafeRedirectProtocolHandler() override = default;
+
+    // URLRequestJobFactory::ProtocolHandler implementation:
+
+    URLRequestJob* MaybeCreateJob(
+        URLRequest* request,
+        NetworkDelegate* network_delegate) const override {
+      NOTREACHED();
+      return nullptr;
+    }
+
+    bool IsSafeRedirectTarget(const GURL& location) const override {
+      return false;
+    }
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(UnsafeRedirectProtocolHandler);
+  };
+
+  // URLRequestTest interface:
+  void SetUpFactory() override {
+    // Add FTP support to the default URLRequestContext.
+    job_factory_impl_->SetProtocolHandler(
+        "unsafe", std::make_unique<UnsafeRedirectProtocolHandler>());
+  }
+
   // Requests |redirect_url|, which must return a HTTP 3xx redirect.
   // |request_method| is the method to use for the initial request.
   // |redirect_method| is the method that is expected to be used for the second
@@ -6122,17 +6111,7 @@
   req->Cancel();
 }
 
-TEST_F(URLRequestTestHTTP, ProtocolHandlerAndFactoryRestrictDataRedirects) {
-  // Test URLRequestJobFactory::ProtocolHandler::IsSafeRedirectTarget().
-  GURL data_url("data:,foo");
-  DataProtocolHandler data_protocol_handler;
-  EXPECT_FALSE(data_protocol_handler.IsSafeRedirectTarget(data_url));
-
-  // Test URLRequestJobFactoryImpl::IsSafeRedirectTarget().
-  EXPECT_FALSE(job_factory_->IsSafeRedirectTarget(data_url));
-}
-
-TEST_F(URLRequestTestHTTP, RestrictFileRedirects) {
+TEST_F(URLRequestTestHTTP, FileRedirect) {
   ASSERT_TRUE(http_test_server()->Start());
 
   TestDelegate d;
@@ -6146,7 +6125,7 @@
   EXPECT_EQ(1, d.received_redirect_count());
 }
 
-TEST_F(URLRequestTestHTTP, RestrictDataRedirects) {
+TEST_F(URLRequestTestHTTP, DataRedirect) {
   ASSERT_TRUE(http_test_server()->Start());
 
   TestDelegate d;
@@ -6156,6 +6135,21 @@
   req->Start();
   d.RunUntilComplete();
 
+  EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, d.request_status());
+  EXPECT_EQ(1, d.received_redirect_count());
+}
+
+TEST_F(URLRequestTestHTTP, RestrictUnsafeRedirect) {
+  ASSERT_TRUE(http_test_server()->Start());
+
+  TestDelegate d;
+  std::unique_ptr<URLRequest> req(default_context().CreateRequest(
+      http_test_server()->GetURL(
+          "/server-redirect?unsafe://here-there-be-dragons"),
+      DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
+  req->Start();
+  d.RunUntilComplete();
+
   EXPECT_EQ(ERR_UNSAFE_REDIRECT, d.request_status());
 
   // The redirect should have been rejected before reporting it to the
@@ -11111,20 +11105,6 @@
 
 #endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
 
-TEST_F(URLRequestTest, NetworkAccessedClearOnDataRequest) {
-  TestDelegate d;
-  std::unique_ptr<URLRequest> req(default_context().CreateRequest(
-      GURL("data:,"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
-
-  EXPECT_FALSE(req->response_info().network_accessed);
-
-  req->Start();
-  d.RunUntilComplete();
-
-  EXPECT_EQ(1, default_network_delegate_.completed_requests());
-  EXPECT_FALSE(req->response_info().network_accessed);
-}
-
 TEST_F(URLRequestTest, NetworkAccessedSetOnHostResolutionFailure) {
   MockHostResolver host_resolver;
   TestNetworkDelegate network_delegate;  // Must outlive URLRequest.
@@ -11354,23 +11334,6 @@
   EXPECT_EQ("Not Modified", raw_resp_headers[2]->GetStatusText());
 }
 
-TEST_F(URLRequestTest, HeadersCallbacksNonHTTP) {
-  GURL data_url("data:text/html,<html><body>Hello!</body></html>");
-  TestDelegate d;
-  std::unique_ptr<URLRequest> r(default_context().CreateRequest(
-      data_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
-  r->SetRequestHeadersCallback(base::Bind([](net::HttpRawRequestHeaders) {
-    FAIL() << "Callback should not be called for non-HTTP schemes";
-  }));
-  r->SetResponseHeadersCallback(
-      base::Bind([](scoped_refptr<const net::HttpResponseHeaders>) {
-        FAIL() << "Callback should not be called for non-HTTP schemes";
-      }));
-  r->Start();
-  d.RunUntilComplete();
-  EXPECT_FALSE(r->is_pending());
-}
-
 TEST_F(URLRequestTest, UpgradeIfInsecureFlagSet) {
   TestDelegate d;
   BlockingNetworkDelegate network_delegate(
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index e7b1f926..a356cd0a 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -1560,7 +1560,7 @@
   // Capture a single screen image to trigger OS permission checks which will
   // add our binary to the list of apps that can be granted permission. This
   // is only needed for OS X 10.15 and later.
-  if (!base::mac::IsAtLeastOS10_15()) {
+  if (base::mac::IsAtLeastOS10_15()) {
     capture_checker_.reset(new DesktopCapturerChecker());
     capture_checker_->TriggerSingleCapture();
   }
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 41c4cb46..b1065f9 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -329,8 +329,12 @@
 }
 
 std::unique_ptr<NetworkService> NetworkService::CreateForTesting() {
-  return std::make_unique<NetworkService>(
-      std::make_unique<service_manager::BinderRegistry>());
+  auto network_service = std::make_unique<NetworkService>(
+      std::make_unique<service_manager::BinderRegistry>(), mojo::NullReceiver(),
+      true /* delay_initialization_until_set_client */);
+  network_service->Initialize(mojom::NetworkServiceParams::New(),
+                              true /* mock_network_change_notifier */);
+  return network_service;
 }
 
 void NetworkService::RegisterNetworkContext(NetworkContext* network_context) {
diff --git a/services/network/network_service.h b/services/network/network_service.h
index 59472121..3d1e923 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -86,8 +86,9 @@
   static std::unique_ptr<NetworkService> Create(
       mojo::PendingReceiver<mojom::NetworkService> receiver);
 
-  // Creates a testing instance of NetworkService not bound to an actual
-  // Service pipe. This instance must be driven by direct calls onto the
+  // Creates a testing instance of NetworkService not bound to an actual Service
+  // pipe. This will cause NetworkChangeNotifier, unless already created, to be
+  // mocked. This instance must be driven by direct calls onto the
   // NetworkService object.
   static std::unique_ptr<NetworkService> CreateForTesting();
 
diff --git a/services/network/public/cpp/network_mojom_traits_unittest.cc b/services/network/public/cpp/network_mojom_traits_unittest.cc
index c4c5bbc..1aee1d9f 100644
--- a/services/network/public/cpp/network_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/network_mojom_traits_unittest.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/message_loop/message_loop.h"
 #include "base/strings/string_util.h"
+#include "base/test/task_environment.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -34,7 +34,7 @@
     std::move(callback).Run(header);
   }
 
-  base::MessageLoop loop_;
+  base::test::SingleThreadTaskEnvironment task_environment_;
   mojo::ReceiverSet<TraitsTestService> traits_test_receivers_;
   DISALLOW_COPY_AND_ASSIGN(NetworkStructTraitsTest);
 };
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index bb4c69b..611a7b5 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -7005,6 +7005,8 @@
   "GPU FYI Win dEQP Builder": {},
   "GPU FYI Win x64 Builder": {},
   "GPU FYI Win x64 Builder (dbg)": {},
+  "GPU FYI Win x64 DX12 Vulkan Builder": {},
+  "GPU FYI Win x64 DX12 Vulkan Builder (dbg)": {},
   "GPU FYI Win x64 dEQP Builder": {},
   "Linux FYI Debug (NVIDIA)": {
     "gtest_tests": [
@@ -18317,6 +18319,72 @@
       }
     ]
   },
+  "Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "noop_sleep",
+          "--show-stdout",
+          "--browser=debug_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "noop_sleep_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      }
+    ]
+  },
+  "Win10 FYI x64 DX12 Vulkan Release (NVIDIA)": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "noop_sleep",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "noop_sleep_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-win10-stable",
+              "os": "Windows-10",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "idempotent": false
+        }
+      }
+    ]
+  },
   "Win10 FYI x64 Debug (NVIDIA)": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 0d49e3c..1c2438fb 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2650,6 +2650,8 @@
       'GPU FYI Win dEQP Builder': {},
       'GPU FYI Win x64 Builder' : {},
       'GPU FYI Win x64 Builder (dbg)' : {},
+      'GPU FYI Win x64 DX12 Vulkan Builder': {},
+      'GPU FYI Win x64 DX12 Vulkan Builder (dbg)': {},
       'GPU FYI Win x64 dEQP Builder' : {},
       'Linux FYI Debug (NVIDIA)': {
         'browser_config': 'debug',
@@ -3072,6 +3074,26 @@
         'use_multi_dimension_trigger_script': True,
       },
       # END Fake builder used as mirror targets for Optional GPU tryservers
+      'Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)': {
+        'os_type': 'win',
+        'browser_config': 'debug_x64',
+        'mixins': [
+          'win10_nvidia_quadro_p400_stable',
+        ],
+        'test_suites': {
+          'gpu_telemetry_tests': 'gpu_noop_sleep_telemetry_test',
+        },
+      },
+      'Win10 FYI x64 DX12 Vulkan Release (NVIDIA)': {
+        'os_type': 'win',
+        'browser_config': 'release_x64',
+        'mixins': [
+          'win10_nvidia_quadro_p400_stable',
+        ],
+        'test_suites': {
+          'gpu_telemetry_tests': 'gpu_noop_sleep_telemetry_test',
+        },
+      },
       'Win10 FYI x64 Debug (NVIDIA)': {
         'os_type': 'win',
         'browser_config': 'debug_x64',
diff --git a/testing/libfuzzer/fuzzers/template_url_parser_fuzzer.cc b/testing/libfuzzer/fuzzers/template_url_parser_fuzzer.cc
index 64e91223dd..06889bc 100644
--- a/testing/libfuzzer/fuzzers/template_url_parser_fuzzer.cc
+++ b/testing/libfuzzer/fuzzers/template_url_parser_fuzzer.cc
@@ -11,28 +11,21 @@
 #include "libxml/parser.h"
 
 #include "base/at_exit.h"
+#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/i18n/icu_util.h"
 #include "components/search_engines/search_terms_data.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_parser.h"
+#include "testing/libfuzzer/libfuzzer_exports.h"
 
-class PseudoRandomFilter : public TemplateURLParser::ParameterFilter {
- public:
-  explicit PseudoRandomFilter(uint32_t seed) : generator_(seed), pool_(0, 1) {}
-  ~PseudoRandomFilter() override = default;
-
-  bool KeepParameter(const std::string&, const std::string&) override {
-    // Return true 254/255 times, ie: as if pool_ only returned uint8_t.
-    return pool_(generator_) % (UINT8_MAX + 1);
-  }
-
- private:
-  std::mt19937 generator_;
-  // Use a uint16_t here instead of uint8_t because uniform_int_distribution
-  // does not support 8 bit types on Windows.
-  std::uniform_int_distribution<uint16_t> pool_;
-};
+bool PseudoRandomFilter(std::mt19937* generator,
+                        std::uniform_int_distribution<uint16_t>* pool,
+                        const std::string&,
+                        const std::string&) {
+  // Return true 254/255 times, ie: as if pool only returned uint8_t.
+  return (*pool)(*generator) % (UINT8_MAX + 1);
+}
 
 struct FuzzerFixedParams {
   uint32_t seed_;
@@ -60,11 +53,21 @@
   if (size < sizeof(FuzzerFixedParams)) {
     return 0;
   }
+
   const FuzzerFixedParams* params =
       reinterpret_cast<const FuzzerFixedParams*>(data);
   size -= sizeof(FuzzerFixedParams);
+
+  std::mt19937 generator(params->seed_);
+  // Use a uint16_t here instead of uint8_t because uniform_int_distribution
+  // does not support 8 bit types on Windows.
+  std::uniform_int_distribution<uint16_t> pool(0, 1);
+
+  TemplateURLParser::ParameterFilter filter =
+      base::BindRepeating(&PseudoRandomFilter, base::Unretained(&generator),
+                          base::Unretained(&pool));
+
   const char* char_data = reinterpret_cast<const char*>(params + 1);
-  PseudoRandomFilter filter(params->seed_);
-  TemplateURLParser::Parse(SearchTermsData(), char_data, size, &filter);
+  TemplateURLParser::Parse(SearchTermsData(), char_data, size, filter);
   return 0;
 }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index f98e910..1d2d621 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5304,6 +5304,24 @@
             ]
         }
     ],
+    "SafeBrowsingRealTimeUrlLookupFetchAllowlist": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SafeBrowsingRealTimeUrlLookupFetchAllowlist"
+                    ]
+                }
+            ]
+        }
+    ],
     "SafeBrowsingScoutTransitionStudy": [
         {
             "platforms": [
diff --git a/third_party/.gitignore b/third_party/.gitignore
index af2e721..9ee2a01 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -95,7 +95,6 @@
 /google_appengine_cloudstorage
 /google_toolbox_for_mac/src
 /googlemac
-/googletest/src
 /grpc/src
 /gson/lib/
 /gvr-android-sdk/common_library.aar
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
index 878536c..f2d49df 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -63,6 +63,7 @@
 #include "third_party/blink/renderer/core/script/modulator.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
 #include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
@@ -335,7 +336,9 @@
     return;
 
   auto* script_controller =
-      To<WorkerGlobalScope>(execution_context)->ScriptController();
+      execution_context->IsWorkerGlobalScope()
+          ? To<WorkerGlobalScope>(execution_context)->ScriptController()
+          : To<WorkletGlobalScope>(execution_context)->ScriptController();
   DCHECK(script_controller);
 
   PromiseRejectHandler(data, *script_controller->GetRejectedPromises(),
diff --git a/third_party/blink/renderer/core/css/css_paint_value.cc b/third_party/blink/renderer/core/css/css_paint_value.cc
index e2571f0dc..bdc83aa 100644
--- a/third_party/blink/renderer/core/css/css_paint_value.cc
+++ b/third_party/blink/renderer/core/css/css_paint_value.cc
@@ -21,14 +21,19 @@
 
 namespace blink {
 
-CSSPaintValue::CSSPaintValue(CSSCustomIdentValue* name)
+CSSPaintValue::CSSPaintValue(CSSCustomIdentValue* name,
+                             bool threaded_compositing_enabled)
     : CSSImageGeneratorValue(kPaintClass),
       name_(name),
       paint_image_generator_observer_(MakeGarbageCollected<Observer>(this)),
       off_thread_paint_state_(
-          RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()
-              ? OffThreadPaintState::kUnknown
-              : OffThreadPaintState::kMainThread) {}
+          (!threaded_compositing_enabled ||
+           !RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled())
+              ? OffThreadPaintState::kMainThread
+              : OffThreadPaintState::kUnknown) {}
+
+CSSPaintValue::CSSPaintValue(CSSCustomIdentValue* name)
+    : CSSPaintValue(name, Thread::CompositorThread()) {}
 
 CSSPaintValue::CSSPaintValue(
     CSSCustomIdentValue* name,
diff --git a/third_party/blink/renderer/core/css/css_paint_value.h b/third_party/blink/renderer/core/css/css_paint_value.h
index fcf98c8..0e85bdb 100644
--- a/third_party/blink/renderer/core/css/css_paint_value.h
+++ b/third_party/blink/renderer/core/css/css_paint_value.h
@@ -20,6 +20,7 @@
 class CORE_EXPORT CSSPaintValue : public CSSImageGeneratorValue {
  public:
   explicit CSSPaintValue(CSSCustomIdentValue* name);
+  CSSPaintValue(CSSCustomIdentValue* name, bool threaded_compositing_enabled);
   CSSPaintValue(CSSCustomIdentValue* name,
                 Vector<scoped_refptr<CSSVariableData>>&);
   ~CSSPaintValue();
diff --git a/third_party/blink/renderer/core/css/css_paint_value_test.cc b/third_party/blink/renderer/core/css/css_paint_value_test.cc
index 03fe77e1..b32f0a1 100644
--- a/third_party/blink/renderer/core/css/css_paint_value_test.cc
+++ b/third_party/blink/renderer/core/css/css_paint_value_test.cc
@@ -89,7 +89,7 @@
   const ComputedStyle& style = *target->Style();
 
   auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
-  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
   // Mark the generator as ready - GetImage should succeed when
   // OffMainThreadCSSPaint is enabled.
   ON_CALL(*mock_generator, IsImageGeneratorReady()).WillByDefault(Return(true));
@@ -135,7 +135,7 @@
   LayoutObject* target = GetLayoutObjectByElementId("target");
   auto style = ComputedStyle::Create();
   auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
-  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
   StyleGeneratedImage* style_image =
       MakeGarbageCollected<StyleGeneratedImage>(*paint_value);
   style->SetBorderImageSource(style_image);
@@ -187,7 +187,7 @@
   const ComputedStyle& style = *target->Style();
 
   auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
-  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
 
   // Initially the generator is not ready, so GetImage should fail (and no paint
   // should happen).
@@ -220,7 +220,7 @@
   const ComputedStyle& style = *target->Style();
 
   auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
-  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
 
   EXPECT_EQ(paint_value->NumberOfGeneratorsForTesting(), 0u);
   paint_value->GetImage(*target, GetDocument(), style, target_size);
@@ -238,7 +238,7 @@
   SetBodyInnerHTML(R"HTML(<div id="target"></div>)HTML");
 
   auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
-  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
 
   EXPECT_EQ(paint_value->NumberOfGeneratorsForTesting(), 0u);
   // There is no generator, so returning a nullptr.
@@ -249,7 +249,7 @@
   SetBodyInnerHTML(R"HTML(<div id="target"></div>)HTML");
 
   auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
-  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
 
   EXPECT_EQ(paint_value->NumberOfGeneratorsForTesting(), 0u);
   // There is no generator, so returning a nullptr.
@@ -278,7 +278,7 @@
   const ComputedStyle& style = *target->Style();
 
   auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
-  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
 
   ON_CALL(*mock_generator, IsImageGeneratorReady()).WillByDefault(Return(true));
   // This PW can be composited, so we should only fall back to main once, in
@@ -319,7 +319,7 @@
   ASSERT_NE(style.InsideLink(), EInsideLink::kNotInsideLink);
 
   auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("linkpainter");
-  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
   EXPECT_FALSE(paint_value->GetImage(*target, GetDocument(), style,
                                      FloatSize(100, 100)));
 }
@@ -347,14 +347,14 @@
   ASSERT_NE(style.InsideLink(), EInsideLink::kNotInsideLink);
 
   auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("linkpainter");
-  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
   EXPECT_FALSE(paint_value->GetImage(*target, GetDocument(), style,
                                      FloatSize(100, 100)));
 }
 
 TEST_P(CSSPaintValueTest, BuildInputArgumentValuesNotCrash) {
   auto* ident = MakeGarbageCollected<CSSCustomIdentValue>("testpainter");
-  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident);
+  CSSPaintValue* paint_value = MakeGarbageCollected<CSSPaintValue>(ident, true);
 
   ASSERT_EQ(paint_value->GetParsedInputArgumentsForTesting(), nullptr);
   Vector<std::unique_ptr<CrossThreadStyleValue>> cross_thread_input_arguments;
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_fuzzer.cc b/third_party/blink/renderer/core/display_lock/display_lock_fuzzer.cc
index 03ab9065..fdbbddb 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_fuzzer.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_fuzzer.cc
@@ -9,23 +9,14 @@
 #include "third_party/blink/public/web/web_view.h"
 #include "third_party/blink/public/web/web_widget.h"
 
-static content::Env* env;
-
-bool Initialize() {
-  blink::WebRuntimeFeatures::EnableDisplayLocking(true);
-  env = new content::Env();
-  return true;
-}
-
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  static bool initialized = Initialize();
-  // Suppress unused variable warning.
-  (void)initialized;
-
   // Only handle reasonable size inputs.
   if (size < 1 || size > 10000)
     return 0;
 
+  blink::WebRuntimeFeatures::EnableDisplayLocking(true);
+  static base::NoDestructor<content::Env> env;
+
   std::string data_as_string(reinterpret_cast<const char*>(data), size);
   int num_rafs = std::hash<std::string>()(data_as_string) % 10;
   env->adapter->LoadHTML(data_as_string, "");
diff --git a/third_party/blink/renderer/core/fileapi/public_url_manager.cc b/third_party/blink/renderer/core/fileapi/public_url_manager.cc
index 1165b72..cb0b18a 100644
--- a/third_party/blink/renderer/core/fileapi/public_url_manager.cc
+++ b/third_party/blink/renderer/core/fileapi/public_url_manager.cc
@@ -33,77 +33,20 @@
 #include "third_party/blink/renderer/core/fileapi/url_registry.h"
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
 #include "third_party/blink/renderer/platform/blob/blob_url.h"
+#include "third_party/blink/renderer/platform/blob/blob_url_null_origin_map.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
-#include "third_party/blink/renderer/platform/weborigin/url_security_origin_map.h"
-#include "third_party/blink/renderer/platform/wtf/hash_map.h"
-#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
-#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
 namespace {
 
-// When a blob URL is created in an opaque origin or something whose
-// SecurityOrigin::SerializesAsNull() returns true, the origin is serialized
-// into the URL as "null". Since that makes it impossible to parse the origin
-// back out and compare it against a context's origin (to check if a context is
-// allowed to dereference the URL) we store a map of blob URL to SecurityOrigin
-// instance for blob URLs with such the origins.
-
-class BlobURLNullOriginMap final : public URLSecurityOriginMap {
- public:
-  BlobURLNullOriginMap();
-
-  // If the given blob URL has a "null" origin, returns SecurityOrigin that
-  // represents the "null" origin. Otherwise, returns nullptr.
-  SecurityOrigin* GetOrigin(const KURL& blob_url) override;
-};
-
-typedef HashMap<String, scoped_refptr<SecurityOrigin>> BlobURLOriginMap;
-static ThreadSpecific<BlobURLOriginMap>& OriginMap() {
-  // We want to create the BlobURLNullOriginMap exactly once because it is
-  // shared by all the threads.
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(BlobURLNullOriginMap, cache, ());
-  // BlobURLNullOriginMap's constructor does the interesting work.
-  (void)cache;
-
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<BlobURLOriginMap>, map, ());
-  return map;
-}
-
-static void SaveToOriginMap(SecurityOrigin* origin, const KURL& blob_url) {
-  DCHECK(blob_url.ProtocolIs("blob"));
-  DCHECK(!blob_url.HasFragmentIdentifier());
-
-  // If the blob URL contains "null" origin, as in the context with opaque
-  // security origin or file URL, save the mapping between url and origin so
-  // that the origin can be retrieved when doing security origin check.
-  //
-  // See the definition of the origin of a Blob URL in the File API spec.
-  if (origin && origin->SerializesAsNull()) {
-    DCHECK_EQ(BlobURL::GetOrigin(blob_url), "null");
-    OriginMap()->insert(blob_url.GetString(), origin);
-  }
-}
-
-static void RemoveFromOriginMap(const KURL& blob_url) {
+static void RemoveFromNullOriginMapIfNecessary(const KURL& blob_url) {
   DCHECK(blob_url.ProtocolIs("blob"));
   if (BlobURL::GetOrigin(blob_url) == "null")
-    OriginMap()->erase(blob_url.GetString());
-}
-
-BlobURLNullOriginMap::BlobURLNullOriginMap() {
-  SecurityOrigin::SetBlobURLNullOriginMap(this);
-}
-
-SecurityOrigin* BlobURLNullOriginMap::GetOrigin(const KURL& blob_url) {
-  DCHECK(blob_url.ProtocolIs("blob"));
-  KURL blob_url_without_fragment = blob_url;
-  blob_url_without_fragment.RemoveFragmentIdentifier();
-  return OriginMap()->at(blob_url_without_fragment.GetString());
+    BlobURLNullOriginMap::GetInstance()->Remove(blob_url);
 }
 
 }  // namespace
@@ -138,7 +81,9 @@
     registry->RegisterURL(origin, url, registrable);
     url_to_registry_.insert(url_string, registry);
   }
-  SaveToOriginMap(origin, url);
+
+  if (origin->SerializesAsNull())
+    BlobURLNullOriginMap::GetInstance()->Add(url, origin);
 
   return url_string;
 }
@@ -162,7 +107,7 @@
   url_store_->Revoke(url);
   mojo_urls_.erase(url.GetString());
 
-  RemoveFromOriginMap(url);
+  RemoveFromNullOriginMapIfNecessary(url);
   auto it = url_to_registry_.find(url.GetString());
   if (it == url_to_registry_.end())
     return;
@@ -208,10 +153,10 @@
   is_stopped_ = true;
   for (auto& url_registry : url_to_registry_) {
     url_registry.value->UnregisterURL(KURL(url_registry.key));
-    RemoveFromOriginMap(KURL(url_registry.key));
+    RemoveFromNullOriginMapIfNecessary(KURL(url_registry.key));
   }
   for (const auto& url : mojo_urls_)
-    RemoveFromOriginMap(KURL(url));
+    RemoveFromNullOriginMapIfNecessary(KURL(url));
 
   url_to_registry_.clear();
   mojo_urls_.clear();
diff --git a/third_party/blink/renderer/core/html/html_link_element.cc b/third_party/blink/renderer/core/html/html_link_element.cc
index 009606e..59c6354 100644
--- a/third_party/blink/renderer/core/html/html_link_element.cc
+++ b/third_party/blink/renderer/core/html/html_link_element.cc
@@ -177,22 +177,10 @@
 
   if (!link_) {
     if (rel_attribute_.IsImport()) {
-      // Only create an import link when HTML imports are enabled. Either:
-      // 1) For chrome internal resources only, if HTMLImportsOnlyChromeEnabled.
-      // 2) The WebComponentsV0 origin trial is enabled.
-      bool imports_enabled =
-          RuntimeEnabledFeatures::HTMLImportsOnlyChromeEnabled() &&
-          (Href().Protocol() == "chrome" ||
-           Href().Protocol() == "chrome-extension");
-      if (!imports_enabled) {
-        imports_enabled =
-            RuntimeEnabledFeatures::HTMLImportsEnabled(&GetDocument());
-      }
-      if (imports_enabled) {
-        link_ = MakeGarbageCollected<LinkImport>(this);
-      } else {
+      // Only create an import link when HTML imports are enabled.
+      if (!RuntimeEnabledFeatures::HTMLImportsEnabled(&GetDocument()))
         return nullptr;
-      }
+      link_ = MakeGarbageCollected<LinkImport>(this);
     } else if (rel_attribute_.IsManifest()) {
       link_ = MakeGarbageCollected<LinkManifest>(this);
     } else {
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index 7afa284a..d574b65 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -62,16 +62,6 @@
                               WebGestureEvent::InertialPhaseState::kMomentum,
       .event_time = event.TimeStamp()};
 }
-
-ScrollableArea* ScrollableAreaForSnapping(LayoutBox* layout_box) {
-  if (!layout_box)
-    return nullptr;
-
-  return layout_box->IsLayoutView()
-             ? layout_box->GetFrameView()->GetScrollableArea()
-             : layout_box->GetScrollableArea();
-}
-
 }  // namespace
 
 ScrollManager::ScrollManager(LocalFrame& frame) : frame_(frame) {
@@ -284,27 +274,9 @@
         ToPhysicalDirection(direction, box->IsHorizontalWritingMode(),
                             box->Style()->IsFlippedBlocksWritingMode());
 
-    ScrollableArea* scrollable_area = nullptr;
-
-    // The global root scroller must be scrolled by the RootFrameViewport.
-    if (box->IsGlobalRootScroller()) {
-      LocalFrame& main_frame = frame_->LocalFrameRoot();
-      // The local root must be the main frame if we have the global root
-      // scroller since it can't yet be set on OOPIFs.
-      DCHECK(main_frame.IsMainFrame());
-
-      scrollable_area = main_frame.View()->GetScrollableArea();
-    } else {
-      scrollable_area = box->GetScrollableArea();
-    }
-
+    ScrollableArea* scrollable_area = ScrollableArea::GetForScrolling(box);
     DCHECK(scrollable_area);
 
-    // TODO(crbug.com/1015451): Remove ScrollableAreaForSnapping and use the
-    // RootFrameViewport if the scroller is a global root scroller.
-    ScrollableArea* scrollable_area_for_snapping =
-        ScrollableAreaForSnapping(box);
-
     ScrollOffset delta = ToScrollDelta(physical_direction, 1);
     delta.Scale(scrollable_area->ScrollStep(granularity, kHorizontalScrollbar),
                 scrollable_area->ScrollStep(granularity, kVerticalScrollbar));
@@ -314,12 +286,12 @@
     // scroll with intended end position only.
     switch (granularity) {
       case ScrollGranularity::kScrollByLine: {
-        if (scrollable_area_for_snapping->SnapForDirection(delta))
+        if (scrollable_area->SnapForDirection(delta))
           return true;
         break;
       }
       case ScrollGranularity::kScrollByPage: {
-        if (scrollable_area_for_snapping->SnapForEndAndDirection(delta))
+        if (scrollable_area->SnapForEndAndDirection(delta))
           return true;
         break;
       }
@@ -329,8 +301,8 @@
                           physical_direction == kScrollRight;
         bool scrolled_y = physical_direction == kScrollUp ||
                           physical_direction == kScrollDown;
-        if (scrollable_area_for_snapping->SnapForEndPosition(
-                end_position, scrolled_x, scrolled_y))
+        if (scrollable_area->SnapForEndPosition(end_position, scrolled_x,
+                                                scrolled_y))
           return true;
         break;
       }
@@ -767,9 +739,9 @@
   if (!previous_gesture_scrolled_node_ ||
       (!did_scroll_x_for_scroll_gesture_ && !did_scroll_y_for_scroll_gesture_))
     return false;
-  LayoutBox* layout_box = LayoutBoxForSnapping();
-  ScrollableArea* scrollable_area = ScrollableAreaForSnapping(layout_box);
-  if (!layout_box || !scrollable_area)
+  ScrollableArea* scrollable_area =
+      ScrollableArea::GetForScrolling(LayoutBoxForSnapping());
+  if (!scrollable_area)
     return false;
 
   bool is_mouse_wheel =
@@ -806,9 +778,9 @@
     const gfx::Vector2dF& natural_displacement,
     gfx::Vector2dF* out_initial_position,
     gfx::Vector2dF* out_target_position) const {
-  LayoutBox* layout_box = LayoutBoxForSnapping();
-  ScrollableArea* scrollable_area = ScrollableAreaForSnapping(layout_box);
-  if (!layout_box || !scrollable_area)
+  ScrollableArea* scrollable_area =
+      ScrollableArea::GetForScrolling(LayoutBoxForSnapping());
+  if (!scrollable_area)
     return false;
 
   FloatPoint current_position = scrollable_area->ScrollPosition();
@@ -828,7 +800,7 @@
 gfx::Vector2dF ScrollManager::ScrollByForSnapFling(
     const gfx::Vector2dF& delta) {
   ScrollableArea* scrollable_area =
-      ScrollableAreaForSnapping(LayoutBoxForSnapping());
+      ScrollableArea::GetForScrolling(LayoutBoxForSnapping());
   if (!scrollable_area)
     return gfx::Vector2dF();
 
diff --git a/third_party/blink/renderer/core/inspector/dev_tools_host.cc b/third_party/blink/renderer/core/inspector/dev_tools_host.cc
index 082fe36..0818c26 100644
--- a/third_party/blink/renderer/core/inspector/dev_tools_host.cc
+++ b/third_party/blink/renderer/core/inspector/dev_tools_host.cc
@@ -36,7 +36,6 @@
 #include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
-#include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -126,8 +125,6 @@
   if (!script_state)
     return;
   ScriptState::Scope scope(script_state);
-  std::unique_ptr<UserGestureIndicator> gesture_indicator =
-      LocalFrame::NotifyUserActivation(frontend_frame_);
   v8::MicrotasksScope microtasks(script_state->GetIsolate(),
                                  v8::MicrotasksScope::kRunMicrotasks);
   ScriptSourceCode source_code(expression, ScriptSourceLocationType::kInternal,
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 667f66c..20c1fb9 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3247,7 +3247,9 @@
   // their intrinsic widths.
   // FIXME: Think about writing-mode here.
   // https://bugs.webkit.org/show_bug.cgi?id=46473
-  if (Parent()->StyleRef().IsDeprecatedWebkitBox() &&
+  if ((Parent()->IsDeprecatedFlexibleBox() ||
+       (Parent()->StyleRef().IsDeprecatedWebkitBox() &&
+        Parent()->IsFlexibleBox())) &&
       (Parent()->StyleRef().BoxOrient() == EBoxOrient::kHorizontal ||
        Parent()->StyleRef().BoxAlign() != EBoxAlignment::kStretch))
     return true;
diff --git a/third_party/blink/renderer/core/layout/layout_theme_default.cc b/third_party/blink/renderer/core/layout/layout_theme_default.cc
index 8f227a5..8c38b30 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_default.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_default.cc
@@ -35,7 +35,6 @@
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/data_resource_helper.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
-#include "third_party/blink/renderer/platform/web_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -46,10 +45,6 @@
 static const float kMinCancelButtonSize = 5;
 static const float kMaxCancelButtonSize = 21;
 
-static bool UseMockTheme() {
-  return WebTestSupport::IsMockThemeEnabledForTest();
-}
-
 LayoutThemeDefault::LayoutThemeDefault()
     : LayoutTheme(),
       caret_blink_interval_(LayoutTheme::CaretBlinkInterval()),
@@ -58,13 +53,6 @@
 LayoutThemeDefault::~LayoutThemeDefault() = default;
 
 bool LayoutThemeDefault::ThemeDrawsFocusRing(const ComputedStyle& style) const {
-  if (UseMockTheme()) {
-    // Don't use focus rings for buttons when mocking controls.
-    return style.EffectiveAppearance() == kButtonPart ||
-           style.EffectiveAppearance() == kPushButtonPart ||
-           style.EffectiveAppearance() == kSquareButtonPart;
-  }
-
   // This causes Blink to draw the focus rings for us.
   return false;
 }
@@ -77,12 +65,6 @@
   constexpr Color kDefaultMenuColorDark(0xff404040);
 
   if (css_value_id == CSSValueID::kButtonface) {
-    if (UseMockTheme()) {
-      if (color_scheme == WebColorScheme::kLight)
-        return Color(0xc0, 0xc0, 0xc0);
-      else
-        return Color(0x80, 0x80, 0x80);
-    }
     switch (color_scheme) {
       case WebColorScheme::kLight:
         return kDefaultButtonGrayColor;
@@ -153,35 +135,25 @@
 
 Color LayoutThemeDefault::PlatformActiveSelectionBackgroundColor(
     WebColorScheme color_scheme) const {
-  if (UseMockTheme())
-    return Color(0x00, 0x00, 0xff);  // Royal blue.
   return active_selection_background_color_;
 }
 
 Color LayoutThemeDefault::PlatformInactiveSelectionBackgroundColor(
     WebColorScheme color_scheme) const {
-  if (UseMockTheme())
-    return Color(0x99, 0x99, 0x99);  // Medium gray.
   return inactive_selection_background_color_;
 }
 
 Color LayoutThemeDefault::PlatformActiveSelectionForegroundColor(
     WebColorScheme color_scheme) const {
-  if (UseMockTheme())
-    return Color(0xff, 0xff, 0xcc);  // Pale yellow.
   return active_selection_foreground_color_;
 }
 
 Color LayoutThemeDefault::PlatformInactiveSelectionForegroundColor(
     WebColorScheme color_scheme) const {
-  if (UseMockTheme())
-    return Color::kWhite;
   return inactive_selection_foreground_color_;
 }
 
 IntSize LayoutThemeDefault::SliderTickSize() const {
-  if (UseMockTheme())
-    return IntSize(1, 3);
   if (RuntimeEnabledFeatures::FormControlsRefreshEnabled())
     return IntSize(1, 4);
   else
@@ -189,8 +161,6 @@
 }
 
 int LayoutThemeDefault::SliderTickOffsetFromTrackCenter() const {
-  if (UseMockTheme())
-    return 11;
   if (RuntimeEnabledFeatures::FormControlsRefreshEnabled())
     return 7;
   else
@@ -204,8 +174,7 @@
   IntSize size = Platform::Current()->ThemeEngine()->GetSize(
       WebThemeEngine::kPartSliderThumb);
 
-  // FIXME: Mock theme doesn't handle zoomed sliders.
-  float zoom_level = UseMockTheme() ? 1 : style.EffectiveZoom();
+  float zoom_level = style.EffectiveZoom();
   if (style.EffectiveAppearance() == kSliderThumbHorizontalPart) {
     style.SetWidth(Length::Fixed(size.Width() * zoom_level));
     style.SetHeight(Length::Fixed(size.Height() * zoom_level));
@@ -268,18 +237,6 @@
   return true;
 }
 
-bool LayoutThemeDefault::ShouldUseFallbackTheme(
-    const ComputedStyle& style) const {
-  if (UseMockTheme()) {
-    // The mock theme can't handle zoomed controls, so we fall back to the
-    // "fallback" theme.
-    ControlPart part = style.EffectiveAppearance();
-    if (part == kCheckboxPart || part == kRadioPart)
-      return style.EffectiveZoom() != 1;
-  }
-  return LayoutTheme::ShouldUseFallbackTheme(style);
-}
-
 bool LayoutThemeDefault::SupportsHover(const ComputedStyle& style) const {
   return true;
 }
diff --git a/third_party/blink/renderer/core/layout/layout_theme_default.h b/third_party/blink/renderer/core/layout/layout_theme_default.h
index ff349a83..397383f4 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_default.h
+++ b/third_party/blink/renderer/core/layout/layout_theme_default.h
@@ -137,7 +137,6 @@
  protected:
   LayoutThemeDefault();
   ~LayoutThemeDefault() override;
-  bool ShouldUseFallbackTheme(const ComputedStyle&) const override;
 
   IntRect DeterminateProgressValueRectFor(LayoutProgress*,
                                           const IntRect&) const;
diff --git a/third_party/blink/renderer/core/layout/layout_theme_test.cc b/third_party/blink/renderer/core/layout/layout_theme_test.cc
index ff7f66b..ef113db1 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_test.cc
@@ -20,7 +20,6 @@
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
-#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -29,16 +28,6 @@
  protected:
   LayoutThemeTest() : ScopedCSSColorSchemeForTest(true) {}
   void SetHtmlInnerHTML(const char* html_content);
-
-  void SetUp() override {
-    WebTestSupport::SetMockThemeEnabledForTest(true);
-    PageTestBase::SetUp();
-  }
-
-  void TearDown() override {
-    PageTestBase::TearDown();
-    WebTestSupport::SetMockThemeEnabledForTest(false);
-  }
 };
 
 void LayoutThemeTest::SetHtmlInnerHTML(const char* html_content) {
@@ -138,8 +127,8 @@
             initial_style->VisitedDependentColor(GetCSSPropertyColor()));
 }
 
-// Mock theming is done on LayoutThemeDefault which is not a base class for
-// LayoutThemeMac.
+// The expectations are based on LayoutThemeDefault::SystemColor.
+// LayoutThemeMac doesn't use that code path.
 #if !defined(OS_MACOSX)
 TEST_F(LayoutThemeTest, SystemColorWithColorScheme) {
   SetHtmlInnerHTML(R"HTML(
@@ -157,7 +146,7 @@
 
   const ComputedStyle* style = dark_element->GetComputedStyle();
   EXPECT_EQ(WebColorScheme::kLight, style->UsedColorScheme());
-  EXPECT_EQ(Color(0xc0, 0xc0, 0xc0),
+  EXPECT_EQ(Color(0xdd, 0xdd, 0xdd),
             style->VisitedDependentColor(GetCSSPropertyColor()));
 
   // Change color scheme to dark.
@@ -168,7 +157,7 @@
 
   style = dark_element->GetComputedStyle();
   EXPECT_EQ(WebColorScheme::kDark, style->UsedColorScheme());
-  EXPECT_EQ(Color(0x80, 0x80, 0x80),
+  EXPECT_EQ(Color(0x44, 0x44, 0x44),
             style->VisitedDependentColor(GetCSSPropertyColor()));
 }
 #endif  // !defined(OS_MACOSX)
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
index d19fb55..93e75e9 100644
--- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
@@ -44,12 +44,6 @@
   return box;
 }
 
-static ScrollableArea* ScrollableAreaForSnapping(const LayoutBox& layout_box) {
-  return layout_box.IsLayoutView()
-             ? layout_box.GetFrameView()->GetScrollableArea()
-             : layout_box.GetScrollableArea();
-}
-
 // Snap types are categorized according to the spec
 // https://drafts.csswg.org/css-scroll-snap-1/#snap-axis
 static cc::ScrollSnapType GetPhysicalSnapType(const LayoutBox& snap_container) {
@@ -91,10 +85,12 @@
       snap_container.GetDocument().documentElement())
     return;
 
-  bool is_scroll_container =
-      snap_container.IsLayoutView() || snap_container.HasOverflowClip();
-  if (!is_scroll_container) {
-    DCHECK(!ScrollableAreaForSnapping(snap_container));
+  // Per specification snap positions only affect *scroll containers* [1]. So if
+  // the layout box is not a scroll container we ignore it here even if it has
+  // non-none scroll-snap-type. Note that in blink, existence of scrollable area
+  // directly maps to being a scroll container in the specification. [1]
+  // https://drafts.csswg.org/css-scroll-snap/#overview
+  if (!snap_container.GetScrollableArea()) {
     DCHECK(!snap_containers_.Contains(&snap_container));
     return;
   }
@@ -149,8 +145,8 @@
 }
 
 void SnapCoordinator::UpdateSnapContainerData(LayoutBox& snap_container) {
-
-  ScrollableArea* scrollable_area = ScrollableAreaForSnapping(snap_container);
+  ScrollableArea* scrollable_area =
+      ScrollableArea::GetForScrolling(&snap_container);
   const auto* old_snap_container_data = scrollable_area->GetSnapContainerData();
   auto snap_type = GetPhysicalSnapType(snap_container);
 
@@ -270,7 +266,9 @@
   PhysicalRect area_rect = snap_area.PhysicalBorderBoxRect();
   area_rect = snap_area.LocalToAncestorRect(area_rect, &snap_container,
                                             kTraverseDocumentBoundaries);
-  ScrollableArea* scrollable_area = ScrollableAreaForSnapping(snap_container);
+  ScrollableArea* scrollable_area =
+      ScrollableArea::GetForScrolling(&snap_container);
+
   if (scrollable_area) {
     if (snap_container.IsLayoutView()) {
       area_rect = snap_container.GetFrameView()->FrameToDocument(area_rect);
@@ -313,10 +311,12 @@
 }
 
 void SnapCoordinator::ShowSnapDataFor(const LayoutBox* snap_container) {
+  if (!snap_container)
+    return;
+  ScrollableArea* scrollable_area =
+      ScrollableArea::GetForScrolling(snap_container);
   const auto* optional_data =
-      ScrollableAreaForSnapping(*snap_container)
-          ? ScrollableAreaForSnapping(*snap_container)->GetSnapContainerData()
-          : nullptr;
+      scrollable_area ? scrollable_area->GetSnapContainerData() : nullptr;
   if (optional_data)
     LOG(INFO) << *optional_data;
 }
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h
index 48d7d6f3..402ceaa5 100644
--- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h
+++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h
@@ -53,6 +53,7 @@
   // Called by LocalFrameView::PerformPostLayoutTasks(), so that the snap data
   // are updated whenever a layout happens.
   void UpdateAllSnapContainerData();
+  void UpdateSnapContainerData(LayoutBox&);
 
 #ifndef NDEBUG
   void ShowSnapAreaMap();
@@ -63,8 +64,6 @@
  private:
   friend class SnapCoordinatorTest;
 
-  void UpdateSnapContainerData(LayoutBox&);
-
   HashSet<LayoutBox*> snap_containers_;
   DISALLOW_COPY_AND_ASSIGN(SnapCoordinator);
 };
diff --git a/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc b/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc
index ecf4b4e9..661c7c3 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_clipper_test.cc
@@ -11,7 +11,6 @@
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
-#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -19,16 +18,6 @@
  public:
   PaintLayerClipperTest()
       : RenderingTest(MakeGarbageCollected<EmptyLocalFrameClient>()) {}
-
-  void SetUp() override {
-    WebTestSupport::SetMockThemeEnabledForTest(true);
-    RenderingTest::SetUp();
-  }
-
-  void TearDown() override {
-    RenderingTest::TearDown();
-    WebTestSupport::SetMockThemeEnabledForTest(false);
-  }
 };
 
 TEST_F(PaintLayerClipperTest, ParentBackgroundClipRectSubpixelAccumulation) {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index d168706..8bcc55a 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -1072,6 +1072,12 @@
     SetHasHorizontalScrollbar(needs_horizontal_scrollbar);
     SetHasVerticalScrollbar(needs_vertical_scrollbar);
   }
+
+  // Recalculate the snap container data since the scrolling behaviour for this
+  // layout box changed (i.e. it either became the layout viewport or it
+  // is no longer the layout viewport).
+  GetLayoutBox()->GetDocument().GetSnapCoordinator().UpdateSnapContainerData(
+      *GetLayoutBox());
 }
 
 bool PaintLayerScrollableArea::ShouldPerformScrollAnchoring() const {
diff --git a/third_party/blink/renderer/core/paint/theme_painter_default.cc b/third_party/blink/renderer/core/paint/theme_painter_default.cc
index 5d32376..47f76ec 100644
--- a/third_party/blink/renderer/core/paint/theme_painter_default.cc
+++ b/third_party/blink/renderer/core/paint/theme_painter_default.cc
@@ -38,7 +38,6 @@
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
-#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
@@ -46,19 +45,11 @@
 
 const unsigned kDefaultButtonBackgroundColor = 0xffdddddd;
 
-bool UseMockTheme() {
-  return WebTestSupport::IsMockThemeEnabledForTest();
-}
-
 WebThemeEngine::State GetWebThemeState(const Node* node) {
   if (!LayoutTheme::IsEnabled(node))
     return WebThemeEngine::kStateDisabled;
-  if (UseMockTheme() && LayoutTheme::IsReadOnlyControl(node))
-    return WebThemeEngine::kStateReadonly;
   if (LayoutTheme::IsPressed(node))
     return WebThemeEngine::kStatePressed;
-  if (UseMockTheme() && LayoutTheme::IsFocused(node))
-    return WebThemeEngine::kStateFocused;
   if (LayoutTheme::IsHovered(node))
     return WebThemeEngine::kStateHover;
 
@@ -199,8 +190,7 @@
   cc::PaintCanvas* canvas = paint_info.context.Canvas();
   extra_params.button = WebThemeEngine::ButtonExtraParams();
   extra_params.button.has_border = true;
-  extra_params.button.background_color =
-      UseMockTheme() ? 0xffc0c0c0 : kDefaultButtonBackgroundColor;
+  extra_params.button.background_color = kDefaultButtonBackgroundColor;
   if (style.HasBackground()) {
     extra_params.button.background_color =
         style.VisitedDependentColor(GetCSSPropertyBackgroundColor()).Rgb();
@@ -314,36 +304,17 @@
   float arrow_box_width =
       theme_.ClampedMenuListArrowPaddingSize(document.GetFrame(), style);
   float arrow_scale_factor = arrow_box_width / theme_.MenuListArrowWidthInDIP();
-  if (UseMockTheme()) {
-    // The size and position of the drop-down button is different between
-    // the mock theme and the regular aura theme.
-
-    // Padding inside the arrowBox.
-    float extra_padding = 2 * arrow_scale_factor;
-    float arrow_size =
-        std::min(arrow_box_width,
-                 static_cast<float>(rect.Height() - style.BorderTopWidth() -
-                                    style.BorderBottomWidth())) -
-        2 * extra_padding;
-    // |arrowX| is the middle position for mock theme engine.
-    extra_params.menu_list.arrow_x =
-        (style.Direction() == TextDirection::kRtl)
-            ? rect.X() + extra_padding + (arrow_size / 2)
-            : right - (arrow_size / 2) - extra_padding;
-    extra_params.menu_list.arrow_size = arrow_size;
-  } else {
-    // TODO(tkent): This should be 7.0 to match scroll bar buttons.
-    float arrow_size =
-        (RuntimeEnabledFeatures::FormControlsRefreshEnabled() ? 8.0 : 6.0) *
-        arrow_scale_factor;
-    // Put the arrow at the center of paddingForArrow area.
-    // |arrowX| is the left position for Aura theme engine.
-    extra_params.menu_list.arrow_x =
-        (style.Direction() == TextDirection::kRtl)
-            ? left + (arrow_box_width - arrow_size) / 2
-            : right - (arrow_box_width + arrow_size) / 2;
-    extra_params.menu_list.arrow_size = arrow_size;
-  }
+  // TODO(tkent): This should be 7.0 to match scroll bar buttons.
+  float arrow_size =
+      (RuntimeEnabledFeatures::FormControlsRefreshEnabled() ? 8.0 : 6.0) *
+      arrow_scale_factor;
+  // Put the arrow at the center of paddingForArrow area.
+  // |arrowX| is the left position for Aura theme engine.
+  extra_params.menu_list.arrow_x =
+      (style.Direction() == TextDirection::kRtl)
+          ? left + (arrow_box_width - arrow_size) / 2
+          : right - (arrow_box_width + arrow_size) / 2;
+  extra_params.menu_list.arrow_size = arrow_size;
   extra_params.menu_list.arrow_color =
       style.VisitedDependentColor(GetCSSPropertyColor()).Rgb();
 }
@@ -359,8 +330,7 @@
 
   PaintSliderTicks(o, i, rect);
 
-  // FIXME: Mock theme doesn't handle zoomed sliders.
-  float zoom_level = UseMockTheme() ? 1 : o.StyleRef().EffectiveZoom();
+  float zoom_level = o.StyleRef().EffectiveZoom();
   GraphicsContextStateSaver state_saver(i.context, false);
   IntRect unzoomed_rect = rect;
   if (zoom_level != 1) {
@@ -405,8 +375,7 @@
       style.EffectiveAppearance() == kSliderThumbVerticalPart;
   extra_params.slider.in_drag = LayoutTheme::IsPressed(node);
 
-  // FIXME: Mock theme doesn't handle zoomed sliders.
-  float zoom_level = UseMockTheme() ? 1 : style.EffectiveZoom();
+  float zoom_level = style.EffectiveZoom();
   GraphicsContextStateSaver state_saver(paint_info.context, false);
   IntRect unzoomed_rect = rect;
   if (zoom_level != 1) {
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index 434fb27b..8dc487a1 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -43,6 +43,7 @@
 #include "third_party/blink/renderer/core/layout/layout_shift_tracker.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
 #include "third_party/blink/renderer/core/scroll/programmatic_scroll_animator.h"
 #include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
@@ -868,6 +869,7 @@
     bool scrolled_x,
     bool scrolled_y,
     base::ScopedClosureRunner on_finish) {
+  DCHECK(IsRootFrameViewport() || !GetLayoutBox()->IsGlobalRootScroller());
   FloatPoint current_position = ScrollPosition();
   return SnapForEndPosition(current_position, scrolled_x, scrolled_y,
                             std::move(on_finish));
@@ -877,6 +879,7 @@
                                         bool scrolled_x,
                                         bool scrolled_y,
                                         base::ScopedClosureRunner on_finish) {
+  DCHECK(IsRootFrameViewport() || !GetLayoutBox()->IsGlobalRootScroller());
   std::unique_ptr<cc::SnapSelectionStrategy> strategy =
       cc::SnapSelectionStrategy::CreateForEndPosition(
           gfx::ScrollOffset(end_position), scrolled_x, scrolled_y);
@@ -885,6 +888,7 @@
 
 bool ScrollableArea::SnapForDirection(const ScrollOffset& delta,
                                       base::ScopedClosureRunner on_finish) {
+  DCHECK(IsRootFrameViewport() || !GetLayoutBox()->IsGlobalRootScroller());
   FloatPoint current_position = ScrollPosition();
   std::unique_ptr<cc::SnapSelectionStrategy> strategy =
       cc::SnapSelectionStrategy::CreateForDirection(
@@ -894,6 +898,7 @@
 }
 
 bool ScrollableArea::SnapForEndAndDirection(const ScrollOffset& delta) {
+  DCHECK(IsRootFrameViewport() || !GetLayoutBox()->IsGlobalRootScroller());
   FloatPoint current_position = ScrollPosition();
   std::unique_ptr<cc::SnapSelectionStrategy> strategy =
       cc::SnapSelectionStrategy::CreateForEndAndDirection(
@@ -907,7 +912,6 @@
   base::Optional<FloatPoint> snap_point = GetSnapPosition(strategy);
   if (!snap_point)
     return false;
-
   CancelScrollAnimation();
   CancelProgrammaticScrollAnimation();
   SetScrollOffset(ScrollPositionToOffset(snap_point.value()),
@@ -935,4 +939,17 @@
       GetCompositorElementId(), gesture_type);
 }
 
+ScrollableArea* ScrollableArea::GetForScrolling(const LayoutBox* layout_box) {
+  if (!layout_box)
+    return nullptr;
+
+  if (!layout_box->IsGlobalRootScroller())
+    return layout_box->GetScrollableArea();
+
+  // The global root scroller should be scrolled by the root frame view's
+  // ScrollableArea.
+  LocalFrame& root_frame = layout_box->GetFrame()->LocalFrameRoot();
+  return root_frame.View()->GetScrollableArea();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h
index 306047e..6ce1b22 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.h
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -462,6 +462,11 @@
                                 ScrollGranularity granularity,
                                 WebInputEvent::Type gesture_type) const;
 
+  // If the layout box is a global root scroller then the root frame view's
+  // ScrollableArea is returned. Otherwise, the layout box's
+  // PaintLayerScrollableArea (which can be null) is returned.
+  static ScrollableArea* GetForScrolling(const LayoutBox* layout_box);
+
  protected:
   // Deduces the ScrollBehavior based on the element style and the parameter set
   // by programmatic scroll into either instant or smooth scroll.
diff --git a/third_party/blink/renderer/core/scroll/scrollbar.cc b/third_party/blink/renderer/core/scroll/scrollbar.cc
index 09ff4fa..ba0fee2 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar.cc
+++ b/third_party/blink/renderer/core/scroll/scrollbar.cc
@@ -512,13 +512,9 @@
     if (is_captured)
       scrollable_area_->MouseReleasedScrollbar();
 
-    ScrollableArea* scrollable_area_for_snapping =
-        scrollable_area_->GetLayoutBox()->IsLayoutView()
-            ? scrollable_area_->GetLayoutBox()
-                  ->GetFrameView()
-                  ->GetScrollableArea()
-            : scrollable_area_.Get();
-    scrollable_area_for_snapping->SnapAfterScrollbarScrolling(orientation_);
+    ScrollableArea* scrollable_area_for_scrolling =
+        ScrollableArea::GetForScrolling(scrollable_area_->GetLayoutBox());
+    scrollable_area_for_scrolling->SnapAfterScrollbarScrolling(orientation_);
 
     ScrollbarPart part = GetTheme().HitTest(
         *this, FlooredIntPoint(mouse_event.PositionInRootFrame()));
diff --git a/third_party/blink/renderer/core/testing/sim/sim_test.cc b/third_party/blink/renderer/core/testing/sim/sim_test.cc
index 45c09a90..0497ae3b 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_test.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_test.cc
@@ -14,20 +14,11 @@
 #include "third_party/blink/renderer/core/loader/document_loader.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
-#include "third_party/blink/renderer/platform/web_test_support.h"
 
 namespace blink {
 
 SimTest::SimTest() {
   Document::SetThreadedParsingEnabledForTesting(false);
-  // Use the mock theme to get more predictable code paths, this also avoids
-  // the OS callbacks in ScrollAnimatorMac which can schedule frames
-  // unpredictably since the OS will randomly call into blink for
-  // updateScrollerStyleForNewRecommendedScrollerStyle which then does
-  // FrameView::scrollbarStyleChanged and will adjust the scrollbar existence
-  // in the middle of a test.
-  WebTestSupport::SetMockThemeEnabledForTest(true);
-  ScrollbarTheme::SetMockScrollbarsEnabled(true);
   // Threaded animations are usually enabled for blink. However these tests use
   // synchronous compositing, which can not run threaded animations.
   bool was_threaded_animation_enabled =
@@ -39,8 +30,6 @@
 
 SimTest::~SimTest() {
   Document::SetThreadedParsingEnabledForTesting(true);
-  WebTestSupport::SetMockThemeEnabledForTest(false);
-  ScrollbarTheme::SetMockScrollbarsEnabled(false);
   content::TestBlinkWebUnitTestSupport::SetThreadedAnimationEnabled(true);
   WebCache::Clear();
 }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
index e3ce253..e1cdb4b 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
@@ -49,7 +49,10 @@
       Supplement<LocalDOMWindow>(*frame->DomWindow()),
       pending_generator_registry_(
           MakeGarbageCollected<PaintWorkletPendingGeneratorRegistry>()),
-      worklet_id_(NextId()) {}
+      worklet_id_(NextId()),
+      is_paint_off_thread_(
+          RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled() &&
+          Thread::CompositorThread()) {}
 
 PaintWorklet::~PaintWorklet() = default;
 
@@ -58,6 +61,10 @@
   pending_generator_registry_->AddPendingGenerator(name, generator);
 }
 
+void PaintWorklet::ResetIsPaintOffThreadForTesting() {
+  is_paint_off_thread_ = RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled();
+}
+
 // We start with a random global scope when a new frame starts. Then within this
 // frame, we switch to the other global scope after certain amount of paint
 // calls (rand(kMaxPaintCountToSwitch)).
@@ -169,10 +176,9 @@
     // regiserered from RegisterCSSPaintDefinition and one extra definition from
     // RegisterMainThreadDocumentPaintDefinition if OffMainThreadCSSPaintEnabled
     // is true.
-    unsigned required_registered_count =
-        RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()
-            ? kNumGlobalScopesPerThread + 1
-            : kNumGlobalScopesPerThread;
+    unsigned required_registered_count = is_paint_off_thread_
+                                             ? kNumGlobalScopesPerThread + 1
+                                             : kNumGlobalScopesPerThread;
     if (existing_document_definition->GetRegisteredDefinitionCount() ==
         required_registered_count)
       pending_generator_registry_->NotifyGeneratorReady(name);
@@ -229,7 +235,7 @@
 bool PaintWorklet::NeedsToCreateGlobalScope() {
   wtf_size_t num_scopes_needed = kNumGlobalScopesPerThread;
   // If we are running off main thread, we will need twice as many global scopes
-  if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled())
+  if (is_paint_off_thread_)
     num_scopes_needed *= 2;
   return GetNumberOfGlobalScopes() < num_scopes_needed;
 }
@@ -241,7 +247,7 @@
   // scopes from the beginning of the vector.  If this code is changed to put
   // the main thread global scopes at the end, then SelectNewGlobalScope must
   // also be changed.
-  if (!RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled() ||
+  if (!is_paint_off_thread_ ||
       GetNumberOfGlobalScopes() < kNumGlobalScopesPerThread) {
     return MakeGarbageCollected<PaintWorkletGlobalScopeProxy>(
         To<Document>(GetExecutionContext())->GetFrame(), ModuleResponsesMap(),
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.h b/third_party/blink/renderer/modules/csspaint/paint_worklet.h
index dd0ef31..3b8b0fa 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.h
@@ -89,6 +89,8 @@
     proxy_client_ = proxy_client;
   }
 
+  void ResetIsPaintOffThreadForTesting();
+
  protected:
   // Since paint worklet has more than one global scope, we MUST override this
   // function and provide our own selection logic.
@@ -140,6 +142,12 @@
   // to ensure that all global scopes get the same proxy client.
   Member<PaintWorkletProxyClient> proxy_client_;
 
+  // When running layout test, paint worklet has to be on the main thread
+  // because "enable-threaded-compositing" is off by default. However, some unit
+  // tests may be testing the functionality of the APIs when the paint worklet
+  // is off the main thread.
+  bool is_paint_off_thread_;
+
   DISALLOW_COPY_AND_ASSIGN(PaintWorklet);
 };
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
index f5b4cfd..ae5877f 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
@@ -26,7 +26,9 @@
 namespace blink {
 class TestPaintWorklet : public PaintWorklet {
  public:
-  explicit TestPaintWorklet(LocalFrame* frame) : PaintWorklet(frame) {}
+  explicit TestPaintWorklet(LocalFrame* frame) : PaintWorklet(frame) {
+    ResetIsPaintOffThreadForTesting();
+  }
 
   void SetPaintsToSwitch(int num) { paints_to_switch_ = num; }
 
@@ -158,8 +160,14 @@
   EXPECT_TRUE(generator);
   EXPECT_EQ(generator->GetRegisteredDefinitionCountForTesting(), 1u);
   DocumentPaintDefinition* definition;
-  EXPECT_FALSE(generator->GetValidDocumentDefinitionForTesting(definition));
-  EXPECT_FALSE(definition);
+  // Please refer to CSSPaintImageGeneratorImpl::GetValidDocumentDefinition for
+  // the logic.
+  if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) {
+    EXPECT_TRUE(generator->GetValidDocumentDefinitionForTesting(definition));
+  } else {
+    EXPECT_FALSE(generator->GetValidDocumentDefinitionForTesting(definition));
+    EXPECT_FALSE(definition);
+  }
 }
 
 // In this test, we set a list of "paints_to_switch" numbers, and in each frame,
@@ -223,6 +231,7 @@
 TEST_P(MainOrOffThreadPaintWorkletTest, ConsistentGlobalScopeOnMainThread) {
   PaintWorklet* paint_worklet_to_test =
       PaintWorklet::From(*GetFrame().GetDocument()->domWindow());
+  paint_worklet_to_test->ResetIsPaintOffThreadForTesting();
 
   MockObserver* observer = MakeGarbageCollected<MockObserver>();
   CSSPaintImageGeneratorImpl* generator_foo =
@@ -294,6 +303,7 @@
 TEST_P(MainOrOffThreadPaintWorkletTest, AllGlobalScopesMustBeCreated) {
   PaintWorklet* paint_worklet_to_test =
       MakeGarbageCollected<PaintWorklet>(&GetFrame());
+  paint_worklet_to_test->ResetIsPaintOffThreadForTesting();
 
   EXPECT_TRUE(paint_worklet_to_test->GetGlobalScopesForTesting().IsEmpty());
 
@@ -321,6 +331,7 @@
   ScopedOffMainThreadCSSPaintForTest off_main_thread_css_paint(true);
   PaintWorklet* paint_worklet_to_test =
       PaintWorklet::From(*GetFrame().GetDocument()->domWindow());
+  paint_worklet_to_test->ResetIsPaintOffThreadForTesting();
 
   MockObserver* observer = MakeGarbageCollected<MockObserver>();
   CSSPaintImageGeneratorImpl* generator_foo =
@@ -490,6 +501,7 @@
   ScopedOffMainThreadCSSPaintForTest off_main_thread_css_paint(true);
   PaintWorklet* paint_worklet_to_test =
       PaintWorklet::From(*GetFrame().GetDocument()->domWindow());
+  paint_worklet_to_test->ResetIsPaintOffThreadForTesting();
 
   MockObserver* observer = MakeGarbageCollected<MockObserver>();
   CSSPaintImageGeneratorImpl* generator =
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 7d40ee97..40ad9a77 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1408,7 +1408,6 @@
     "weborigin/security_policy.cc",
     "weborigin/security_policy.h",
     "weborigin/security_violation_reporting_policy.h",
-    "weborigin/url_security_origin_map.h",
     "webrtc/peer_connection_remote_audio_source.cc",
     "webrtc/peer_connection_remote_audio_source.h",
     "webrtc/track_observer.cc",
diff --git a/third_party/blink/renderer/platform/blob/BUILD.gn b/third_party/blink/renderer/platform/blob/BUILD.gn
index 50a8ac77..635665e 100644
--- a/third_party/blink/renderer/platform/blob/BUILD.gn
+++ b/third_party/blink/renderer/platform/blob/BUILD.gn
@@ -27,6 +27,8 @@
     "blob_data.h",
     "blob_url.cc",
     "blob_url.h",
+    "blob_url_null_origin_map.cc",
+    "blob_url_null_origin_map.h",
     "serialized_blob_mojom_traits.cc",
     "serialized_blob_mojom_traits.h",
   ]
diff --git a/third_party/blink/renderer/platform/blob/blob_url_null_origin_map.cc b/third_party/blink/renderer/platform/blob/blob_url_null_origin_map.cc
new file mode 100644
index 0000000..180dc52
--- /dev/null
+++ b/third_party/blink/renderer/platform/blob/blob_url_null_origin_map.cc
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/blob/blob_url_null_origin_map.h"
+
+#include "third_party/blink/renderer/platform/blob/blob_url.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+// static
+ThreadSpecific<BlobURLNullOriginMap>& BlobURLNullOriginMap::GetInstance() {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<BlobURLNullOriginMap>, map,
+                                  ());
+  return map;
+}
+
+void BlobURLNullOriginMap::Add(const KURL& blob_url, SecurityOrigin* origin) {
+  DCHECK(blob_url.ProtocolIs("blob"));
+  DCHECK_EQ(BlobURL::GetOrigin(blob_url), "null");
+  DCHECK(!blob_url.HasFragmentIdentifier());
+  DCHECK(origin->SerializesAsNull());
+  blob_url_null_origin_map_.insert(blob_url.GetString(), origin);
+}
+
+void BlobURLNullOriginMap::Remove(const KURL& blob_url) {
+  DCHECK(blob_url.ProtocolIs("blob"));
+  DCHECK_EQ(BlobURL::GetOrigin(blob_url), "null");
+  blob_url_null_origin_map_.erase(blob_url.GetString());
+}
+
+SecurityOrigin* BlobURLNullOriginMap::Get(const KURL& blob_url) {
+  DCHECK(blob_url.ProtocolIs("blob"));
+  DCHECK_EQ(BlobURL::GetOrigin(blob_url), "null");
+  KURL blob_url_without_fragment = blob_url;
+  blob_url_without_fragment.RemoveFragmentIdentifier();
+  return blob_url_null_origin_map_.at(blob_url_without_fragment.GetString());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/blob/blob_url_null_origin_map.h b/third_party/blink/renderer/platform/blob/blob_url_null_origin_map.h
new file mode 100644
index 0000000..09c06bd
--- /dev/null
+++ b/third_party/blink/renderer/platform/blob/blob_url_null_origin_map.h
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_URL_NULL_ORIGIN_MAP_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_URL_NULL_ORIGIN_MAP_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+
+namespace blink {
+
+class KURL;
+class SecurityOrigin;
+
+// BlobURLNullOriginMap contains pairs of blob URL and security origin that is
+// serialized into "null". An instance of this class is per-thread, and created
+// when GetInstace() is called for the first time.
+//
+// When a blob URL is created in an opaque origin or something whose
+// SecurityOrigin::SerializesAsNull() returns true, the origin is serialized
+// into the URL as "null". Since that makes it impossible to parse the origin
+// back out and compare it against a context's origin (to check if a context is
+// allowed to dereference the URL), this class stores a map of blob URL to such
+// an origin.
+class PLATFORM_EXPORT BlobURLNullOriginMap {
+ public:
+  // Returns a thread-specific instance. The instance is created when this
+  // function is called for the first time.
+  static ThreadSpecific<BlobURLNullOriginMap>& GetInstance();
+
+  // Adds a pair of |blob_url| and |origin| to the map. |blob_url| and |origin|
+  // must have the same "null" origin.
+  void Add(const KURL& blob_url, SecurityOrigin* origin);
+
+  // Removes a "null" origin keyed with |blob_url| from the map. |blob_url| must
+  // have the "null" origin.
+  void Remove(const KURL& blob_url);
+
+  // Returns a "null" origin keyed with |blob_url| from the map. |blob_url| must
+  // have the "null" origin.
+  SecurityOrigin* Get(const KURL& blob_url);
+
+ private:
+  friend class ThreadSpecific<BlobURLNullOriginMap>;
+
+  HashMap<String, scoped_refptr<SecurityOrigin>> blob_url_null_origin_map_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BLOB_BLOB_URL_NULL_ORIGIN_MAP_H_
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index f0c38155..ecae142 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -647,7 +647,6 @@
 
 void WebRuntimeFeatures::EnableHTMLImports(bool enable) {
   RuntimeEnabledFeatures::SetHTMLImportsEnabled(enable);
-  RuntimeEnabledFeatures::SetHTMLImportsOnlyChromeEnabled(enable);
 }
 
 void WebRuntimeFeatures::EnableSignedExchangePrefetchCacheForNavigations(
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 930944a0..3bdedb1 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -67,6 +67,7 @@
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
@@ -143,16 +144,11 @@
   }
 
   // Cancels incremental marking task in case there is any pending.
-  void Cancel() {
-    if (!pending_task_) {
-      return;
-    }
-    pending_task_->Cancel();
-  }
+  void Cancel() { task_.Cancel(); }
 
  private:
   void Init(BlinkGC::GCReason reason) {
-    DCHECK(!pending_task_);
+    DCHECK(!task_.IsActive());
     reason_ = reason;
     next_incremental_marking_step_duration_ =
         kDefaultIncrementalMarkingStepDuration;
@@ -160,47 +156,13 @@
   }
 
   void ScheduleTask() {
-    if (pending_task_) {
-      return;
-    }
-    auto task = std::make_unique<Task>(this);
-    pending_task_ = task.get();
-    ThreadScheduler::Current()->V8TaskRunner()->PostNonNestableTask(
-        FROM_HERE, WTF::Bind(&Task::Run, std::move(task)));
+    // Reassigning to the task will cancel the currently scheduled one.
+    task_ = PostNonNestableCancellableTask(
+        *ThreadScheduler::Current()->V8TaskRunner(), FROM_HERE,
+        WTF::Bind(&IncrementalMarkingScheduler::Dispatch,
+                  WTF::Unretained(this)));
   }
 
-  // TODO(bikineev): Replace with PostNonNestableCancelableTask when
-  // implemented.
-  class Task {
-   public:
-    explicit Task(IncrementalMarkingScheduler* scheduler)
-        : scheduler_(scheduler) {}
-    ~Task() {
-      if (cancelled_) {
-        return;
-      }
-      Cancel();
-    }
-
-    void Run() {
-      // Bail out if the ThreadState has been cancelled/destroyed.
-      if (cancelled_) {
-        return;
-      }
-      Cancel();
-      scheduler_->Dispatch();
-    }
-
-    void Cancel() {
-      cancelled_ = true;
-      scheduler_->pending_task_ = nullptr;
-    }
-
-   private:
-    IncrementalMarkingScheduler* scheduler_;
-    bool cancelled_ = false;
-  };
-
   void Dispatch() {
     switch (thread_state_->GetGCState()) {
       case ThreadState::kIncrementalGCScheduled:
@@ -241,7 +203,7 @@
       kDefaultIncrementalMarkingStepDuration;
   base::TimeDelta previous_incremental_marking_time_left_ =
       base::TimeDelta::Max();
-  Task* pending_task_ = nullptr;
+  TaskHandle task_;
 };
 
 ThreadState::ThreadState()
diff --git a/third_party/blink/renderer/platform/network/DEPS b/third_party/blink/renderer/platform/network/DEPS
index e678379b..d89e72da 100644
--- a/third_party/blink/renderer/platform/network/DEPS
+++ b/third_party/blink/renderer/platform/network/DEPS
@@ -13,8 +13,6 @@
     "+net/base",
     "+net/http",
     "+net/nqe",
-    # For URLRequestDataJob::BuildResponse().
-    "+net/url_request/url_request_data_job.h",
 
     "+services/network/public/cpp/features.h",
     "+third_party/blink/renderer/platform/blob/blob_data.h",
diff --git a/third_party/blink/renderer/platform/network/network_utils.cc b/third_party/blink/renderer/platform/network/network_utils.cc
index 58197a6..84d1883 100644
--- a/third_party/blink/renderer/platform/network/network_utils.cc
+++ b/third_party/blink/renderer/platform/network/network_utils.cc
@@ -11,7 +11,6 @@
 #include "net/base/url_util.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
-#include "net/url_request/url_request_data_job.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
@@ -75,11 +74,11 @@
   std::string utf8_mime_type;
   std::string utf8_charset;
   std::string data_string;
-  auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(std::string());
+  scoped_refptr<net::HttpResponseHeaders> headers;
 
-  int result = net::URLRequestDataJob::BuildResponse(
-      GURL(url), method.Ascii(), &utf8_mime_type, &utf8_charset, &data_string,
-      headers.get());
+  net::Error result =
+      net::DataURL::BuildResponse(GURL(url), method.Ascii(), &utf8_mime_type,
+                                  &utf8_charset, &data_string, &headers);
   if (result != net::OK)
     return std::make_tuple(result, ResourceResponse(), nullptr);
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 43aa05a..bac8c463 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -757,10 +757,6 @@
       origin_trial_feature_name: "WebComponentsV0",
       status: "stable",
     },
-    // Allow import only chrome internal resources.
-    {
-      name: "HTMLImportsOnlyChrome",
-    },
     {
       name: "IDBObserver",
       status: "experimental",
@@ -1100,6 +1096,7 @@
     },
     {
       name: "OffMainThreadCSSPaint",
+      status: "stable",
     },
     {
       name: "OffscreenCanvasCommit",
diff --git a/third_party/blink/renderer/platform/web_test_support.cc b/third_party/blink/renderer/platform/web_test_support.cc
index 670799d..2f705df 100644
--- a/third_party/blink/renderer/platform/web_test_support.cc
+++ b/third_party/blink/renderer/platform/web_test_support.cc
@@ -53,7 +53,6 @@
 }
 
 static bool g_is_running_web_test = false;
-static bool g_is_mock_theme_enabled = false;
 static bool g_is_font_antialiasing_enabled = false;
 static bool g_is_subpixel_positioning_allowed = true;
 
@@ -65,15 +64,6 @@
   g_is_running_web_test = value;
 }
 
-bool WebTestSupport::IsMockThemeEnabledForTest() {
-  return g_is_mock_theme_enabled;
-}
-
-void WebTestSupport::SetMockThemeEnabledForTest(bool value) {
-  DCHECK(g_is_running_web_test);
-  g_is_mock_theme_enabled = value;
-}
-
 bool WebTestSupport::IsFontAntialiasingEnabledForTest() {
   return g_is_font_antialiasing_enabled;
 }
diff --git a/third_party/blink/renderer/platform/web_test_support.h b/third_party/blink/renderer/platform/web_test_support.h
index afc920e..451ed5c37 100644
--- a/third_party/blink/renderer/platform/web_test_support.h
+++ b/third_party/blink/renderer/platform/web_test_support.h
@@ -42,8 +42,6 @@
  public:
   PLATFORM_EXPORT static bool IsRunningWebTest();
   PLATFORM_EXPORT static void SetIsRunningWebTest(bool);
-  PLATFORM_EXPORT static bool IsMockThemeEnabledForTest();
-  PLATFORM_EXPORT static void SetMockThemeEnabledForTest(bool);
   PLATFORM_EXPORT static bool IsFontAntialiasingEnabledForTest();
   PLATFORM_EXPORT static void SetFontAntialiasingEnabledForTest(bool);
   PLATFORM_EXPORT static bool IsTextSubpixelPositioningAllowedForTest();
diff --git a/third_party/blink/renderer/platform/weborigin/DEPS b/third_party/blink/renderer/platform/weborigin/DEPS
index 14eb4e4..0d5faf4 100644
--- a/third_party/blink/renderer/platform/weborigin/DEPS
+++ b/third_party/blink/renderer/platform/weborigin/DEPS
@@ -12,6 +12,7 @@
     "+services/network/public/cpp/cors/origin_access_entry.h",
     "+services/network/public/cpp/cors/origin_access_list.h",
     "+third_party/blink/renderer/platform/blob/blob_url.h",
+    "+third_party/blink/renderer/platform/blob/blob_url_null_origin_map.h",
     "+third_party/blink/renderer/platform/platform_export.h",
     "+third_party/blink/renderer/platform/runtime_enabled_features.h",
     "+third_party/blink/renderer/platform/testing",
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.cc b/third_party/blink/renderer/platform/weborigin/security_origin.cc
index 922fbde..43cd852 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.cc
@@ -35,13 +35,14 @@
 #include <utility>
 
 #include "net/base/url_util.h"
+#include "third_party/blink/renderer/platform/blob/blob_url.h"
+#include "third_party/blink/renderer/platform/blob/blob_url_null_origin_map.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/weborigin/known_ports.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/origin_access_entry.h"
 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
-#include "third_party/blink/renderer/platform/weborigin/url_security_origin_map.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
@@ -63,15 +64,6 @@
 
 }  // namespace
 
-static URLSecurityOriginMap* g_blob_url_null_origin_map = nullptr;
-
-static SecurityOrigin* GetNullOriginFromBlobURL(const KURL& blob_url) {
-  DCHECK(blob_url.ProtocolIs("blob"));
-  if (g_blob_url_null_origin_map)
-    return g_blob_url_null_origin_map->GetOrigin(blob_url);
-  return nullptr;
-}
-
 bool SecurityOrigin::ShouldUseInnerURL(const KURL& url) {
   // FIXME: Blob URLs don't have inner URLs. Their form is
   // "blob:<inner-origin>/<UUID>", so treating the part after "blob:" as a URL
@@ -94,12 +86,6 @@
   return KURL(url.GetPath());
 }
 
-void SecurityOrigin::SetBlobURLNullOriginMap(
-    URLSecurityOriginMap* blob_url_null_origin_map) {
-  DCHECK(!g_blob_url_null_origin_map);
-  g_blob_url_null_origin_map = blob_url_null_origin_map;
-}
-
 static bool ShouldTreatAsOpaqueOrigin(const KURL& url) {
   if (!url.IsValid())
     return true;
@@ -205,8 +191,9 @@
 scoped_refptr<SecurityOrigin> SecurityOrigin::CreateWithReferenceOrigin(
     const KURL& url,
     const SecurityOrigin* reference_origin) {
-  if (url.ProtocolIs("blob")) {
-    if (scoped_refptr<SecurityOrigin> origin = GetNullOriginFromBlobURL(url))
+  if (url.ProtocolIs("blob") && BlobURL::GetOrigin(url) == "null") {
+    if (scoped_refptr<SecurityOrigin> origin =
+            BlobURLNullOriginMap::GetInstance()->Get(url))
       return origin;
   }
 
@@ -421,8 +408,10 @@
     // with |this|.
     // TODO(nhiroki): Probably we should check the equality by
     // SecurityOrigin::IsSameSchemeHostPort().
-    if (url.ProtocolIs("blob") && GetNullOriginFromBlobURL(url) == this)
+    if (url.ProtocolIs("blob") && BlobURL::GetOrigin(url) == "null" &&
+        BlobURLNullOriginMap::GetInstance()->Get(url) == this) {
       return true;
+    }
     return false;
   }
 
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.h b/third_party/blink/renderer/platform/weborigin/security_origin.h
index 1959535..693023e8 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.h
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.h
@@ -47,7 +47,6 @@
 namespace blink {
 
 class KURL;
-class URLSecurityOriginMap;
 struct SecurityOriginHash;
 
 // An identifier which defines the source of content (e.g. a document) and
@@ -95,10 +94,6 @@
   static scoped_refptr<SecurityOrigin> CreateFromUrlOrigin(const url::Origin&);
   url::Origin ToUrlOrigin() const;
 
-  // Sets the map to look up a SecurityOrigin instance serialized to "null" from
-  // a blob URL.
-  static void SetBlobURLNullOriginMap(URLSecurityOriginMap*);
-
   // Some URL schemes use nested URLs for their security context. For example,
   // filesystem URLs look like the following:
   //
diff --git a/third_party/blink/renderer/platform/weborigin/url_security_origin_map.h b/third_party/blink/renderer/platform/weborigin/url_security_origin_map.h
deleted file mode 100644
index 0cfe303..0000000
--- a/third_party/blink/renderer/platform/weborigin/url_security_origin_map.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-* Copyright (C) 2013 Google Inc. All rights reserved.
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are
-* met:
-*
-*     * Redistributions of source code must retain the above copyright
-* notice, this list of conditions and the following disclaimer.
-*     * Redistributions in binary form must reproduce the above
-* copyright notice, this list of conditions and the following disclaimer
-* in the documentation and/or other materials provided with the
-* distribution.
-*     * Neither the name of Google Inc. nor the names of its
-* contributors may be used to endorse or promote products derived from
-* this software without specific prior written permission.
-*
-* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_URL_SECURITY_ORIGIN_MAP_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_URL_SECURITY_ORIGIN_MAP_H_
-
-#include "base/macros.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-class KURL;
-class SecurityOrigin;
-
-class URLSecurityOriginMap {
-  USING_FAST_MALLOC(URLSecurityOriginMap);
-
- public:
-  URLSecurityOriginMap() = default;
-  virtual ~URLSecurityOriginMap() = default;
-
-  // Returns a SecurityOrigin instance that represents the origin of the given
-  // URL. May return nullptr.
-  virtual SecurityOrigin* GetOrigin(const KURL&) = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(URLSecurityOriginMap);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_URL_SECURITY_ORIGIN_MAP_H_
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index ae4a82f4..ddb99a9 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -390868,7 +390868,7 @@
    "testharness"
   ],
   "css/css-position/position-absolute-replaced-minmax.html": [
-   "00780d650fae639b37f4e82eabcd77794eb1aae2",
+   "5a76277092bf9e84605fbb8e1fd64e17259e3f44",
    "testharness"
   ],
   "css/css-position/position-fixed-at-bottom-right-on-viewport.html": [
@@ -463796,7 +463796,7 @@
    "support"
   ],
   "interfaces/credential-management.idl": [
-   "da1b9dfca6909eab1e3c7fd5e16f1391d69f9dcb",
+   "d097dd084b64dcdd1776a2bb86809e50275d337a",
    "support"
   ],
   "interfaces/csp-embedded-enforcement.idl": [
diff --git a/third_party/blink/web_tests/external/wpt/compat/webkit-box-fieldset-ref.html b/third_party/blink/web_tests/external/wpt/compat/webkit-box-fieldset-ref.html
new file mode 100644
index 0000000..b5d7176
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compat/webkit-box-fieldset-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<p>Fieldset should contain a green div spanning the width.
+<fieldset>
+  <div id="child" style="background:green; height:10px"></div>
+</fieldset>
diff --git a/third_party/blink/web_tests/external/wpt/compat/webkit-box-fieldset.html b/third_party/blink/web_tests/external/wpt/compat/webkit-box-fieldset.html
new file mode 100644
index 0000000..c3a2e64
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compat/webkit-box-fieldset.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel="match" href="webkit-box-fieldset-ref.html">
+<p>Fieldset should contain a green div spanning the width.
+<fieldset style="display: -webkit-box">
+  <div id="child" style="background:green; height:10px"></div>
+</fieldset>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/credential-management.idl b/third_party/blink/web_tests/external/wpt/interfaces/credential-management.idl
index da1b9df..d097dd0 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/credential-management.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/credential-management.idl
@@ -21,9 +21,9 @@
 
 [Exposed=Window, SecureContext]
 interface CredentialsContainer {
-  Promise<Credential?> get(optional CredentialRequestOptions options);
+  Promise<Credential?> get(optional CredentialRequestOptions options = {});
   Promise<Credential> store(Credential credential);
-  Promise<Credential?> create(optional CredentialCreationOptions options);
+  Promise<Credential?> create(optional CredentialCreationOptions options = {});
   Promise<void> preventSilentAccess();
 };
 
@@ -46,11 +46,11 @@
   AbortSignal signal;
 };
 
-[Constructor(HTMLFormElement form),
- Constructor(PasswordCredentialData data),
- Exposed=Window,
+[Exposed=Window,
  SecureContext]
 interface PasswordCredential : Credential {
+  constructor(HTMLFormElement form);
+  constructor(PasswordCredentialData data);
   readonly attribute USVString password;
 };
 PasswordCredential includes CredentialUserData;
@@ -72,10 +72,10 @@
   PasswordCredentialInit password;
 };
 
-[Constructor(FederatedCredentialInit data),
- Exposed=Window,
+[Exposed=Window,
  SecureContext]
 interface FederatedCredential : Credential {
+  constructor(FederatedCredentialInit data);
   readonly attribute USVString provider;
   readonly attribute DOMString? protocol;
 };
diff --git a/third_party/blink/web_tests/fast/media/matchmedium-query-api-expected.txt b/third_party/blink/web_tests/fast/media/matchmedium-query-api-expected.txt
index e090706..94107165 100644
--- a/third_party/blink/web_tests/fast/media/matchmedium-query-api-expected.txt
+++ b/third_party/blink/web_tests/fast/media/matchmedium-query-api-expected.txt
@@ -8,6 +8,6 @@
 "(color" evaluates to true: PASS
 "color" evaluates to false: PASS
 "garbage" evaluates to false: PASS
-"(min-device-width: 100px)" evaluates to true: PASS
+"(min-device-width: 1px)" evaluates to true: PASS
 "(min-device-width: 50000px)" evaluates to false: PASS
 
diff --git a/third_party/blink/web_tests/fast/media/matchmedium-query-api.html b/third_party/blink/web_tests/fast/media/matchmedium-query-api.html
index c91f57e..f27b44b 100644
--- a/third_party/blink/web_tests/fast/media/matchmedium-query-api.html
+++ b/third_party/blink/web_tests/fast/media/matchmedium-query-api.html
@@ -36,7 +36,7 @@
 
     testQuery('garbage', false);
 
-    testQuery('(min-device-width: 100px)', true);
+    testQuery('(min-device-width: 1px)', true);
     testQuery('(min-device-width: 50000px)', false);
   }
 
diff --git a/third_party/blink/web_tests/fast/scroll-snap/root-scroller-snap-behaviour/arrow-key-scroll-snaps-visual-viewport.html b/third_party/blink/web_tests/fast/scroll-snap/root-scroller-snap-behaviour/arrow-key-scroll-snaps-visual-viewport.html
new file mode 100644
index 0000000..ca382a9
--- /dev/null
+++ b/third_party/blink/web_tests/fast/scroll-snap/root-scroller-snap-behaviour/arrow-key-scroll-snaps-visual-viewport.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<style>
+body, html {
+  width: 100%;
+  height: 100%;
+  margin: 0px;
+}
+
+#root-scroller::-webkit-scrollbar {
+  width: 0px;
+  height: 0px;
+}
+
+#root-scroller {
+  width: 100%;
+  height: 100%;
+  overflow: scroll;
+  position: absolute;
+  left: 0;
+  top: 0;
+  background-color: red;
+  scroll-snap-type: y mandatory;
+}
+
+.spacer {
+  width: 100%;
+  height: 100%;
+}
+
+#snap-area {
+  width: 200px;
+  height: 50%;
+  background-color: blue;
+  scroll-snap-align: start;
+}
+</style>
+
+<script src="../../../resources/gesture-util.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<div id="root-scroller">
+  <div class="spacer" style="background-color: PaleGreen"></div>
+  <div class="spacer" style="background-color: PaleGreen"></div>
+  <div id="snap-area"></div>
+</div>
+
+<script>
+if (window.internals) {
+  internals.runtimeFlags.implicitRootScrollerEnabled = true;
+}
+
+const rootscroller = document.getElementById("root-scroller");
+const snaparea = document.getElementById("snap-area");
+
+async function arrowDown() {
+  // Click on the middle of the viewport.
+  const initial_scroll_position = {
+    x: visualViewport.width / 2,
+    y: visualViewport.height / 2
+  }
+  await mouseClickOn(initial_scroll_position.x, initial_scroll_position.y);
+  await new Promise((resolve, reject) => {
+    if (window.eventSender) {
+      eventSender.keyDown("ArrowDown");
+      resolve();
+    }
+    else {
+      reject('This test requires window.eventSender');
+    }
+  });
+}
+
+// Tests that the visual viewport is affected when scrolling the global root
+// scroller with an arrow key. The snap area is located at the bottom of the layout
+// viewport, so the layout viewport cannot align with the snap area.
+// However, when it becomes the global root scroller we should be scrolling the
+// visual viewport too to align with the snap area.
+promise_test(async function () {
+  const scale_factor = 2;
+  internals.setPageScaleFactor(scale_factor);
+  internals.setVisualViewportOffset(0, 0);
+
+  assert_equals(visualViewport.scale, 2);
+  assert_equals(visualViewport.offsetTop, 0);
+
+  await waitForCompositorCommit();
+
+  assert_equals(window.internals.effectiveRootScroller(document),
+    rootscroller,
+    "#root-scroller must be the effective root scroller");
+
+  await arrowDown()
+
+  const max_frames = 500;
+  const max_unchanged_frames = 15;
+  await waitForAnimationEnd(() => {
+    return rootscroller.scrollTop;
+  }, max_frames, max_unchanged_frames);
+
+  // The offset of the visual viewport and the layout viewport combined should
+  // be at the snap point.
+  assert_equals(visualViewport.offsetTop + rootscroller.scrollTop,
+    snaparea.offsetTop, "Visual viewport offset combined with the scroller's\
+    scroll offset should add to the snap area's position.");
+}, "Snapping the root scroller after arrow key scrolling should affect the\
+visual viewport offset.");
+</script>
diff --git a/third_party/blink/web_tests/fast/scroll-snap/root-scroller-snap-behaviour/smooth-scroll-snaps-visual-viewport.html b/third_party/blink/web_tests/fast/scroll-snap/root-scroller-snap-behaviour/smooth-scroll-snaps-visual-viewport.html
new file mode 100644
index 0000000..cd7f87d9
--- /dev/null
+++ b/third_party/blink/web_tests/fast/scroll-snap/root-scroller-snap-behaviour/smooth-scroll-snaps-visual-viewport.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<style>
+body, html {
+  width: 100%;
+  height: 100%;
+  margin: 0px;
+}
+
+#root-scroller::-webkit-scrollbar {
+  width: 0px;
+  height: 0px;
+}
+
+#root-scroller {
+  width: 100%;
+  height: 100%;
+  overflow: scroll;
+  position: absolute;
+  left: 0;
+  top: 0;
+  background-color: red;
+  scroll-snap-type: y mandatory;
+}
+
+.spacer {
+  width: 100%;
+  height: 100%;
+}
+
+#snap-area {
+  width: 200px;
+  height: 50%;
+  background-color: blue;
+  scroll-snap-align: start;
+}
+</style>
+
+<script src="../../../resources/gesture-util.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+
+<div id="root-scroller">
+  <div class="spacer" style="background-color: PaleGreen"></div>
+  <div class="spacer" style="background-color: PaleGreen"></div>
+  <div id="snap-area"></div>
+</div>
+
+<script>
+if (window.internals) {
+  internals.runtimeFlags.implicitRootScrollerEnabled = true;
+}
+
+const rootscroller = document.getElementById("root-scroller");
+const snaparea = document.getElementById("snap-area");
+
+// Tests that the visual viewport is affected when scrolling the global root
+// scroller by gesture. The snap area is located at the bottom of the layout
+// viewport, so the layout viewport cannot align with the snap area.
+// However, when it becomes the global root scroller we should be scrolling the
+// visual viewport too to align with the snap area.
+//
+// TODO: There is no test for programmatic scrolling because the expected
+// behaviour of the visual viewport while scrolling the root scroller is not
+// clear [1]. The current behaviour is that the visual viewport offset does not
+// change, and only the layout viewport scrolls to the target.
+// [1] https://github.com/w3c/csswg-drafts/issues/4393
+promise_test(async function () {
+  const scale_factor = 2;
+  internals.setPageScaleFactor(scale_factor);
+  internals.setVisualViewportOffset(0, 0);
+
+  assert_equals(visualViewport.scale, 2);
+  assert_equals(visualViewport.offsetTop, 0);
+
+  await waitForCompositorCommit();
+
+  assert_equals(window.internals.effectiveRootScroller(document),
+    rootscroller,
+    "#root-scroller must be the effective root scroller");
+
+  // Scroll halfway to the snap area. This will trigger the snapping.
+  const delta = snaparea.offsetTop / 2;
+
+  // Start scroll from the middle of the viewport.
+  const initial_scroll_position = {
+    x: visualViewport.width / 2,
+    y: visualViewport.height / 2
+  }
+  await smoothScroll(
+    delta,
+    initial_scroll_position.x,
+    initial_scroll_position.y,
+    GestureSourceType.TOUCH_INPUT,
+    'down',
+    SPEED_INSTANT);
+
+  const max_frames = 500;
+  const max_unchanged_frames = 15;
+  await waitForAnimationEnd(() => {
+    return rootscroller.scrollTop;
+  }, max_frames, max_unchanged_frames);
+
+  // The offset of the visual viewport and the layout viewport combined should
+  // be at the snap point.
+  assert_equals(visualViewport.offsetTop + rootscroller.scrollTop,
+    snaparea.offsetTop, "Visual viewport offset combined with the scroller's\
+    scroll offset should add to the snap area's position.");
+
+}, "Snapping the root scroller after smooth scrolling should affect the\
+visual viewport offset.");
+</script>
diff --git a/third_party/blink/web_tests/resources/gesture-util.js b/third_party/blink/web_tests/resources/gesture-util.js
index 13a6ed5..3dbed91 100644
--- a/third_party/blink/web_tests/resources/gesture-util.js
+++ b/third_party/blink/web_tests/resources/gesture-util.js
@@ -63,6 +63,8 @@
   });
 }
 
+// TODO: Frames are animated every 1ms for testing. It may be better to have the
+// timeout based on time rather than frame count.
 function waitForAnimationEnd(getValue, max_frame, max_unchanged_frame) {
   const MAX_FRAME = max_frame;
   const MAX_UNCHANGED_FRAME = max_unchanged_frame;
diff --git a/third_party/blink/web_tests/virtual/threaded/http/tests/worklet/dynamic-import-expected.txt b/third_party/blink/web_tests/virtual/threaded/http/tests/worklet/dynamic-import-expected.txt
new file mode 100644
index 0000000..c190611c
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/threaded/http/tests/worklet/dynamic-import-expected.txt
@@ -0,0 +1,9 @@
+CONSOLE MESSAGE: line 9: This test logs a result once from each PaintWorkletGlobalScope
+CONSOLE ERROR: line 3: TypeError: import() is disallowed on WorkletGlobalScope.
+CONSOLE ERROR: line 3: TypeError: import() is disallowed on WorkletGlobalScope.
+CONSOLE ERROR: line 3: TypeError: import() is disallowed on WorkletGlobalScope.
+CONSOLE ERROR: line 3: TypeError: import() is disallowed on WorkletGlobalScope.
+This is a testharness.js-based test.
+PASS Dynamic import() on WorkletGlobalScope should reject the promise.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/threaded/http/tests/worklet/paint-worklet-csp-eval-expected.txt b/third_party/blink/web_tests/virtual/threaded/http/tests/worklet/paint-worklet-csp-eval-expected.txt
new file mode 100644
index 0000000..5dda4f4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/threaded/http/tests/worklet/paint-worklet-csp-eval-expected.txt
@@ -0,0 +1,13 @@
+CONSOLE ERROR: line 1: Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
+
+CONSOLE ERROR: line 1: Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
+
+CONSOLE ERROR: line 1: Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
+
+CONSOLE ERROR: line 1: Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
+
+This is a testharness.js-based test.
+PASS eval() call on the worklet should be blocked because the script-src unsafe-eval directive is not specified.
+PASS eval() call on the worklet should not be blocked because the script-src unsafe-eval directive allows it.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/threaded/http/tests/worklet/webexposed/global-interface-listing-paint-worklet-expected.txt b/third_party/blink/web_tests/virtual/threaded/http/tests/worklet/webexposed/global-interface-listing-paint-worklet-expected.txt
new file mode 100644
index 0000000..ba52f1f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/threaded/http/tests/worklet/webexposed/global-interface-listing-paint-worklet-expected.txt
@@ -0,0 +1,1114 @@
+CONSOLE MESSAGE: line 10: This test logs exposed APIs once from each PaintWorkletGlobalScope
+CONSOLE MESSAGE: line 153: interface ByteLengthQueuingStrategy
+CONSOLE MESSAGE: line 153:     getter highWaterMark
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSImageValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSKeywordValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter value
+CONSOLE MESSAGE: line 153: interface CSSMathInvert : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathMax : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathMin : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathNegate : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathProduct : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathSum : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathValue : CSSNumericValue
+CONSOLE MESSAGE: line 153:     getter operator
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMatrixComponent : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter matrix
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter matrix
+CONSOLE MESSAGE: line 153: interface CSSNumericArray
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSNumericValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     method add
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method div
+CONSOLE MESSAGE: line 153:     method equals
+CONSOLE MESSAGE: line 153:     method max
+CONSOLE MESSAGE: line 153:     method min
+CONSOLE MESSAGE: line 153:     method mul
+CONSOLE MESSAGE: line 153:     method sub
+CONSOLE MESSAGE: line 153:     method to
+CONSOLE MESSAGE: line 153:     method toSum
+CONSOLE MESSAGE: line 153:     method type
+CONSOLE MESSAGE: line 153: interface CSSPerspective : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter length
+CONSOLE MESSAGE: line 153: interface CSSPositionValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153: interface CSSRotate : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter angle
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter angle
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSScale : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSSkew : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ax
+CONSOLE MESSAGE: line 153:     getter ay
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ax
+CONSOLE MESSAGE: line 153:     setter ay
+CONSOLE MESSAGE: line 153: interface CSSSkewX : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ax
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ax
+CONSOLE MESSAGE: line 153: interface CSSSkewY : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ay
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ay
+CONSOLE MESSAGE: line 153: interface CSSStyleValue
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method toString
+CONSOLE MESSAGE: line 153: interface CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter is2D
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method toMatrix
+CONSOLE MESSAGE: line 153:     method toString
+CONSOLE MESSAGE: line 153:     setter is2D
+CONSOLE MESSAGE: line 153: interface CSSTransformValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter is2D
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method toMatrix
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSTranslate : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSUnitValue : CSSNumericValue
+CONSOLE MESSAGE: line 153:     getter unit
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter value
+CONSOLE MESSAGE: line 153: interface CSSUnparsedValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSVariableReferenceValue
+CONSOLE MESSAGE: line 153:     getter fallback
+CONSOLE MESSAGE: line 153:     getter variable
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter variable
+CONSOLE MESSAGE: line 153: interface CountQueuingStrategy
+CONSOLE MESSAGE: line 153:     getter highWaterMark
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface PaintRenderingContext2D
+CONSOLE MESSAGE: line 153:     getter fillStyle
+CONSOLE MESSAGE: line 153:     getter filter
+CONSOLE MESSAGE: line 153:     getter globalAlpha
+CONSOLE MESSAGE: line 153:     getter globalCompositeOperation
+CONSOLE MESSAGE: line 153:     getter imageSmoothingEnabled
+CONSOLE MESSAGE: line 153:     getter imageSmoothingQuality
+CONSOLE MESSAGE: line 153:     getter lineCap
+CONSOLE MESSAGE: line 153:     getter lineDashOffset
+CONSOLE MESSAGE: line 153:     getter lineJoin
+CONSOLE MESSAGE: line 153:     getter lineWidth
+CONSOLE MESSAGE: line 153:     getter miterLimit
+CONSOLE MESSAGE: line 153:     getter shadowBlur
+CONSOLE MESSAGE: line 153:     getter shadowColor
+CONSOLE MESSAGE: line 153:     getter shadowOffsetX
+CONSOLE MESSAGE: line 153:     getter shadowOffsetY
+CONSOLE MESSAGE: line 153:     getter strokeStyle
+CONSOLE MESSAGE: line 153:     method arc
+CONSOLE MESSAGE: line 153:     method arcTo
+CONSOLE MESSAGE: line 153:     method beginPath
+CONSOLE MESSAGE: line 153:     method bezierCurveTo
+CONSOLE MESSAGE: line 153:     method clearRect
+CONSOLE MESSAGE: line 153:     method clip
+CONSOLE MESSAGE: line 153:     method closePath
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method createLinearGradient
+CONSOLE MESSAGE: line 153:     method createPattern
+CONSOLE MESSAGE: line 153:     method createRadialGradient
+CONSOLE MESSAGE: line 153:     method drawImage
+CONSOLE MESSAGE: line 153:     method ellipse
+CONSOLE MESSAGE: line 153:     method fill
+CONSOLE MESSAGE: line 153:     method fillRect
+CONSOLE MESSAGE: line 153:     method getLineDash
+CONSOLE MESSAGE: line 153:     method getTransform
+CONSOLE MESSAGE: line 153:     method isPointInPath
+CONSOLE MESSAGE: line 153:     method isPointInStroke
+CONSOLE MESSAGE: line 153:     method lineTo
+CONSOLE MESSAGE: line 153:     method moveTo
+CONSOLE MESSAGE: line 153:     method quadraticCurveTo
+CONSOLE MESSAGE: line 153:     method rect
+CONSOLE MESSAGE: line 153:     method resetTransform
+CONSOLE MESSAGE: line 153:     method restore
+CONSOLE MESSAGE: line 153:     method rotate
+CONSOLE MESSAGE: line 153:     method save
+CONSOLE MESSAGE: line 153:     method scale
+CONSOLE MESSAGE: line 153:     method setLineDash
+CONSOLE MESSAGE: line 153:     method setTransform
+CONSOLE MESSAGE: line 153:     method stroke
+CONSOLE MESSAGE: line 153:     method strokeRect
+CONSOLE MESSAGE: line 153:     method transform
+CONSOLE MESSAGE: line 153:     method translate
+CONSOLE MESSAGE: line 153:     setter fillStyle
+CONSOLE MESSAGE: line 153:     setter filter
+CONSOLE MESSAGE: line 153:     setter globalAlpha
+CONSOLE MESSAGE: line 153:     setter globalCompositeOperation
+CONSOLE MESSAGE: line 153:     setter imageSmoothingEnabled
+CONSOLE MESSAGE: line 153:     setter imageSmoothingQuality
+CONSOLE MESSAGE: line 153:     setter lineCap
+CONSOLE MESSAGE: line 153:     setter lineDashOffset
+CONSOLE MESSAGE: line 153:     setter lineJoin
+CONSOLE MESSAGE: line 153:     setter lineWidth
+CONSOLE MESSAGE: line 153:     setter miterLimit
+CONSOLE MESSAGE: line 153:     setter shadowBlur
+CONSOLE MESSAGE: line 153:     setter shadowColor
+CONSOLE MESSAGE: line 153:     setter shadowOffsetX
+CONSOLE MESSAGE: line 153:     setter shadowOffsetY
+CONSOLE MESSAGE: line 153:     setter strokeStyle
+CONSOLE MESSAGE: line 153: interface PaintSize
+CONSOLE MESSAGE: line 153:     getter height
+CONSOLE MESSAGE: line 153:     getter width
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface PaintWorkletGlobalScope : WorkletGlobalScope
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface Path2D
+CONSOLE MESSAGE: line 153:     method addPath
+CONSOLE MESSAGE: line 153:     method arc
+CONSOLE MESSAGE: line 153:     method arcTo
+CONSOLE MESSAGE: line 153:     method bezierCurveTo
+CONSOLE MESSAGE: line 153:     method closePath
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method ellipse
+CONSOLE MESSAGE: line 153:     method lineTo
+CONSOLE MESSAGE: line 153:     method moveTo
+CONSOLE MESSAGE: line 153:     method quadraticCurveTo
+CONSOLE MESSAGE: line 153:     method rect
+CONSOLE MESSAGE: line 153: interface ReadableStream
+CONSOLE MESSAGE: line 153:     getter locked
+CONSOLE MESSAGE: line 153:     method cancel
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getReader
+CONSOLE MESSAGE: line 153:     method pipeThrough
+CONSOLE MESSAGE: line 153:     method pipeTo
+CONSOLE MESSAGE: line 153:     method tee
+CONSOLE MESSAGE: line 153: interface ReadableStreamDefaultReader
+CONSOLE MESSAGE: line 153:     getter closed
+CONSOLE MESSAGE: line 153:     method cancel
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method read
+CONSOLE MESSAGE: line 153:     method releaseLock
+CONSOLE MESSAGE: line 153: interface StylePropertyMapReadOnly
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method get
+CONSOLE MESSAGE: line 153:     method getAll
+CONSOLE MESSAGE: line 153:     method has
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface TransformStream
+CONSOLE MESSAGE: line 153:     getter readable
+CONSOLE MESSAGE: line 153:     getter writable
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WorkletGlobalScope
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WritableStream
+CONSOLE MESSAGE: line 153:     getter locked
+CONSOLE MESSAGE: line 153:     method abort
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getWriter
+CONSOLE MESSAGE: line 153: interface WritableStreamDefaultWriter
+CONSOLE MESSAGE: line 153:     getter closed
+CONSOLE MESSAGE: line 153:     getter desiredSize
+CONSOLE MESSAGE: line 153:     getter ready
+CONSOLE MESSAGE: line 153:     method abort
+CONSOLE MESSAGE: line 153:     method close
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method releaseLock
+CONSOLE MESSAGE: line 153:     method write
+CONSOLE MESSAGE: line 153: global object
+CONSOLE MESSAGE: line 153:     attribute console
+CONSOLE MESSAGE: line 153:     attribute globalThis
+CONSOLE MESSAGE: line 153:     getter devicePixelRatio
+CONSOLE MESSAGE: line 153:     method gc
+CONSOLE MESSAGE: line 153:     method registerPaint
+CONSOLE MESSAGE: line 153: interface ByteLengthQueuingStrategy
+CONSOLE MESSAGE: line 153:     getter highWaterMark
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSImageValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSKeywordValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter value
+CONSOLE MESSAGE: line 153: interface CSSMathInvert : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathMax : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathMin : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathNegate : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathProduct : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathSum : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathValue : CSSNumericValue
+CONSOLE MESSAGE: line 153:     getter operator
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMatrixComponent : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter matrix
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter matrix
+CONSOLE MESSAGE: line 153: interface CSSNumericArray
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSNumericValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     method add
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method div
+CONSOLE MESSAGE: line 153:     method equals
+CONSOLE MESSAGE: line 153:     method max
+CONSOLE MESSAGE: line 153:     method min
+CONSOLE MESSAGE: line 153:     method mul
+CONSOLE MESSAGE: line 153:     method sub
+CONSOLE MESSAGE: line 153:     method to
+CONSOLE MESSAGE: line 153:     method toSum
+CONSOLE MESSAGE: line 153:     method type
+CONSOLE MESSAGE: line 153: interface CSSPerspective : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter length
+CONSOLE MESSAGE: line 153: interface CSSPositionValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153: interface CSSRotate : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter angle
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter angle
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSScale : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSSkew : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ax
+CONSOLE MESSAGE: line 153:     getter ay
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ax
+CONSOLE MESSAGE: line 153:     setter ay
+CONSOLE MESSAGE: line 153: interface CSSSkewX : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ax
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ax
+CONSOLE MESSAGE: line 153: interface CSSSkewY : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ay
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ay
+CONSOLE MESSAGE: line 153: interface CSSStyleValue
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method toString
+CONSOLE MESSAGE: line 153: interface CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter is2D
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method toMatrix
+CONSOLE MESSAGE: line 153:     method toString
+CONSOLE MESSAGE: line 153:     setter is2D
+CONSOLE MESSAGE: line 153: interface CSSTransformValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter is2D
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method toMatrix
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSTranslate : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSUnitValue : CSSNumericValue
+CONSOLE MESSAGE: line 153:     getter unit
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter value
+CONSOLE MESSAGE: line 153: interface CSSUnparsedValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSVariableReferenceValue
+CONSOLE MESSAGE: line 153:     getter fallback
+CONSOLE MESSAGE: line 153:     getter variable
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter variable
+CONSOLE MESSAGE: line 153: interface CountQueuingStrategy
+CONSOLE MESSAGE: line 153:     getter highWaterMark
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface PaintRenderingContext2D
+CONSOLE MESSAGE: line 153:     getter fillStyle
+CONSOLE MESSAGE: line 153:     getter filter
+CONSOLE MESSAGE: line 153:     getter globalAlpha
+CONSOLE MESSAGE: line 153:     getter globalCompositeOperation
+CONSOLE MESSAGE: line 153:     getter imageSmoothingEnabled
+CONSOLE MESSAGE: line 153:     getter imageSmoothingQuality
+CONSOLE MESSAGE: line 153:     getter lineCap
+CONSOLE MESSAGE: line 153:     getter lineDashOffset
+CONSOLE MESSAGE: line 153:     getter lineJoin
+CONSOLE MESSAGE: line 153:     getter lineWidth
+CONSOLE MESSAGE: line 153:     getter miterLimit
+CONSOLE MESSAGE: line 153:     getter shadowBlur
+CONSOLE MESSAGE: line 153:     getter shadowColor
+CONSOLE MESSAGE: line 153:     getter shadowOffsetX
+CONSOLE MESSAGE: line 153:     getter shadowOffsetY
+CONSOLE MESSAGE: line 153:     getter strokeStyle
+CONSOLE MESSAGE: line 153:     method arc
+CONSOLE MESSAGE: line 153:     method arcTo
+CONSOLE MESSAGE: line 153:     method beginPath
+CONSOLE MESSAGE: line 153:     method bezierCurveTo
+CONSOLE MESSAGE: line 153:     method clearRect
+CONSOLE MESSAGE: line 153:     method clip
+CONSOLE MESSAGE: line 153:     method closePath
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method createLinearGradient
+CONSOLE MESSAGE: line 153:     method createPattern
+CONSOLE MESSAGE: line 153:     method createRadialGradient
+CONSOLE MESSAGE: line 153:     method drawImage
+CONSOLE MESSAGE: line 153:     method ellipse
+CONSOLE MESSAGE: line 153:     method fill
+CONSOLE MESSAGE: line 153:     method fillRect
+CONSOLE MESSAGE: line 153:     method getLineDash
+CONSOLE MESSAGE: line 153:     method getTransform
+CONSOLE MESSAGE: line 153:     method isPointInPath
+CONSOLE MESSAGE: line 153:     method isPointInStroke
+CONSOLE MESSAGE: line 153:     method lineTo
+CONSOLE MESSAGE: line 153:     method moveTo
+CONSOLE MESSAGE: line 153:     method quadraticCurveTo
+CONSOLE MESSAGE: line 153:     method rect
+CONSOLE MESSAGE: line 153:     method resetTransform
+CONSOLE MESSAGE: line 153:     method restore
+CONSOLE MESSAGE: line 153:     method rotate
+CONSOLE MESSAGE: line 153:     method save
+CONSOLE MESSAGE: line 153:     method scale
+CONSOLE MESSAGE: line 153:     method setLineDash
+CONSOLE MESSAGE: line 153:     method setTransform
+CONSOLE MESSAGE: line 153:     method stroke
+CONSOLE MESSAGE: line 153:     method strokeRect
+CONSOLE MESSAGE: line 153:     method transform
+CONSOLE MESSAGE: line 153:     method translate
+CONSOLE MESSAGE: line 153:     setter fillStyle
+CONSOLE MESSAGE: line 153:     setter filter
+CONSOLE MESSAGE: line 153:     setter globalAlpha
+CONSOLE MESSAGE: line 153:     setter globalCompositeOperation
+CONSOLE MESSAGE: line 153:     setter imageSmoothingEnabled
+CONSOLE MESSAGE: line 153:     setter imageSmoothingQuality
+CONSOLE MESSAGE: line 153:     setter lineCap
+CONSOLE MESSAGE: line 153:     setter lineDashOffset
+CONSOLE MESSAGE: line 153:     setter lineJoin
+CONSOLE MESSAGE: line 153:     setter lineWidth
+CONSOLE MESSAGE: line 153:     setter miterLimit
+CONSOLE MESSAGE: line 153:     setter shadowBlur
+CONSOLE MESSAGE: line 153:     setter shadowColor
+CONSOLE MESSAGE: line 153:     setter shadowOffsetX
+CONSOLE MESSAGE: line 153:     setter shadowOffsetY
+CONSOLE MESSAGE: line 153:     setter strokeStyle
+CONSOLE MESSAGE: line 153: interface PaintSize
+CONSOLE MESSAGE: line 153:     getter height
+CONSOLE MESSAGE: line 153:     getter width
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface PaintWorkletGlobalScope : WorkletGlobalScope
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface Path2D
+CONSOLE MESSAGE: line 153:     method addPath
+CONSOLE MESSAGE: line 153:     method arc
+CONSOLE MESSAGE: line 153:     method arcTo
+CONSOLE MESSAGE: line 153:     method bezierCurveTo
+CONSOLE MESSAGE: line 153:     method closePath
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method ellipse
+CONSOLE MESSAGE: line 153:     method lineTo
+CONSOLE MESSAGE: line 153:     method moveTo
+CONSOLE MESSAGE: line 153:     method quadraticCurveTo
+CONSOLE MESSAGE: line 153:     method rect
+CONSOLE MESSAGE: line 153: interface ReadableStream
+CONSOLE MESSAGE: line 153:     getter locked
+CONSOLE MESSAGE: line 153:     method cancel
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getReader
+CONSOLE MESSAGE: line 153:     method pipeThrough
+CONSOLE MESSAGE: line 153:     method pipeTo
+CONSOLE MESSAGE: line 153:     method tee
+CONSOLE MESSAGE: line 153: interface ReadableStreamDefaultReader
+CONSOLE MESSAGE: line 153:     getter closed
+CONSOLE MESSAGE: line 153:     method cancel
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method read
+CONSOLE MESSAGE: line 153:     method releaseLock
+CONSOLE MESSAGE: line 153: interface StylePropertyMapReadOnly
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method get
+CONSOLE MESSAGE: line 153:     method getAll
+CONSOLE MESSAGE: line 153:     method has
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface TransformStream
+CONSOLE MESSAGE: line 153:     getter readable
+CONSOLE MESSAGE: line 153:     getter writable
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WorkletGlobalScope
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WritableStream
+CONSOLE MESSAGE: line 153:     getter locked
+CONSOLE MESSAGE: line 153:     method abort
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getWriter
+CONSOLE MESSAGE: line 153: interface WritableStreamDefaultWriter
+CONSOLE MESSAGE: line 153:     getter closed
+CONSOLE MESSAGE: line 153:     getter desiredSize
+CONSOLE MESSAGE: line 153:     getter ready
+CONSOLE MESSAGE: line 153:     method abort
+CONSOLE MESSAGE: line 153:     method close
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method releaseLock
+CONSOLE MESSAGE: line 153:     method write
+CONSOLE MESSAGE: line 153: global object
+CONSOLE MESSAGE: line 153:     attribute console
+CONSOLE MESSAGE: line 153:     attribute globalThis
+CONSOLE MESSAGE: line 153:     getter devicePixelRatio
+CONSOLE MESSAGE: line 153:     method gc
+CONSOLE MESSAGE: line 153:     method registerPaint
+CONSOLE MESSAGE: line 153: interface ByteLengthQueuingStrategy
+CONSOLE MESSAGE: line 153:     getter highWaterMark
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSImageValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSKeywordValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter value
+CONSOLE MESSAGE: line 153: interface CSSMathInvert : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathMax : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathMin : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathNegate : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathProduct : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathSum : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathValue : CSSNumericValue
+CONSOLE MESSAGE: line 153:     getter operator
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMatrixComponent : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter matrix
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter matrix
+CONSOLE MESSAGE: line 153: interface CSSNumericArray
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSNumericValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     method add
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method div
+CONSOLE MESSAGE: line 153:     method equals
+CONSOLE MESSAGE: line 153:     method max
+CONSOLE MESSAGE: line 153:     method min
+CONSOLE MESSAGE: line 153:     method mul
+CONSOLE MESSAGE: line 153:     method sub
+CONSOLE MESSAGE: line 153:     method to
+CONSOLE MESSAGE: line 153:     method toSum
+CONSOLE MESSAGE: line 153:     method type
+CONSOLE MESSAGE: line 153: interface CSSPerspective : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter length
+CONSOLE MESSAGE: line 153: interface CSSPositionValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153: interface CSSRotate : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter angle
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter angle
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSScale : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSSkew : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ax
+CONSOLE MESSAGE: line 153:     getter ay
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ax
+CONSOLE MESSAGE: line 153:     setter ay
+CONSOLE MESSAGE: line 153: interface CSSSkewX : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ax
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ax
+CONSOLE MESSAGE: line 153: interface CSSSkewY : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ay
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ay
+CONSOLE MESSAGE: line 153: interface CSSStyleValue
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method toString
+CONSOLE MESSAGE: line 153: interface CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter is2D
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method toMatrix
+CONSOLE MESSAGE: line 153:     method toString
+CONSOLE MESSAGE: line 153:     setter is2D
+CONSOLE MESSAGE: line 153: interface CSSTransformValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter is2D
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method toMatrix
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSTranslate : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSUnitValue : CSSNumericValue
+CONSOLE MESSAGE: line 153:     getter unit
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter value
+CONSOLE MESSAGE: line 153: interface CSSUnparsedValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSVariableReferenceValue
+CONSOLE MESSAGE: line 153:     getter fallback
+CONSOLE MESSAGE: line 153:     getter variable
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter variable
+CONSOLE MESSAGE: line 153: interface CountQueuingStrategy
+CONSOLE MESSAGE: line 153:     getter highWaterMark
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface PaintRenderingContext2D
+CONSOLE MESSAGE: line 153:     getter fillStyle
+CONSOLE MESSAGE: line 153:     getter filter
+CONSOLE MESSAGE: line 153:     getter globalAlpha
+CONSOLE MESSAGE: line 153:     getter globalCompositeOperation
+CONSOLE MESSAGE: line 153:     getter imageSmoothingEnabled
+CONSOLE MESSAGE: line 153:     getter imageSmoothingQuality
+CONSOLE MESSAGE: line 153:     getter lineCap
+CONSOLE MESSAGE: line 153:     getter lineDashOffset
+CONSOLE MESSAGE: line 153:     getter lineJoin
+CONSOLE MESSAGE: line 153:     getter lineWidth
+CONSOLE MESSAGE: line 153:     getter miterLimit
+CONSOLE MESSAGE: line 153:     getter shadowBlur
+CONSOLE MESSAGE: line 153:     getter shadowColor
+CONSOLE MESSAGE: line 153:     getter shadowOffsetX
+CONSOLE MESSAGE: line 153:     getter shadowOffsetY
+CONSOLE MESSAGE: line 153:     getter strokeStyle
+CONSOLE MESSAGE: line 153:     method arc
+CONSOLE MESSAGE: line 153:     method arcTo
+CONSOLE MESSAGE: line 153:     method beginPath
+CONSOLE MESSAGE: line 153:     method bezierCurveTo
+CONSOLE MESSAGE: line 153:     method clearRect
+CONSOLE MESSAGE: line 153:     method clip
+CONSOLE MESSAGE: line 153:     method closePath
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method createLinearGradient
+CONSOLE MESSAGE: line 153:     method createPattern
+CONSOLE MESSAGE: line 153:     method createRadialGradient
+CONSOLE MESSAGE: line 153:     method drawImage
+CONSOLE MESSAGE: line 153:     method ellipse
+CONSOLE MESSAGE: line 153:     method fill
+CONSOLE MESSAGE: line 153:     method fillRect
+CONSOLE MESSAGE: line 153:     method getLineDash
+CONSOLE MESSAGE: line 153:     method getTransform
+CONSOLE MESSAGE: line 153:     method isPointInPath
+CONSOLE MESSAGE: line 153:     method isPointInStroke
+CONSOLE MESSAGE: line 153:     method lineTo
+CONSOLE MESSAGE: line 153:     method moveTo
+CONSOLE MESSAGE: line 153:     method quadraticCurveTo
+CONSOLE MESSAGE: line 153:     method rect
+CONSOLE MESSAGE: line 153:     method resetTransform
+CONSOLE MESSAGE: line 153:     method restore
+CONSOLE MESSAGE: line 153:     method rotate
+CONSOLE MESSAGE: line 153:     method save
+CONSOLE MESSAGE: line 153:     method scale
+CONSOLE MESSAGE: line 153:     method setLineDash
+CONSOLE MESSAGE: line 153:     method setTransform
+CONSOLE MESSAGE: line 153:     method stroke
+CONSOLE MESSAGE: line 153:     method strokeRect
+CONSOLE MESSAGE: line 153:     method transform
+CONSOLE MESSAGE: line 153:     method translate
+CONSOLE MESSAGE: line 153:     setter fillStyle
+CONSOLE MESSAGE: line 153:     setter filter
+CONSOLE MESSAGE: line 153:     setter globalAlpha
+CONSOLE MESSAGE: line 153:     setter globalCompositeOperation
+CONSOLE MESSAGE: line 153:     setter imageSmoothingEnabled
+CONSOLE MESSAGE: line 153:     setter imageSmoothingQuality
+CONSOLE MESSAGE: line 153:     setter lineCap
+CONSOLE MESSAGE: line 153:     setter lineDashOffset
+CONSOLE MESSAGE: line 153:     setter lineJoin
+CONSOLE MESSAGE: line 153:     setter lineWidth
+CONSOLE MESSAGE: line 153:     setter miterLimit
+CONSOLE MESSAGE: line 153:     setter shadowBlur
+CONSOLE MESSAGE: line 153:     setter shadowColor
+CONSOLE MESSAGE: line 153:     setter shadowOffsetX
+CONSOLE MESSAGE: line 153:     setter shadowOffsetY
+CONSOLE MESSAGE: line 153:     setter strokeStyle
+CONSOLE MESSAGE: line 153: interface PaintSize
+CONSOLE MESSAGE: line 153:     getter height
+CONSOLE MESSAGE: line 153:     getter width
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface PaintWorkletGlobalScope : WorkletGlobalScope
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface Path2D
+CONSOLE MESSAGE: line 153:     method addPath
+CONSOLE MESSAGE: line 153:     method arc
+CONSOLE MESSAGE: line 153:     method arcTo
+CONSOLE MESSAGE: line 153:     method bezierCurveTo
+CONSOLE MESSAGE: line 153:     method closePath
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method ellipse
+CONSOLE MESSAGE: line 153:     method lineTo
+CONSOLE MESSAGE: line 153:     method moveTo
+CONSOLE MESSAGE: line 153:     method quadraticCurveTo
+CONSOLE MESSAGE: line 153:     method rect
+CONSOLE MESSAGE: line 153: interface ReadableStream
+CONSOLE MESSAGE: line 153:     getter locked
+CONSOLE MESSAGE: line 153:     method cancel
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getReader
+CONSOLE MESSAGE: line 153:     method pipeThrough
+CONSOLE MESSAGE: line 153:     method pipeTo
+CONSOLE MESSAGE: line 153:     method tee
+CONSOLE MESSAGE: line 153: interface ReadableStreamDefaultReader
+CONSOLE MESSAGE: line 153:     getter closed
+CONSOLE MESSAGE: line 153:     method cancel
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method read
+CONSOLE MESSAGE: line 153:     method releaseLock
+CONSOLE MESSAGE: line 153: interface StylePropertyMapReadOnly
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method get
+CONSOLE MESSAGE: line 153:     method getAll
+CONSOLE MESSAGE: line 153:     method has
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface TransformStream
+CONSOLE MESSAGE: line 153:     getter readable
+CONSOLE MESSAGE: line 153:     getter writable
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WorkletGlobalScope
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WritableStream
+CONSOLE MESSAGE: line 153:     getter locked
+CONSOLE MESSAGE: line 153:     method abort
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getWriter
+CONSOLE MESSAGE: line 153: interface WritableStreamDefaultWriter
+CONSOLE MESSAGE: line 153:     getter closed
+CONSOLE MESSAGE: line 153:     getter desiredSize
+CONSOLE MESSAGE: line 153:     getter ready
+CONSOLE MESSAGE: line 153:     method abort
+CONSOLE MESSAGE: line 153:     method close
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method releaseLock
+CONSOLE MESSAGE: line 153:     method write
+CONSOLE MESSAGE: line 153: global object
+CONSOLE MESSAGE: line 153:     attribute console
+CONSOLE MESSAGE: line 153:     attribute globalThis
+CONSOLE MESSAGE: line 153:     getter devicePixelRatio
+CONSOLE MESSAGE: line 153:     method gc
+CONSOLE MESSAGE: line 153:     method registerPaint
+CONSOLE MESSAGE: line 153: interface ByteLengthQueuingStrategy
+CONSOLE MESSAGE: line 153:     getter highWaterMark
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSImageValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSKeywordValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter value
+CONSOLE MESSAGE: line 153: interface CSSMathInvert : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathMax : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathMin : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathNegate : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathProduct : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathSum : CSSMathValue
+CONSOLE MESSAGE: line 153:     getter values
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMathValue : CSSNumericValue
+CONSOLE MESSAGE: line 153:     getter operator
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface CSSMatrixComponent : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter matrix
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter matrix
+CONSOLE MESSAGE: line 153: interface CSSNumericArray
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSNumericValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     method add
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method div
+CONSOLE MESSAGE: line 153:     method equals
+CONSOLE MESSAGE: line 153:     method max
+CONSOLE MESSAGE: line 153:     method min
+CONSOLE MESSAGE: line 153:     method mul
+CONSOLE MESSAGE: line 153:     method sub
+CONSOLE MESSAGE: line 153:     method to
+CONSOLE MESSAGE: line 153:     method toSum
+CONSOLE MESSAGE: line 153:     method type
+CONSOLE MESSAGE: line 153: interface CSSPerspective : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter length
+CONSOLE MESSAGE: line 153: interface CSSPositionValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153: interface CSSRotate : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter angle
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter angle
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSScale : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSSkew : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ax
+CONSOLE MESSAGE: line 153:     getter ay
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ax
+CONSOLE MESSAGE: line 153:     setter ay
+CONSOLE MESSAGE: line 153: interface CSSSkewX : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ax
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ax
+CONSOLE MESSAGE: line 153: interface CSSSkewY : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter ay
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter ay
+CONSOLE MESSAGE: line 153: interface CSSStyleValue
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method toString
+CONSOLE MESSAGE: line 153: interface CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter is2D
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method toMatrix
+CONSOLE MESSAGE: line 153:     method toString
+CONSOLE MESSAGE: line 153:     setter is2D
+CONSOLE MESSAGE: line 153: interface CSSTransformValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter is2D
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method toMatrix
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSTranslate : CSSTransformComponent
+CONSOLE MESSAGE: line 153:     getter x
+CONSOLE MESSAGE: line 153:     getter y
+CONSOLE MESSAGE: line 153:     getter z
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter x
+CONSOLE MESSAGE: line 153:     setter y
+CONSOLE MESSAGE: line 153:     setter z
+CONSOLE MESSAGE: line 153: interface CSSUnitValue : CSSNumericValue
+CONSOLE MESSAGE: line 153:     getter unit
+CONSOLE MESSAGE: line 153:     getter value
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter value
+CONSOLE MESSAGE: line 153: interface CSSUnparsedValue : CSSStyleValue
+CONSOLE MESSAGE: line 153:     getter length
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface CSSVariableReferenceValue
+CONSOLE MESSAGE: line 153:     getter fallback
+CONSOLE MESSAGE: line 153:     getter variable
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     setter variable
+CONSOLE MESSAGE: line 153: interface CountQueuingStrategy
+CONSOLE MESSAGE: line 153:     getter highWaterMark
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface PaintRenderingContext2D
+CONSOLE MESSAGE: line 153:     getter fillStyle
+CONSOLE MESSAGE: line 153:     getter filter
+CONSOLE MESSAGE: line 153:     getter globalAlpha
+CONSOLE MESSAGE: line 153:     getter globalCompositeOperation
+CONSOLE MESSAGE: line 153:     getter imageSmoothingEnabled
+CONSOLE MESSAGE: line 153:     getter imageSmoothingQuality
+CONSOLE MESSAGE: line 153:     getter lineCap
+CONSOLE MESSAGE: line 153:     getter lineDashOffset
+CONSOLE MESSAGE: line 153:     getter lineJoin
+CONSOLE MESSAGE: line 153:     getter lineWidth
+CONSOLE MESSAGE: line 153:     getter miterLimit
+CONSOLE MESSAGE: line 153:     getter shadowBlur
+CONSOLE MESSAGE: line 153:     getter shadowColor
+CONSOLE MESSAGE: line 153:     getter shadowOffsetX
+CONSOLE MESSAGE: line 153:     getter shadowOffsetY
+CONSOLE MESSAGE: line 153:     getter strokeStyle
+CONSOLE MESSAGE: line 153:     method arc
+CONSOLE MESSAGE: line 153:     method arcTo
+CONSOLE MESSAGE: line 153:     method beginPath
+CONSOLE MESSAGE: line 153:     method bezierCurveTo
+CONSOLE MESSAGE: line 153:     method clearRect
+CONSOLE MESSAGE: line 153:     method clip
+CONSOLE MESSAGE: line 153:     method closePath
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method createLinearGradient
+CONSOLE MESSAGE: line 153:     method createPattern
+CONSOLE MESSAGE: line 153:     method createRadialGradient
+CONSOLE MESSAGE: line 153:     method drawImage
+CONSOLE MESSAGE: line 153:     method ellipse
+CONSOLE MESSAGE: line 153:     method fill
+CONSOLE MESSAGE: line 153:     method fillRect
+CONSOLE MESSAGE: line 153:     method getLineDash
+CONSOLE MESSAGE: line 153:     method getTransform
+CONSOLE MESSAGE: line 153:     method isPointInPath
+CONSOLE MESSAGE: line 153:     method isPointInStroke
+CONSOLE MESSAGE: line 153:     method lineTo
+CONSOLE MESSAGE: line 153:     method moveTo
+CONSOLE MESSAGE: line 153:     method quadraticCurveTo
+CONSOLE MESSAGE: line 153:     method rect
+CONSOLE MESSAGE: line 153:     method resetTransform
+CONSOLE MESSAGE: line 153:     method restore
+CONSOLE MESSAGE: line 153:     method rotate
+CONSOLE MESSAGE: line 153:     method save
+CONSOLE MESSAGE: line 153:     method scale
+CONSOLE MESSAGE: line 153:     method setLineDash
+CONSOLE MESSAGE: line 153:     method setTransform
+CONSOLE MESSAGE: line 153:     method stroke
+CONSOLE MESSAGE: line 153:     method strokeRect
+CONSOLE MESSAGE: line 153:     method transform
+CONSOLE MESSAGE: line 153:     method translate
+CONSOLE MESSAGE: line 153:     setter fillStyle
+CONSOLE MESSAGE: line 153:     setter filter
+CONSOLE MESSAGE: line 153:     setter globalAlpha
+CONSOLE MESSAGE: line 153:     setter globalCompositeOperation
+CONSOLE MESSAGE: line 153:     setter imageSmoothingEnabled
+CONSOLE MESSAGE: line 153:     setter imageSmoothingQuality
+CONSOLE MESSAGE: line 153:     setter lineCap
+CONSOLE MESSAGE: line 153:     setter lineDashOffset
+CONSOLE MESSAGE: line 153:     setter lineJoin
+CONSOLE MESSAGE: line 153:     setter lineWidth
+CONSOLE MESSAGE: line 153:     setter miterLimit
+CONSOLE MESSAGE: line 153:     setter shadowBlur
+CONSOLE MESSAGE: line 153:     setter shadowColor
+CONSOLE MESSAGE: line 153:     setter shadowOffsetX
+CONSOLE MESSAGE: line 153:     setter shadowOffsetY
+CONSOLE MESSAGE: line 153:     setter strokeStyle
+CONSOLE MESSAGE: line 153: interface PaintSize
+CONSOLE MESSAGE: line 153:     getter height
+CONSOLE MESSAGE: line 153:     getter width
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface PaintWorkletGlobalScope : WorkletGlobalScope
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface Path2D
+CONSOLE MESSAGE: line 153:     method addPath
+CONSOLE MESSAGE: line 153:     method arc
+CONSOLE MESSAGE: line 153:     method arcTo
+CONSOLE MESSAGE: line 153:     method bezierCurveTo
+CONSOLE MESSAGE: line 153:     method closePath
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method ellipse
+CONSOLE MESSAGE: line 153:     method lineTo
+CONSOLE MESSAGE: line 153:     method moveTo
+CONSOLE MESSAGE: line 153:     method quadraticCurveTo
+CONSOLE MESSAGE: line 153:     method rect
+CONSOLE MESSAGE: line 153: interface ReadableStream
+CONSOLE MESSAGE: line 153:     getter locked
+CONSOLE MESSAGE: line 153:     method cancel
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getReader
+CONSOLE MESSAGE: line 153:     method pipeThrough
+CONSOLE MESSAGE: line 153:     method pipeTo
+CONSOLE MESSAGE: line 153:     method tee
+CONSOLE MESSAGE: line 153: interface ReadableStreamDefaultReader
+CONSOLE MESSAGE: line 153:     getter closed
+CONSOLE MESSAGE: line 153:     method cancel
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method read
+CONSOLE MESSAGE: line 153:     method releaseLock
+CONSOLE MESSAGE: line 153: interface StylePropertyMapReadOnly
+CONSOLE MESSAGE: line 153:     getter size
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method entries
+CONSOLE MESSAGE: line 153:     method forEach
+CONSOLE MESSAGE: line 153:     method get
+CONSOLE MESSAGE: line 153:     method getAll
+CONSOLE MESSAGE: line 153:     method has
+CONSOLE MESSAGE: line 153:     method keys
+CONSOLE MESSAGE: line 153:     method values
+CONSOLE MESSAGE: line 153: interface TransformStream
+CONSOLE MESSAGE: line 153:     getter readable
+CONSOLE MESSAGE: line 153:     getter writable
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WorkletGlobalScope
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153: interface WritableStream
+CONSOLE MESSAGE: line 153:     getter locked
+CONSOLE MESSAGE: line 153:     method abort
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method getWriter
+CONSOLE MESSAGE: line 153: interface WritableStreamDefaultWriter
+CONSOLE MESSAGE: line 153:     getter closed
+CONSOLE MESSAGE: line 153:     getter desiredSize
+CONSOLE MESSAGE: line 153:     getter ready
+CONSOLE MESSAGE: line 153:     method abort
+CONSOLE MESSAGE: line 153:     method close
+CONSOLE MESSAGE: line 153:     method constructor
+CONSOLE MESSAGE: line 153:     method releaseLock
+CONSOLE MESSAGE: line 153:     method write
+CONSOLE MESSAGE: line 153: global object
+CONSOLE MESSAGE: line 153:     attribute console
+CONSOLE MESSAGE: line 153:     attribute globalThis
+CONSOLE MESSAGE: line 153:     getter devicePixelRatio
+CONSOLE MESSAGE: line 153:     method gc
+CONSOLE MESSAGE: line 153:     method registerPaint
+
diff --git a/third_party/googletest/.gitignore b/third_party/googletest/.gitignore
new file mode 100644
index 0000000..a57582c
--- /dev/null
+++ b/third_party/googletest/.gitignore
@@ -0,0 +1 @@
+/src
diff --git a/third_party/googletest/DEPS b/third_party/googletest/DEPS
new file mode 100644
index 0000000..f23dccc9
--- /dev/null
+++ b/third_party/googletest/DEPS
@@ -0,0 +1,18 @@
+# This file is a dummy used so that non-Chromium clients can specify
+# recursive DEPS. Otherwise the clients would need to nest DEPS inside
+# each other. Nested DEPS are not supported by gclient.
+#
+# Clients *must* specify googletest_revision when using this DEPS file.
+
+use_relative_paths = True
+
+vars = {
+  'chromium_git': 'https://chromium.googlesource.com',
+
+  # We must specify a dummy variable here for recursedeps to work.
+  'googletest_revision': 'master',
+}
+
+deps = {
+  'src': '{chromium_git}/external/github.com/google/googletest.git@{googletest_revision}'
+}
diff --git a/third_party/mesa_headers/LICENSE b/third_party/mesa_headers/LICENSE
index 792c6fe..e4e08870c 100644
--- a/third_party/mesa_headers/LICENSE
+++ b/third_party/mesa_headers/LICENSE
@@ -1,3 +1,7 @@
+The Mesa header files use the following licenses.
+
+================================================================================
+
 The default Mesa license is as follows:
 
 Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
@@ -19,494 +23,20 @@
 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
+================================================================================
 
+GLES/glext.h, GLES/gl.h and GLES/glplatform.h use the following license:
 
-Some parts of Mesa are copyrighted under the GNU LGPL.  See the
-Mesa/docs/COPYRIGHT file for details.
+SGI FREE SOFTWARE LICENSE B
+(Version 2.0, Sept. 18, 2008)
 
-The following is the standard GNU copyright file.
-----------------------------------------------------------------------
+Copyright (C) [dates of first publication] Silicon Graphics, Inc. All Rights Reserved.
 
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 
-		  GNU LIBRARY GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
+The above copyright notice including the dates of first publication and either this permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be included in all copies or substantial portions of the Software.
 
- Copyright (C) 1991 Free Software Foundation, Inc.
-                    675 Mass Ave, Cambridge, MA 02139, USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-[This is the first released version of the library GPL.  It is
- numbered 2 because it goes with version 2 of the ordinary GPL.]
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Library General Public License, applies to some
-specially designated Free Software Foundation software, and to any
-other libraries whose authors decide to use it.  You can use it for
-your libraries, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if
-you distribute copies of the library, or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link a program with the library, you must provide
-complete object files to the recipients so that they can relink them
-with the library, after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  Our method of protecting your rights has two steps: (1) copyright
-the library, and (2) offer you this license which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  Also, for each distributor's protection, we want to make certain
-that everyone understands that there is no warranty for this free
-library.  If the library is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original
-version, so that any problems introduced by others will not reflect on
-the original authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that companies distributing free
-software will individually obtain patent licenses, thus in effect
-transforming the program into proprietary software.  To prevent this,
-we have made it clear that any patent must be licensed for everyone's
-free use or not licensed at all.
-
-  Most GNU software, including some libraries, is covered by the ordinary
-GNU General Public License, which was designed for utility programs.  This
-license, the GNU Library General Public License, applies to certain
-designated libraries.  This license is quite different from the ordinary
-one; be sure to read it in full, and don't assume that anything in it is
-the same as in the ordinary license.
-
-  The reason we have a separate public license for some libraries is that
-they blur the distinction we usually make between modifying or adding to a
-program and simply using it.  Linking a program with a library, without
-changing the library, is in some sense simply using the library, and is
-analogous to running a utility program or application program.  However, in
-a textual and legal sense, the linked executable is a combined work, a
-derivative of the original library, and the ordinary General Public License
-treats it as such.
-
-  Because of this blurred distinction, using the ordinary General
-Public License for libraries did not effectively promote software
-sharing, because most developers did not use the libraries.  We
-concluded that weaker conditions might promote sharing better.
-
-  However, unrestricted linking of non-free programs would deprive the
-users of those programs of all benefit from the free status of the
-libraries themselves.  This Library General Public License is intended to
-permit developers of non-free programs to use free libraries, while
-preserving your freedom as a user of such programs to change the free
-libraries that are incorporated in them.  (We have not seen how to achieve
-this as regards changes in header files, but we have achieved it as regards
-changes in the actual functions of the Library.)  The hope is that this
-will lead to faster development of free libraries.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, while the latter only
-works together with the library.
-
-  Note that it is possible for a library to be covered by the ordinary
-General Public License rather than by this special one.
-
-		  GNU LIBRARY GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library which
-contains a notice placed by the copyright holder or other authorized
-party saying it may be distributed under the terms of this Library
-General Public License (also called "this License").  Each licensee is
-addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-  
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-
-  6. As an exception to the Sections above, you may also compile or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Accompany the work with a written offer, valid for at
-    least three years, to give the same user the materials
-    specified in Subsection 6a, above, for a charge no more
-    than the cost of performing this distribution.
-
-    c) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    d) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the source code distributed need not include anything that is normally
-distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded.  In such case, this License incorporates the limitation as if
-written in the body of this License.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Library General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
-			    NO WARRANTY
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-     Appendix: How to Apply These Terms to Your New Libraries
-
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.  It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library; if not, write to the Free
-    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
-  <signature of Ty Coon>, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!
+Except as contained in this notice, the name of Silicon Graphics, Inc. shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Silicon Graphics, Inc.
 
diff --git a/third_party/mesa_headers/README.chromium b/third_party/mesa_headers/README.chromium
index 90312c2..03f9ee52e 100644
--- a/third_party/mesa_headers/README.chromium
+++ b/third_party/mesa_headers/README.chromium
@@ -1,14 +1,13 @@
 Name: mesa_headers
 Version: 9.0.3
 URL: http://www.mesa3d.org/
-License: MIT and LGPL v2
+License: MIT and SGI Free Software B License Version 2.0
 Security Critical: Yes
 
 Description:
 This directory contains a copy of the Mesa headers.
 
-The license file in this directory is derived from src/docs/license.html
-and src/docs/COPYING.
+The LICENSE file in this directory is derived from those header files.
 
 Modifications made:
 - Added the file README.chromium (this file)
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath
index d899752..10e784cc 100644
--- a/tools/android/eclipse/.classpath
+++ b/tools/android/eclipse/.classpath
@@ -55,6 +55,12 @@
     <classpathentry kind="src" path="chrome/android/webapk/shell_apk/src"/>
     <classpathentry kind="src" path="chrome/browser/android/thin_webview/internal/java/src"/>
     <classpathentry kind="src" path="chrome/browser/android/thin_webview/java/src"/>
+    <classpathentry kind="src" path="chrome/browser/download/android/java/src"/>
+    <classpathentry kind="src" path="chrome/browser/image_fetcher/android/java/src"/>
+    <classpathentry kind="src" path="chrome/browser/share/android/java/src"/>
+    <classpathentry kind="src" path="chrome/browser/touch_to_fill/android/java/src"/>
+    <classpathentry kind="src" path="chrome/browser/touch_to_fill/android/internal/java/src"/>
+    <classpathentry kind="src" path="chrome/browser/ui/android/styles/java/src"/>
     <classpathentry kind="src" path="chrome/browser/ui/android/widget/java/src"/>
     <classpathentry kind="src" path="chrome/browser/ui/android/widget/test/java/src"/>
     <classpathentry kind="src" path="chrome/browser/util/android/java/src"/>
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 6a813ba..8f21890 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -393,10 +393,10 @@
       'Android FYI Release (Nexus 9)': 'gpu_tests_android_release_trybot_arm64',
       'Android FYI Release (NVIDIA Shield TV)': 'gpu_tests_android_release_trybot_arm64',
       'Android FYI Release (Pixel 2)': 'gpu_tests_android_release_trybot',
-      'Android FYI 32 Vk Release (Pixel 2)': 'gpu_tests_android_vulkan_release_trybot',
-      'Android FYI 64 Vk Release (Pixel 2)': 'gpu_tests_android_vulkan_release_trybot_arm64',
-      'Android FYI 32 dEQP Vk Release (Pixel 2)': 'deqp_android_vulkan_release_trybot',
-      'Android FYI 64 dEQP Vk Release (Pixel 2)': 'deqp_android_vulkan_release_trybot_arm64',
+      'Android FYI 32 Vk Release (Pixel 2)': 'gpu_tests_android_vulkan_ndk_release_trybot',
+      'Android FYI 64 Vk Release (Pixel 2)': 'gpu_tests_android_vulkan_ndk_release_trybot_arm64',
+      'Android FYI 32 dEQP Vk Release (Pixel 2)': 'deqp_android_vulkan_ndk_release_trybot',
+      'Android FYI 64 dEQP Vk Release (Pixel 2)': 'deqp_android_vulkan_ndk_release_trybot_arm64',
       'Android FYI SkiaRenderer GL (Nexus 5X)': 'gpu_tests_android_release_trybot_arm64',
       'Android FYI SkiaRenderer Vulkan (Pixel 2)': 'gpu_tests_android_release_trybot',
       'GPU FYI Linux Builder': 'gpu_fyi_tests_release_trybot',
@@ -406,13 +406,15 @@
       'GPU FYI Mac Builder': 'gpu_fyi_tests_release_trybot',
       'GPU FYI Mac Builder (dbg)': 'gpu_fyi_tests_debug_trybot',
       'GPU FYI Mac dEQP Builder': 'deqp_release_trybot',
-      'GPU FYI Perf Android 64 Builder': 'gpu_tests_android_vulkan_release_trybot_arm64',
+      'GPU FYI Perf Android 64 Builder': 'gpu_tests_android_vulkan_ndk_release_trybot_arm64',
       'GPU FYI Win Builder': 'gpu_fyi_tests_release_trybot_x86',
       'GPU FYI Win Builder (dbg)': 'gpu_fyi_tests_debug_trybot_x86',
       'GPU FYI Win dEQP Builder': 'deqp_release_trybot_x86',
       'GPU FYI Win x64 Builder': 'gpu_fyi_tests_release_trybot',
       'GPU FYI Win x64 Builder (dbg)': 'gpu_fyi_tests_debug_trybot',
       'GPU FYI Win x64 dEQP Builder': 'deqp_release_trybot',
+      'GPU FYI Win x64 DX12 Vulkan Builder': 'gpu_fyi_tests_dx12vk_release_trybot',
+      'GPU FYI Win x64 DX12 Vulkan Builder (dbg)': 'gpu_fyi_tests_dx12vk_debug_trybot',
       'GPU FYI XR Win x64 Builder': 'gpu_fyi_tests_release_trybot',
       'Linux FYI GPU TSAN Release': 'gpu_fyi_tests_release_trybot_tsan',
       'Mac FYI GPU ASAN Release': 'gpu_fyi_tests_release_trybot_asan',
@@ -694,10 +696,10 @@
       'gpu-fyi-try-android-n-nvidia-shield-tv-64': 'gpu_tests_android_release_trybot_arm64',
       'gpu-fyi-try-android-p-pixel-2-32': 'gpu_tests_android_release_trybot',
       'gpu-fyi-try-android-p-pixel-2-skv-32': 'gpu_tests_android_release_trybot',
-      'gpu-fyi-try-android-q-pixel-2-deqp-vk-32': 'deqp_android_vulkan_release_trybot',
-      'gpu-fyi-try-android-q-pixel-2-deqp-vk-64': 'deqp_android_vulkan_release_trybot_arm64',
-      'gpu-fyi-try-android-q-pixel-2-vk-32': 'gpu_tests_android_vulkan_release_trybot',
-      'gpu-fyi-try-android-q-pixel-2-vk-64': 'gpu_tests_android_vulkan_release_trybot_arm64',
+      'gpu-fyi-try-android-q-pixel-2-deqp-vk-32': 'deqp_android_vulkan_ndk_release_trybot',
+      'gpu-fyi-try-android-q-pixel-2-deqp-vk-64': 'deqp_android_vulkan_ndk_release_trybot_arm64',
+      'gpu-fyi-try-android-q-pixel-2-vk-32': 'gpu_tests_android_vulkan_ndk_release_trybot',
+      'gpu-fyi-try-android-q-pixel-2-vk-64': 'gpu_tests_android_vulkan_ndk_release_trybot_arm64',
       'gpu-try-android-m-nexus-5x-64': 'gpu_tests_android_release_trybot_arm64',
       'linux_android_dbg_ng': 'android_debug_trybot',
       'try-nougat-phone-tester': 'android_debug_trybot_arm64',
@@ -707,11 +709,11 @@
 
     'tryserver.chromium.angle': {
       'android_angle_rel_ng': 'gpu_tests_android_release_trybot_arm64',
-      'android_angle_vk32_rel_ng': 'gpu_tests_android_vulkan_release_trybot',
-      'android_angle_vk64_rel_ng': 'gpu_tests_android_vulkan_release_trybot_arm64',
+      'android_angle_vk32_rel_ng': 'gpu_tests_android_vulkan_ndk_release_trybot',
+      'android_angle_vk64_rel_ng': 'gpu_tests_android_vulkan_ndk_release_trybot_arm64',
       'android_angle_deqp_rel_ng': 'deqp_android_release_trybot_arm64',
-      'android_angle_vk32_deqp_rel_ng': 'deqp_android_vulkan_release_trybot',
-      'android_angle_vk64_deqp_rel_ng': 'deqp_android_vulkan_release_trybot_arm64',
+      'android_angle_vk32_deqp_rel_ng': 'deqp_android_vulkan_ndk_release_trybot',
+      'android_angle_vk64_deqp_rel_ng': 'deqp_android_vulkan_ndk_release_trybot_arm64',
       'fuchsia-angle-rel': 'gpu_fyi_tests_release_trybot_fuchsia',
       'linux-angle-rel': 'gpu_fyi_tests_release_trybot',
       'linux_angle_ozone_rel_ng': 'gpu_fyi_tests_ozone_linux_system_gbm_libdrm_release_trybot',
@@ -895,6 +897,8 @@
       'gpu-fyi-try-win10-intel-rel-64': 'gpu_fyi_tests_release_trybot',
       'gpu-fyi-try-win10-nvidia-dbg-64': 'gpu_fyi_tests_debug_trybot',
       'gpu-fyi-try-win10-nvidia-dqp-64': 'deqp_release_trybot',
+      'gpu-fyi-try-win10-nvidia-dx12vk-dbg-64': 'gpu_fyi_tests_dx12vk_debug_trybot',
+      'gpu-fyi-try-win10-nvidia-dx12vk-rel-64': 'gpu_fyi_tests_dx12vk_release_trybot',
       'gpu-fyi-try-win10-nvidia-exp-64': 'gpu_fyi_tests_release_trybot',
       'gpu-fyi-try-win10-nvidia-rel-32': 'gpu_tests_release_trybot_x86_resource_whitelisting',
       'gpu-fyi-try-win10-nvidia-rel-64': 'gpu_fyi_tests_release_trybot',
@@ -1537,12 +1541,12 @@
       'angle_deqp_tests', 'android', 'release_trybot', 'arm64',
     ],
 
-    'deqp_android_vulkan_release_trybot': [
-      'angle_deqp_tests', 'android', 'vulkan', 'release_trybot',
+    'deqp_android_vulkan_ndk_release_trybot': [
+      'angle_deqp_tests', 'android', 'vulkan_ndk', 'release_trybot',
     ],
 
-    'deqp_android_vulkan_release_trybot_arm64': [
-      'angle_deqp_tests', 'android', 'vulkan', 'release_trybot', 'arm64',
+    'deqp_android_vulkan_ndk_release_trybot_arm64': [
+      'angle_deqp_tests', 'android', 'vulkan_ndk', 'release_trybot', 'arm64',
     ],
 
     'deqp_release_trybot_x86': [
@@ -1557,6 +1561,10 @@
       'gpu_fyi_tests', 'debug_trybot',
     ],
 
+    'gpu_fyi_tests_dx12vk_debug_trybot': [
+      'gpu_fyi_tests', 'dx12vk', 'debug_trybot',
+    ],
+
     'gpu_fyi_tests_debug_trybot_x86': [
       'gpu_fyi_tests', 'debug_trybot', 'x86',
     ],
@@ -1585,6 +1593,10 @@
       'gpu_fyi_tests', 'release_trybot', 'fuchsia',
     ],
 
+    'gpu_fyi_tests_dx12vk_release_trybot': [
+      'gpu_fyi_tests', 'dx12vk',
+    ],
+
     'gpu_tests_android_release_bot_minimal_symbols_arm64': [
       'android', 'release_bot', 'minimal_symbols', 'arm64',
       'resource_whitelisting', 'static_angle',
@@ -1607,12 +1619,12 @@
       'use_java_coverage', 'partial_code_coverage_instrumentation',
     ],
 
-    'gpu_tests_android_vulkan_release_trybot': [
-      'gpu_tests', 'android', 'vulkan', 'release_trybot', 'static_angle',
+    'gpu_tests_android_vulkan_ndk_release_trybot': [
+      'gpu_tests', 'android', 'vulkan_ndk', 'release_trybot', 'static_angle',
     ],
 
-    'gpu_tests_android_vulkan_release_trybot_arm64': [
-      'gpu_tests', 'android', 'vulkan', 'release_trybot', 'arm64', 'static_angle',
+    'gpu_tests_android_vulkan_ndk_release_trybot_arm64': [
+      'gpu_tests', 'android', 'vulkan_ndk', 'release_trybot', 'arm64', 'static_angle',
     ],
 
     'gpu_tests_debug_bot': [
@@ -2186,6 +2198,11 @@
       'gn_args': 'archive_seed_corpus=false',
     },
 
+    'dx12vk': {
+      # TODO(https://crbug.com/1010584): Explicitly enable DirectX 12.
+      'mixins': ['enable_vulkan'],
+    },
+
     'enable_blink_animation_use_time_delta': {
       'gn_args': 'blink_animation_use_time_delta=true',
     },
@@ -2201,6 +2218,10 @@
       'gn_args': 'skip_archive_compression=false',
     },
 
+    'enable_vulkan': {
+      'gn_args': 'enable_vulkan=true',
+    },
+
     # This mixin is used to force configs that use it to fail. It
     # is used in two cases: when we have bots that we haven't looked
     # at yet and don't know whether they need MB or not, and for bots
@@ -2504,7 +2525,7 @@
       'gn_args': 'enable_vr=true',
     },
 
-    'vulkan': {
+    'vulkan_ndk': {
       'gn_args': 'android32_ndk_api_level=26 android64_ndk_api_level=26',
     },
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index e6fc89c..f01557e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2538,6 +2538,18 @@
   <int value="4" label="Account check failed"/>
 </enum>
 
+<enum name="ArcAuthMainAccountResolutionStatus">
+  <int value="0" label="No hash code no account"/>
+  <int value="1" label="No hash code for single account"/>
+  <int value="2" label="No hash code for multiple accounts"/>
+  <int value="3" label="Hash code set but no account"/>
+  <int value="4" label="Hash code set but does not match single account"/>
+  <int value="5" label="Hash code set but does not any account"/>
+  <int value="6" label="Hash code set and matches single account"/>
+  <int value="7" label="Hash code set and matches one of multiple accounts"/>
+  <int value="8" label="Hash code collides with multiple accounts"/>
+</enum>
+
 <enum name="ArcBootContinueCodeInstallationResult">
   <int value="0" label="Success"/>
   <int value="1" label="Host side code is not ready"/>
@@ -31528,6 +31540,25 @@
   </int>
 </enum>
 
+<enum name="ImeHandwritingActions">
+  <int value="0" label="InsertCreatedByUnknown"/>
+  <int value="1" label="InsertCreatedAfterDelete"/>
+  <int value="2" label="InsertCreatedByTouchTap"/>
+  <int value="3" label="InsertCreatedByPenTap"/>
+  <int value="4" label="InsertCreatedByUpCaret"/>
+  <int value="5" label="InsertCreatedByDownCaret"/>
+  <int value="6" label="EmptyInsertRangeClosed"/>
+  <int value="7" label="InsertRangeWithTextClosed"/>
+  <int value="8" label="DeletedSingleWord"/>
+  <int value="9" label="UndoInsertCreatedByUnknown"/>
+  <int value="10" label="UndoInsertCreatedAfterDelete"/>
+  <int value="11" label="UndoInsertCreatedByTouchTap"/>
+  <int value="12" label="UndoInsertCreatedByPenTap"/>
+  <int value="13" label="UndoInsertCreatedByUpCaret"/>
+  <int value="14" label="UndoInsertCreatedByDownCaret"/>
+  <int value="15" label="UndoDeletedSingleWord"/>
+</enum>
+
 <enum name="ImeMenuButtonType">
   <int value="0" label="Unknown"/>
   <int value="1" label="Emoji"/>
@@ -37282,6 +37313,7 @@
   <int value="460475728" label="wake-on-wifi-packet"/>
   <int value="463582989" label="CompositorThreadedScrollbarScrolling:disabled"/>
   <int value="464226051" label="CrOSComponent:enabled"/>
+  <int value="464773709" label="OmniboxExperimentalSuggestScoring:enabled"/>
   <int value="466248382" label="disable-push-api-background-mode"/>
   <int value="468665559" label="TrilinearFiltering:disabled"/>
   <int value="468959230"
@@ -38547,6 +38579,7 @@
   <int value="2123183411" label="BlinkHeapIncrementalMarking:enabled"/>
   <int value="2123567684" label="OptimizeLoadingIPCForSmallResources:enabled"/>
   <int value="2126203058" label="force-show-update-menu-badge"/>
+  <int value="2127648677" label="OmniboxExperimentalSuggestScoring:disabled"/>
   <int value="2129184006" label="NTPOfflinePageDownloadSuggestions:enabled"/>
   <int value="2129929643" label="enable-use-zoom-for-dsf"/>
   <int value="2132595171" label="OmniboxSearchEngineLogo:enabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 4287b22..01b9d35 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -7201,6 +7201,13 @@
   </summary>
 </histogram>
 
+<histogram name="ArcAuth.MainAccountResolutionStatus"
+    enum="ArcAuthMainAccountResolutionStatus" expires_after="M82">
+  <owner>khmel@chromium.org</owner>
+  <owner>jhorwich@chromium.org</owner>
+  <summary>Contains the status of main account resolution.</summary>
+</histogram>
+
 <histogram name="ArcAuth.NetworkWaitTime" units="ms" expires_after="M77">
   <owner>elijahtaylor@chromium.org</owner>
   <summary>
@@ -55437,6 +55444,46 @@
   </summary>
 </histogram>
 
+<histogram name="InputMethod.Handwriting.Actions" enum="ImeHandwritingActions"
+    expires_after="2020-03-01">
+  <owner>shend@chromium.org</owner>
+  <owner>essential-inputs-team@google.com</owner>
+  <summary>
+    Types of actions performed by the user during handwriting, recorded when the
+    user performs a gesture action.
+  </summary>
+</histogram>
+
+<histogram name="InputMethod.Handwriting.CharsEdited10s" units="chars"
+    expires_after="2020-03-01">
+  <owner>shend@chromium.org</owner>
+  <owner>essential-inputs-team@google.com</owner>
+  <summary>
+    Number of characters written in a 10 second interval, recorded every 10s
+    while the handwriting keyboard is active.
+  </summary>
+</histogram>
+
+<histogram name="InputMethod.Handwriting.CharsEdited5s" units="chars"
+    expires_after="2020-03-01">
+  <owner>shend@chromium.org</owner>
+  <owner>essential-inputs-team@google.com</owner>
+  <summary>
+    Number of characters written in a 5 second interval, recorded every 5s while
+    the handwriting keyboard is active.
+  </summary>
+</histogram>
+
+<histogram name="InputMethod.Handwriting.CharsEdited60s" units="chars"
+    expires_after="2020-03-01">
+  <owner>shend@chromium.org</owner>
+  <owner>essential-inputs-team@google.com</owner>
+  <summary>
+    Number of characters written in a 60 second interval, recorded every 60s
+    while the handwriting keyboard is active.
+  </summary>
+</histogram>
+
 <histogram name="InputMethod.ID" enum="InputMethodID"
     expires_after="2015-07-16">
   <obsolete>
@@ -166188,6 +166235,7 @@
   <affected-histogram name="Arc.Provisioning.TimeDelta.Success"/>
   <affected-histogram name="Arc.Reauthorization.Result"/>
   <affected-histogram name="Arc.StateByUserType"/>
+  <affected-histogram name="ArcAuth.MainAccountResolutionStatus"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="AsyncDNSPref" separator="_">
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 1472a54b..7934cd2 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -3712,6 +3712,18 @@
       activation on the page or without a user initiated same-domain navigation.
     </summary>
   </metric>
+  <metric name="CompletedRebuffersCount">
+    <summary>
+      Integer count of the number of times a previous rebuffer succeeded and
+      playback continued.
+    </summary>
+  </metric>
+  <metric name="CompletedRebuffersDuration">
+    <summary>
+      Sum in milliseconds of all completed rebuffering events. Only reported if
+      a rebuffering completion occurred.
+    </summary>
+  </metric>
   <metric name="Duration">
     <summary>
       Duration in milliseconds, rounded to the most significant digit, of the
@@ -3805,6 +3817,17 @@
       if the video track is unencrypted.
     </summary>
   </metric>
+  <metric name="VideoFramesDecoded">
+    <summary>
+      Integer count of the video frames decoded in this record.
+    </summary>
+  </metric>
+  <metric name="VideoFramesDropped">
+    <summary>
+      Integer count of the video frames dropped in this record. Should not
+      exceed VideoFramesDecoded.
+    </summary>
+  </metric>
   <metric name="VideoNaturalHeight">
     <summary>
       Integer value indicating the natural height of the playback.
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 946ef83..e1584b64 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -8,14 +8,14 @@
 blink_perf.canvas,"aaronhk@chromium.org, fserb@chromium.org",Blink>Canvas,https://bit.ly/blink-perf-benchmarks,
 blink_perf.css,"futhark@chromium.org, andruud@chromium.org",Blink>CSS,https://bit.ly/blink-perf-benchmarks,
 blink_perf.display_locking,vmpstr@chromium.org,Blink>Paint,https://bit.ly/blink-perf-benchmarks,
-blink_perf.dom,hayato@chromium.org,Blink>DOM,https://bit.ly/blink-perf-benchmarks,
-blink_perf.events,hayato@chromium.org,Blink>DOM,https://bit.ly/blink-perf-benchmarks,
+blink_perf.dom,masonfreed@chromium.org,Blink>DOM,https://bit.ly/blink-perf-benchmarks,
+blink_perf.events,masonfreed@chromium.org,Blink>DOM,https://bit.ly/blink-perf-benchmarks,
 blink_perf.image_decoder,cblume@chromium.org,Internals>Images>Codecs,https://bit.ly/blink-perf-benchmarks,
 blink_perf.layout,eae@chromium.org,Blink>Layout,https://bit.ly/blink-perf-benchmarks,
 blink_perf.owp_storage,dmurph@chromium.org,Blink>Storage,https://bit.ly/blink-perf-benchmarks,
 blink_perf.paint,wangxianzhu@chromium.org,Blink>Paint,https://bit.ly/blink-perf-benchmarks,
 blink_perf.parser,"jbroman@chromium.org, yukishiino@chromium.org, haraken@chromium.org",Blink>Bindings,https://bit.ly/blink-perf-benchmarks,
-blink_perf.shadow_dom,hayato@chromium.org,Blink>DOM>ShadowDOM,https://bit.ly/blink-perf-benchmarks,
+blink_perf.shadow_dom,masonfreed@chromium.org,Blink>DOM>ShadowDOM,https://bit.ly/blink-perf-benchmarks,
 blink_perf.svg,"kouhei@chromium.org, fs@opera.com",Blink>SVG,https://bit.ly/blink-perf-benchmarks,
 components_perftests,csharrison@chromium.org,,,
 dawn_perf_tests,"enga@chromium.org, chrome-gpu-perf-owners@chromium.org",Internals>GPU>Dawn,https://dawn.googlesource.com/dawn/+/HEAD/src/tests/perf_tests/README.md,
diff --git a/tools/perf/benchmarks/benchmark_smoke_unittest.py b/tools/perf/benchmarks/benchmark_smoke_unittest.py
index a508d02..3fefdc1 100644
--- a/tools/perf/benchmarks/benchmark_smoke_unittest.py
+++ b/tools/perf/benchmarks/benchmark_smoke_unittest.py
@@ -65,12 +65,6 @@
         # values, and does not take histograms into account. An alternative
         # should be implemented when using the results processor.
         type(self).MAX_NUM_VALUES = MAX_NUM_VALUES / len(story_set)
-
-        # Only smoke test the first story since smoke testing everything takes
-        # too long.
-        for s in story_set.stories[num_pages:]:
-          story_set.RemoveStory(s)
-
         return story_set
 
     # Some benchmarks are running multiple iterations
@@ -83,6 +77,9 @@
       options = options_for_unittests.GetRunOptions(
           output_dir=temp_dir,
           benchmark_cls=SinglePageBenchmark,
+          # Only smoke test num_pages since smoke testing everything takes
+          # too long.
+          overrides={'story_shard_end_index': num_pages},
           environment=chromium_config.GetDefaultChromiumConfig())
       options.pageset_repeat = 1  # For smoke testing only run the page once.
       single_page_benchmark = SinglePageBenchmark()
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py
index 9e306350..eda3b9b 100644
--- a/tools/perf/benchmarks/blink_perf.py
+++ b/tools/perf/benchmarks/blink_perf.py
@@ -428,7 +428,7 @@
       page.skipped_gpus = []
     return story_set
 
-@benchmark.Info(emails=['hayato@chromium.org'],
+@benchmark.Info(emails=['masonfreed@chromium.org'],
                 component='Blink>DOM',
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfDOM(_BlinkPerfBenchmark):
@@ -439,7 +439,7 @@
     return 'blink_perf.dom'
 
 
-@benchmark.Info(emails=['hayato@chromium.org'],
+@benchmark.Info(emails=['masonfreed@chromium.org'],
                 component='Blink>DOM',
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfEvents(_BlinkPerfBenchmark):
@@ -537,7 +537,7 @@
     return 'blink_perf.svg'
 
 
-@benchmark.Info(emails=['hayato@chromium.org'],
+@benchmark.Info(emails=['masonfreed@chromium.org'],
                 component='Blink>DOM>ShadowDOM',
                 documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfShadowDOM(_BlinkPerfBenchmark):
diff --git a/tools/perf/benchmarks/media.py b/tools/perf/benchmarks/media.py
index dcb0e15d9..d0808be4 100644
--- a/tools/perf/benchmarks/media.py
+++ b/tools/perf/benchmarks/media.py
@@ -39,8 +39,9 @@
     options.config.chrome_trace_config.SetMemoryDumpConfig(
         chrome_trace_config.MemoryDumpConfig())
 
-    options.SetTimelineBasedMetrics(['mediaMetric', 'cpuTimeMetric',
-                                     'memoryMetric'])
+    # Note that memoryMetric is added using GetExtraTracingMetrics() for
+    # certain stories.
+    options.SetTimelineBasedMetrics(['mediaMetric', 'cpuTimeMetric'])
     return options
 
 
@@ -52,7 +53,7 @@
   SUPPORTED_PLATFORMS = [story.expectations.ALL_DESKTOP]
 
   def CreateStorySet(self, options):
-    return page_sets.MediaCasesDesktopStorySet(measure_memory=True)
+    return page_sets.MediaCasesDesktopStorySet()
 
   @classmethod
   def Name(cls):
@@ -69,7 +70,7 @@
   SUPPORTED_PLATFORMS = [story.expectations.ANDROID_NOT_WEBVIEW]
 
   def CreateStorySet(self, options):
-    return page_sets.MediaCasesMobileStorySet(measure_memory=True)
+    return page_sets.MediaCasesMobileStorySet()
 
   @classmethod
   def Name(cls):
diff --git a/tools/perf/page_sets/media_cases.py b/tools/perf/page_sets/media_cases.py
index eea6b7c..1cdf9aa 100644
--- a/tools/perf/page_sets/media_cases.py
+++ b/tools/perf/page_sets/media_cases.py
@@ -86,6 +86,22 @@
         extra_browser_args=extra_browser_args,
         traffic_setting=traffic_setting)
 
+  def GetExtraTracingMetrics(self):
+    metrics = super(_MediaPage, self).GetExtraTracingMetrics()
+    if self.ShouldMeasureMemory():
+      metrics.append('memoryMetric')
+    return metrics
+
+  def ShouldMeasureMemory(self):
+    """Returns whether the page should do a memory dump.
+
+    Also controls whether the pages enables the memory metric.
+    Subclasses should override to disable memory measurement.
+    Memory dumps are cpu-intensive, so it is reasonable keep it off in
+    some cases to avoid skewing cpu usage measurements.
+    """
+    return True
+
 
 class _BeginningToEndPlayPage(_MediaPage):
   """A normal play page simply plays the given media until the end."""
@@ -103,7 +119,7 @@
     action_runner.PlayMedia(playing_event_timeout_in_seconds=60,
                             ended_event_timeout_in_seconds=60)
     # Generate memory dump for memoryMetric.
-    if self.story_set.measure_memory:
+    if self.ShouldMeasureMemory():
       action_runner.MeasureMemory()
 
 
@@ -134,7 +150,7 @@
     action_runner.SeekMedia(seconds=9, timeout_in_seconds=timeout,
                             label='seek_cold')
     # Generate memory dump for memoryMetric.
-    if self.story_set.measure_memory:
+    if self.ShouldMeasureMemory():
       action_runner.MeasureMemory()
 
 
@@ -172,7 +188,7 @@
     new_tab.Close()
     action_runner.Wait(.5)
     # Generate memory dump for memoryMetric.
-    if self.story_set.measure_memory:
+    if self.ShouldMeasureMemory():
       action_runner.MeasureMemory()
 
 
@@ -204,6 +220,9 @@
     if test_failed:
       raise RuntimeError(action_runner.EvaluateJavaScript('window.__testError'))
 
+  def ShouldMeasureMemory(self):
+    return False
+
 
 def _GetDesktopOnlyMediaPages(page_set):
   return [
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index 80d591f..0799761 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -3321,10 +3321,60 @@
                                        0xDEADC0DEU);
   list_item2_text_data.SetName("list item 2");
 
+  ui::AXNodeData input_text_data;
+  input_text_data.id = 12;
+  input_text_data.role = ax::mojom::Role::kTextField;
+  input_text_data.AddState(ax::mojom::State::kEditable);
+  input_text_data.AddIntAttribute(
+      ax::mojom::IntAttribute::kNameFrom,
+      static_cast<int>(ax::mojom::NameFrom::kPlaceholder));
+  input_text_data.AddStringAttribute(ax::mojom::StringAttribute::kPlaceholder,
+                                     "placeholder2");
+  input_text_data.AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor,
+                                  0xDEADBEEFU);
+  input_text_data.AddIntAttribute(ax::mojom::IntAttribute::kColor, 0xDEADC0DEU);
+  input_text_data.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot,
+                                   true);
+  input_text_data.SetName("placeholder");
+  input_text_data.child_ids = {13};
+
+  ui::AXNodeData placeholder_text_data;
+  placeholder_text_data.id = 13;
+  placeholder_text_data.role = ax::mojom::Role::kStaticText;
+  placeholder_text_data.AddIntAttribute(
+      ax::mojom::IntAttribute::kBackgroundColor, 0xDEADBEEFU);
+  placeholder_text_data.AddIntAttribute(ax::mojom::IntAttribute::kColor,
+                                        0xDEADC0DEU);
+  placeholder_text_data.SetName("placeholder");
+
+  ui::AXNodeData input_text_data2;
+  input_text_data2.id = 14;
+  input_text_data2.role = ax::mojom::Role::kTextField;
+  input_text_data2.AddState(ax::mojom::State::kEditable);
+  input_text_data2.AddStringAttribute(ax::mojom::StringAttribute::kPlaceholder,
+                                      "placeholder2");
+  input_text_data2.AddIntAttribute(ax::mojom::IntAttribute::kBackgroundColor,
+                                   0xDEADBEEFU);
+  input_text_data2.AddIntAttribute(ax::mojom::IntAttribute::kColor,
+                                   0xDEADC0DEU);
+  input_text_data2.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot,
+                                    true);
+  input_text_data2.SetName("foo");
+  input_text_data2.child_ids = {15};
+
+  ui::AXNodeData placeholder_text_data2;
+  placeholder_text_data2.id = 15;
+  placeholder_text_data2.role = ax::mojom::Role::kStaticText;
+  placeholder_text_data2.AddIntAttribute(
+      ax::mojom::IntAttribute::kBackgroundColor, 0xDEADBEEFU);
+  placeholder_text_data2.AddIntAttribute(ax::mojom::IntAttribute::kColor,
+                                         0xDEADC0DEU);
+  placeholder_text_data2.SetName("placeholder2");
+
   ui::AXNodeData root_data;
   root_data.id = 1;
   root_data.role = ax::mojom::Role::kRootWebArea;
-  root_data.child_ids = {2, 3, 5, 7};
+  root_data.child_ids = {2, 3, 5, 7, 12, 14};
 
   ui::AXTreeUpdate update;
   ui::AXTreeData tree_data;
@@ -3343,6 +3393,10 @@
   update.nodes.push_back(list_item_text_data);
   update.nodes.push_back(list_item2_data);
   update.nodes.push_back(list_item2_text_data);
+  update.nodes.push_back(input_text_data);
+  update.nodes.push_back(placeholder_text_data);
+  update.nodes.push_back(input_text_data2);
+  update.nodes.push_back(placeholder_text_data2);
 
   Init(update);
   AXNodePosition::SetTree(tree_.get());
@@ -3359,6 +3413,10 @@
   AXNode* list_item_text_node = list_item_node->children()[0];
   AXNode* list_item2_node = list_node->children()[1];
   AXNode* list_item2_text_node = list_item2_node->children()[0];
+  AXNode* input_text_node = root_node->children()[4];
+  AXNode* placeholder_text_node = input_text_node->children()[0];
+  AXNode* input_text_node2 = root_node->children()[5];
+  AXNode* placeholder_text_node2 = input_text_node2->children()[0];
 
   ComPtr<ITextRangeProvider> document_range_provider;
   GetTextRangeProviderFromTextNode(document_range_provider, root_node);
@@ -3376,6 +3434,14 @@
   GetTextRangeProviderFromTextNode(list_item2_text_range_provider,
                                    list_item2_text_node);
 
+  ComPtr<ITextRangeProvider> placeholder_text_range_provider;
+  GetTextRangeProviderFromTextNode(placeholder_text_range_provider,
+                                   placeholder_text_node);
+
+  ComPtr<ITextRangeProvider> placeholder_text_range_provider2;
+  GetTextRangeProviderFromTextNode(placeholder_text_range_provider2,
+                                   placeholder_text_node2);
+
   base::win::ScopedVariant expected_variant;
 
   // SkColor is ARGB, COLORREF is 0BGR
@@ -3461,6 +3527,16 @@
                               UIA_IsReadOnlyAttributeId, expected_variant);
   expected_variant.Reset();
 
+  expected_variant.Set(false);
+  EXPECT_UIA_TEXTATTRIBUTE_EQ(placeholder_text_range_provider,
+                              UIA_IsReadOnlyAttributeId, expected_variant);
+  expected_variant.Reset();
+
+  expected_variant.Set(false);
+  EXPECT_UIA_TEXTATTRIBUTE_EQ(placeholder_text_range_provider2,
+                              UIA_IsReadOnlyAttributeId, expected_variant);
+  expected_variant.Reset();
+
   expected_variant.Set(true);
   EXPECT_UIA_TEXTATTRIBUTE_EQ(text_range_provider, UIA_IsSubscriptAttributeId,
                               expected_variant);
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 6f0f550..5d83fc07 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -4355,6 +4355,14 @@
                            : VARIANT_FALSE;
       break;
     case UIA_IsReadOnlyAttributeId:
+      // Placeholder text should return the enclosing element's read-only value.
+      if (IsPlaceholderText()) {
+        AXPlatformNodeWin* parent_platform_node =
+            static_cast<AXPlatformNodeWin*>(
+                FromNativeViewAccessible(GetParent()));
+        return parent_platform_node->GetTextAttributeValue(attribute_id,
+                                                           result);
+      }
       V_VT(result) = VT_BOOL;
       V_BOOL(result) =
           (GetData().GetRestriction() == ax::mojom::Restriction::kReadOnly ||
@@ -7109,6 +7117,17 @@
   return parent->IsAncestorComboBox();
 }
 
+bool AXPlatformNodeWin::IsPlaceholderText() const {
+  if (GetData().role != ax::mojom::Role::kStaticText)
+    return false;
+  AXPlatformNodeWin* parent =
+      static_cast<AXPlatformNodeWin*>(FromNativeViewAccessible(GetParent()));
+  // Static text nodes are always expected to have a parent.
+  DCHECK(parent);
+  return (parent->IsPlainTextField() || parent->IsRichTextField()) &&
+         parent->HasStringAttribute(ax::mojom::StringAttribute::kPlaceholder);
+}
+
 bool AXPlatformNodeWin::IsHyperlink() {
   int32_t hyperlink_index = -1;
   AXPlatformNodeWin* parent =
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h
index 92ce575..109ac745 100644
--- a/ui/accessibility/platform/ax_platform_node_win.h
+++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -1275,6 +1275,8 @@
 
   bool IsAncestorComboBox();
 
+  bool IsPlaceholderText() const;
+
   // Helper method for getting the horizontal scroll percent.
   double GetHorizontalScrollPercent();
 
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 797bf4b..c5d4da4 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -511,6 +511,14 @@
     deps += [ "//third_party/fontconfig" ]
   }
 
+  if (is_desktop_linux) {
+    sources += [
+      "cursor/cursor_theme_manager_linux.cc",
+      "cursor/cursor_theme_manager_linux.h",
+      "cursor/cursor_theme_manager_linux_observer.h",
+    ]
+  }
+
   if (use_glib) {
     configs += [ "//build/config/linux:glib" ]
     sources += [
diff --git a/ui/base/cursor/cursor_loader_x11.cc b/ui/base/cursor/cursor_loader_x11.cc
index e5c2df2..efc6351 100644
--- a/ui/base/cursor/cursor_loader_x11.cc
+++ b/ui/base/cursor/cursor_loader_x11.cc
@@ -81,7 +81,14 @@
 
 CursorLoaderX11::CursorLoaderX11()
     : display_(gfx::GetXDisplay()),
-      invisible_cursor_(CreateInvisibleCursor(), gfx::GetXDisplay()) {}
+      invisible_cursor_(CreateInvisibleCursor(), gfx::GetXDisplay()) {
+  auto* cursor_theme_manager = CursorThemeManagerLinux::GetInstance();
+  if (cursor_theme_manager) {
+    cursor_theme_observer_.Add(cursor_theme_manager);
+    OnCursorThemeNameChanged(cursor_theme_manager->GetCursorThemeName());
+    OnCursorThemeSizeChanged(cursor_theme_manager->GetCursorThemeSize());
+  }
+}
 
 CursorLoaderX11::~CursorLoaderX11() {
   UnloadAll();
@@ -152,14 +159,23 @@
   return test::GetCachedXcursorImage(image_cursors_[id]->cursor);
 }
 
+void CursorLoaderX11::OnCursorThemeNameChanged(
+    const std::string& cursor_theme_name) {
+  XcursorSetTheme(display_, cursor_theme_name.c_str());
+  ClearThemeCursors();
+}
+
+void CursorLoaderX11::OnCursorThemeSizeChanged(int cursor_theme_size) {
+  XcursorSetDefaultSize(display_, cursor_theme_size);
+  ClearThemeCursors();
+}
+
 bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor) {
   CursorType type = native_cursor.native_type();
   return image_cursors_.count(type) || animated_cursors_.count(type);
 }
 
 ::Cursor CursorLoaderX11::CursorFromId(CursorType id) {
-  const char* css_name = CursorCssNameFromId(id);
-
   auto font_it = font_cursors_.find(id);
   if (font_it != font_cursors_.end())
     return font_it->second;
@@ -174,6 +190,7 @@
   }
 
   // First try to load the cursor directly.
+  const char* css_name = CursorCssNameFromId(id);
   ::Cursor cursor = XcursorLibraryLoadCursor(display_, css_name);
   if (cursor == x11::None) {
     // Try a similar cursor supplied by the native cursor theme.
@@ -208,4 +225,9 @@
   return cursor;
 }
 
+void CursorLoaderX11::ClearThemeCursors() {
+  font_cursors_.clear();
+  image_cursors_.clear();
+}
+
 }  // namespace ui
diff --git a/ui/base/cursor/cursor_loader_x11.h b/ui/base/cursor/cursor_loader_x11.h
index 9fa7beaa..68f79ca 100644
--- a/ui/base/cursor/cursor_loader_x11.h
+++ b/ui/base/cursor/cursor_loader_x11.h
@@ -9,15 +9,19 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/scoped_observer.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/cursor/cursor_loader.h"
+#include "ui/base/cursor/cursor_theme_manager_linux.h"
+#include "ui/base/cursor/cursor_theme_manager_linux_observer.h"
 #include "ui/base/ui_base_export.h"
 #include "ui/base/x/x11_util.h"
 #include "ui/gfx/x/x11.h"
 
 namespace ui {
 
-class UI_BASE_EXPORT CursorLoaderX11 : public CursorLoader {
+class UI_BASE_EXPORT CursorLoaderX11 : public CursorLoader,
+                                       public CursorThemeManagerLinuxObserver {
  public:
   CursorLoaderX11();
   ~CursorLoaderX11() override;
@@ -35,6 +39,11 @@
 
   const XcursorImage* GetXcursorImageForTest(CursorType id);
 
+ protected:
+  // CursorThemeManagerLinux:
+  void OnCursorThemeNameChanged(const std::string& cursor_theme_name) override;
+  void OnCursorThemeSizeChanged(int cursor_theme_size) override;
+
  private:
   struct ImageCursor {
     ImageCursor(XcursorImage* x_image,
@@ -53,6 +62,8 @@
   // Loads a new cursor corresponding to |id|.
   ::Cursor CursorFromId(CursorType id);
 
+  void ClearThemeCursors();
+
   XDisplay* display_;
 
   // A map from a cursor native type to X cursor.
@@ -69,6 +80,9 @@
 
   const XScopedCursor invisible_cursor_;
 
+  ScopedObserver<CursorThemeManagerLinux, CursorThemeManagerLinuxObserver>
+      cursor_theme_observer_{this};
+
   DISALLOW_COPY_AND_ASSIGN(CursorLoaderX11);
 };
 
diff --git a/ui/base/cursor/cursor_theme_manager_linux.cc b/ui/base/cursor/cursor_theme_manager_linux.cc
new file mode 100644
index 0000000..fe8a97f
--- /dev/null
+++ b/ui/base/cursor/cursor_theme_manager_linux.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/cursor/cursor_theme_manager_linux.h"
+
+namespace ui {
+
+// static
+CursorThemeManagerLinux* CursorThemeManagerLinux::instance_ = nullptr;
+
+// static
+void CursorThemeManagerLinux::SetInstance(CursorThemeManagerLinux* instance) {
+  instance_ = instance;
+}
+
+// static
+CursorThemeManagerLinux* CursorThemeManagerLinux::GetInstance() {
+  return instance_;
+}
+
+CursorThemeManagerLinux::CursorThemeManagerLinux() = default;
+
+CursorThemeManagerLinux::~CursorThemeManagerLinux() = default;
+
+void CursorThemeManagerLinux::AddObserver(
+    CursorThemeManagerLinuxObserver* observer) {
+  cursor_theme_observers_.AddObserver(observer);
+}
+
+void CursorThemeManagerLinux::RemoveObserver(
+    CursorThemeManagerLinuxObserver* observer) {
+  cursor_theme_observers_.RemoveObserver(observer);
+}
+
+}  // namespace ui
diff --git a/ui/base/cursor/cursor_theme_manager_linux.h b/ui/base/cursor/cursor_theme_manager_linux.h
new file mode 100644
index 0000000..5304867
--- /dev/null
+++ b/ui/base/cursor/cursor_theme_manager_linux.h
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_CURSOR_CURSOR_THEME_MANAGER_LINUX_H_
+#define UI_BASE_CURSOR_CURSOR_THEME_MANAGER_LINUX_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "ui/base/cursor/cursor_theme_manager_linux_observer.h"
+#include "ui/base/ui_base_export.h"
+
+namespace ui {
+
+class UI_BASE_EXPORT CursorThemeManagerLinux {
+ public:
+  virtual ~CursorThemeManagerLinux();
+
+  static void SetInstance(CursorThemeManagerLinux* instance);
+
+  static CursorThemeManagerLinux* GetInstance();
+
+  virtual std::string GetCursorThemeName() = 0;
+  virtual int GetCursorThemeSize() = 0;
+  void AddObserver(CursorThemeManagerLinuxObserver* observer);
+  void RemoveObserver(CursorThemeManagerLinuxObserver* observer);
+
+ protected:
+  CursorThemeManagerLinux();
+
+  const base::ObserverList<CursorThemeManagerLinuxObserver>&
+  cursor_theme_observers() {
+    return cursor_theme_observers_;
+  }
+
+ private:
+  static CursorThemeManagerLinux* instance_;
+
+  base::ObserverList<CursorThemeManagerLinuxObserver> cursor_theme_observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(CursorThemeManagerLinux);
+};
+
+}  // namespace ui
+
+#endif  // UI_BASE_CURSOR_CURSOR_THEME_MANAGER_LINUX_H_
diff --git a/ui/base/cursor/cursor_theme_manager_linux_observer.h b/ui/base/cursor/cursor_theme_manager_linux_observer.h
new file mode 100644
index 0000000..3d0c822
--- /dev/null
+++ b/ui/base/cursor/cursor_theme_manager_linux_observer.h
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_CURSOR_CURSOR_THEME_MANAGER_LINUX_OBSERVER_H_
+#define UI_BASE_CURSOR_CURSOR_THEME_MANAGER_LINUX_OBSERVER_H_
+
+#include <string>
+
+#include "base/observer_list_types.h"
+#include "ui/base/ui_base_export.h"
+
+namespace ui {
+
+class UI_BASE_EXPORT CursorThemeManagerLinuxObserver
+    : public base::CheckedObserver {
+ public:
+  virtual void OnCursorThemeNameChanged(
+      const std::string& cursor_theme_name) = 0;
+  virtual void OnCursorThemeSizeChanged(int cursor_theme_size) = 0;
+
+ protected:
+  ~CursorThemeManagerLinuxObserver() override = default;
+};
+
+}  // namespace ui
+
+#endif  // UI_BASE_CURSOR_CURSOR_THEME_MANAGER_LINUX_OBSERVER_H_
diff --git a/ui/ozone/platform/headless/headless_screen.cc b/ui/ozone/platform/headless/headless_screen.cc
index 2e83e609..924a0085 100644
--- a/ui/ozone/platform/headless/headless_screen.cc
+++ b/ui/ozone/platform/headless/headless_screen.cc
@@ -6,18 +6,12 @@
 
 namespace ui {
 
-namespace {
-
-constexpr gfx::Size kDefaultWindowSize(800, 600);
-
-}  // namespace
-
 HeadlessScreen::HeadlessScreen() {
-  DCHECK_LE(next_display_id_, std::numeric_limits<int64_t>::max());
-
-  display::Display display(next_display_id_++);
-  display.SetScaleAndBounds(1.0f,
-                            gfx::Rect(gfx::Point(0, 0), kDefaultWindowSize));
+  static constexpr int64_t kHeadlessDisplayId = 1;
+  static constexpr float kHeadlessDisplayScale = 1.0f;
+  static constexpr gfx::Rect kHeadlessDisplayBounds(gfx::Size(1, 1));
+  display::Display display(kHeadlessDisplayId);
+  display.SetScaleAndBounds(kHeadlessDisplayScale, kHeadlessDisplayBounds);
   display_list_.AddDisplay(display, display::DisplayList::Type::PRIMARY);
 }
 
diff --git a/ui/ozone/platform/headless/headless_screen.h b/ui/ozone/platform/headless/headless_screen.h
index d6a3f11b..a7aa1d3 100644
--- a/ui/ozone/platform/headless/headless_screen.h
+++ b/ui/ozone/platform/headless/headless_screen.h
@@ -36,9 +36,6 @@
   void RemoveObserver(display::DisplayObserver* observer) override;
 
  private:
-  // The next available display id.
-  int64_t next_display_id_ = 0;
-
   display::DisplayList display_list_;
 
   base::ObserverList<display::DisplayObserver> observers_;
diff --git a/ui/views/layout/animating_layout_manager.cc b/ui/views/layout/animating_layout_manager.cc
index 7443c8a..72c4b4a 100644
--- a/ui/views/layout/animating_layout_manager.cc
+++ b/ui/views/layout/animating_layout_manager.cc
@@ -19,6 +19,19 @@
 
 namespace views {
 
+namespace {
+
+int GetMainAxis(LayoutOrientation orientation, const gfx::Size& size) {
+  switch (orientation) {
+    case LayoutOrientation::kHorizontal:
+      return size.width();
+    case LayoutOrientation::kVertical:
+      return size.height();
+  }
+}
+
+}  // anonymous namespace
+
 // Holds data about a view that is fading in or out as part of an animation.
 struct AnimatingLayoutManager::LayoutFadeInfo {
   // Whether the view is fading in or out.
@@ -215,9 +228,6 @@
 }
 
 void AnimatingLayoutManager::ResetLayout() {
-  if (animation_delegate_)
-    animation_delegate_->Reset();
-
   if (!target_layout_manager())
     return;
 
@@ -225,12 +235,31 @@
       should_animate_bounds_
           ? target_layout_manager()->GetPreferredSize(host_view())
           : host_view()->size();
+
+  ResetLayoutToSize(target_size);
+}
+
+void AnimatingLayoutManager::ResetLayoutToSize(const gfx::Size& target_size) {
+  if (animation_delegate_)
+    animation_delegate_->Reset();
+
   target_layout_ = target_layout_manager()->GetProposedLayout(target_size);
   current_layout_ = target_layout_;
   starting_layout_ = current_layout_;
   fade_infos_.clear();
   current_offset_ = 1.0;
   set_cached_layout_size(target_size);
+
+  if (is_animating_)
+    OnAnimationEnded();
+}
+
+void AnimatingLayoutManager::OnAnimationEnded() {
+  DCHECK(is_animating_);
+  is_animating_ = false;
+  RunDelayedActions();
+  DCHECK(!is_animating_) << "Queued actions should not change animation state.";
+  NotifyIsAnimatingChanged();
 }
 
 void AnimatingLayoutManager::FadeOut(View* child_view) {
@@ -289,7 +318,10 @@
     return gfx::Size();
   // TODO(dfried): consider cases where the minimum size might not be just the
   // minimum size of the embedded layout.
-  return target_layout_manager()->GetMinimumSize(host);
+  gfx::Size minimum_size = target_layout_manager()->GetMinimumSize(host);
+  if (should_animate_bounds_ && is_animating_)
+    minimum_size.SetToMin(current_layout_.host_size);
+  return minimum_size;
 }
 
 int AnimatingLayoutManager::GetPreferredHeightForWidth(const View* host,
@@ -307,21 +339,22 @@
   // Changing the size of a view directly will lead to a layout call rather
   // than an invalidation. This should reset the layout (but see the note in
   // RecalculateTarget() below).
-  if (!should_animate_bounds_ &&
-      (!cached_layout_size() || host->size() != *cached_layout_size())) {
+  if (should_animate_bounds_) {
+    const int host_main = GetMainAxis(orientation(), host->size());
+    const int desired_main =
+        GetMainAxis(orientation(), current_layout_.host_size);
+    if (desired_main > host_main)
+      ResetLayoutToSize(host->size());
+  } else if (!cached_layout_size() || host->size() != *cached_layout_size()) {
     ResetLayout();
   }
+
   ApplyLayout(current_layout_);
 
   // Send animating stopped events on layout so the current layout during the
   // event represents the final state instead of an intermediate state.
-  if (is_animating_ && current_offset_ == 1.0) {
-    is_animating_ = false;
-    RunDelayedActions();
-    DCHECK(!is_animating_)
-        << "Queued actions should not change animation state.";
-    NotifyIsAnimatingChanged();
-  }
+  if (is_animating_ && current_offset_ == 1.0)
+    OnAnimationEnded();
 }
 
 std::vector<View*> AnimatingLayoutManager::GetChildViewsInPaintOrder(
diff --git a/ui/views/layout/animating_layout_manager.h b/ui/views/layout/animating_layout_manager.h
index 013f524..c33e050 100644
--- a/ui/views/layout/animating_layout_manager.h
+++ b/ui/views/layout/animating_layout_manager.h
@@ -133,6 +133,14 @@
   // layout from the embedded layout manager.
   void ResetLayout();
 
+  // Does the work of ResetLayout() or FreezeLayout(), with the resulting layout
+  // snapped to |target_size|.
+  void ResetLayoutToSize(const gfx::Size& target_size);
+
+  // Cleans up after an animation, runs delayed actions, and sends
+  // notifications.
+  void OnAnimationEnded();
+
   // Causes the specified child view to fade out and become hidden. Alternative
   // to directly hiding the view (which will have the same effect, but could
   // cause visual flicker if the view paints before it can re-layout.
diff --git a/ui/views/layout/animating_layout_manager_unittest.cc b/ui/views/layout/animating_layout_manager_unittest.cc
index 3474830..4077546 100644
--- a/ui/views/layout/animating_layout_manager_unittest.cc
+++ b/ui/views/layout/animating_layout_manager_unittest.cc
@@ -22,6 +22,16 @@
 
 namespace {
 
+// Returns a size which is the intersection of |size| and the constraints
+// provided by |bounds|, if any.
+gfx::Size ConstrainSizeToBounds(const gfx::Size& size,
+                                const SizeBounds& bounds) {
+  return gfx::Size{
+      bounds.width() ? std::min(size.width(), *bounds.width()) : size.width(),
+      bounds.height() ? std::min(size.height(), *bounds.height())
+                      : size.height()};
+}
+
 // View that allows directly setting minimum size.
 class TestView : public View {
  public:
@@ -49,7 +59,10 @@
  protected:
   ProposedLayout CalculateProposedLayout(
       const SizeBounds& size_bounds) const override {
-    return layout_;
+    ProposedLayout actual;
+    actual.host_size = ConstrainSizeToBounds(layout_.host_size, size_bounds);
+    actual.child_layouts = layout_.child_layouts;
+    return actual;
   }
 
  private:
@@ -131,6 +144,12 @@
     }
   }
 
+  void SizeAndLayout() {
+    // If the layout of |view| is invalid or the size changes, this will
+    // automatically call |view->Layout()| as well.
+    view_->SizeToPreferredSize();
+  }
+
   virtual bool UseContainerTestApi() const { return true; }
 
  private:
@@ -149,7 +168,7 @@
   layout()->SetShouldAnimateBounds(true);
   layout()->SetTargetLayoutManager(std::move(test_layout));
 
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_EQ(layout1().host_size, view()->size());
   EnsureLayout(layout1());
@@ -162,7 +181,7 @@
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
 
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_EQ(layout1().host_size, view()->size());
   EnsureLayout(layout1());
@@ -174,11 +193,11 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   test_layout->SetLayout(layout2());
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
 
   // At this point animation should have started, but not proceeded.
   EXPECT_TRUE(layout()->is_animating());
@@ -193,15 +212,15 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   test_layout->SetLayout(layout2());
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   ProposedLayout expected = ProposedLayoutBetween(0.25, layout1(), layout2());
   EXPECT_TRUE(layout()->is_animating());
   EXPECT_EQ(expected.host_size, view()->size());
@@ -209,7 +228,7 @@
 
   // Advance again.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   expected = ProposedLayoutBetween(0.5, layout1(), layout2());
   EXPECT_TRUE(layout()->is_animating());
   EXPECT_EQ(expected.host_size, view()->size());
@@ -217,7 +236,7 @@
 
   // Advance to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   expected = layout2();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_EQ(expected.host_size, view()->size());
@@ -231,7 +250,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   // First layout. Should not be animating.
   view()->Layout();
@@ -253,7 +272,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   // First layout. Should not be animating.
   view()->Layout();
@@ -263,7 +282,7 @@
 
   // Because the size of the host view changed, there is no animation.
   test_layout->SetLayout(layout2());
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   view()->Layout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_EQ(layout2().host_size, view()->size());
@@ -277,7 +296,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   // First layout. Should not be animating.
   view()->Layout();
@@ -302,7 +321,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   // First layout. Should not be animating.
   view()->Layout();
@@ -355,7 +374,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   const ProposedLayout final_layout{{35, 20},
                                     {{child(0), true, {5, 5, 10, 10}},
@@ -365,14 +384,14 @@
   child(1)->SetMinimumSize({5, 5});
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   // No change to the layout yet.
   EnsureLayout(initial_layout);
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   ProposedLayout expected =
       ProposedLayoutBetween(0.25, initial_layout, final_layout);
   DCHECK_EQ(expected.child_layouts[1].child_view, child(1));
@@ -389,7 +408,7 @@
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   expected = ProposedLayoutBetween(0.5, initial_layout, final_layout);
   DCHECK_EQ(expected.child_layouts[1].child_view, child(1));
   expected.child_layouts[1].visible = true;
@@ -405,7 +424,7 @@
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   expected = ProposedLayoutBetween(0.75, initial_layout, final_layout);
   // At this point the layout is still animating but the middle view is below
   // zero in size so it will disappear.
@@ -415,7 +434,7 @@
 
   // Advance to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_EQ(final_layout.host_size, view()->size());
   EnsureLayout(final_layout);
@@ -435,7 +454,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   const ProposedLayout final_layout{{35, 20},
                                     {{child(0), true, {5, 5, 10, 10}},
@@ -445,14 +464,14 @@
   child(1)->SetMinimumSize({5, 5});
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   // No change to the layout yet.
   EnsureLayout(initial_layout);
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   ProposedLayout expected =
       ProposedLayoutBetween(0.25, initial_layout, final_layout);
   DCHECK_EQ(expected.child_layouts[1].child_view, child(1));
@@ -469,7 +488,7 @@
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   expected = ProposedLayoutBetween(0.5, initial_layout, final_layout);
   // At this point the layout is still animating but the middle view is below
   // its minimum size so it will disappear.
@@ -479,7 +498,7 @@
 
   // Advance to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_EQ(final_layout.host_size, view()->size());
   EnsureLayout(final_layout);
@@ -499,7 +518,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   const ProposedLayout final_layout{{35, 20},
                                     {{child(0), false},
@@ -509,14 +528,14 @@
   child(0)->SetMinimumSize({5, 5});
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   // No change to the layout yet.
   EnsureLayout(initial_layout);
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   ProposedLayout expected =
       ProposedLayoutBetween(0.25, initial_layout, final_layout);
   DCHECK_EQ(expected.child_layouts[0].child_view, child(0));
@@ -531,7 +550,7 @@
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   expected = ProposedLayoutBetween(0.5, initial_layout, final_layout);
   // At this point the layout is still animating but the middle view is below
   // its minimum size so it will disappear.
@@ -541,7 +560,7 @@
 
   // Advance to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_EQ(final_layout.host_size, view()->size());
   EnsureLayout(final_layout);
@@ -562,7 +581,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   const ProposedLayout final_layout{{50, 20},
                                     {{child(0), true, {5, 5, 10, 10}},
@@ -572,14 +591,14 @@
   child(2)->SetMinimumSize({5, 5});
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   // No change to the layout yet.
   EnsureLayout(initial_layout);
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   ProposedLayout expected =
       ProposedLayoutBetween(0.5, initial_layout, final_layout);
   // At this point the layout is still animating but the middle view is below
@@ -590,7 +609,7 @@
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   expected = ProposedLayoutBetween(0.75, initial_layout, final_layout);
   DCHECK_EQ(expected.child_layouts[2].child_view, child(2));
   expected.child_layouts[2].visible = true;
@@ -606,7 +625,7 @@
 
   // Advance to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_EQ(final_layout.host_size, view()->size());
   EnsureLayout(final_layout);
@@ -626,7 +645,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   const ProposedLayout final_layout{{35, 20},
                                     {{child(0), true, {5, 5, 10, 10}},
@@ -636,14 +655,14 @@
   child(2)->SetMinimumSize({5, 5});
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   // No change to the layout yet.
   EnsureLayout(initial_layout);
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   ProposedLayout expected =
       ProposedLayoutBetween(0.25, initial_layout, final_layout);
   DCHECK_EQ(expected.child_layouts[2].child_view, child(2));
@@ -660,7 +679,7 @@
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   expected = ProposedLayoutBetween(0.5, initial_layout, final_layout);
   // At this point the layout is still animating but the middle view is below
   // its minimum size so it will disappear.
@@ -670,7 +689,7 @@
 
   // Advance to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_EQ(final_layout.host_size, view()->size());
   EnsureLayout(final_layout);
@@ -690,7 +709,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   const ProposedLayout final_layout{{20, 35},
                                     {{child(0), true, {5, 5, 10, 10}},
@@ -700,7 +719,7 @@
   child(2)->SetMinimumSize({5, 5});
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   // No change to the layout yet.
   EnsureLayout(initial_layout);
@@ -723,7 +742,7 @@
 
   // Advance the animation.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(250));
-  view()->Layout();
+  SizeAndLayout();
   expected = ProposedLayoutBetween(0.5, initial_layout, final_layout);
   // At this point the layout is still animating but the middle view is below
   // its minimum size so it will disappear.
@@ -732,7 +751,7 @@
 
   // Advance to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EnsureLayout(final_layout);
 }
@@ -756,32 +775,32 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   ProposedLayout expected_layout = initial_layout;
   expected_layout.child_layouts[2] = {child(2), true, {20, 5, 10, 10}};
   EnsureLayout(expected_layout);
 
   // Advance the animation 20%.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
-  view()->Layout();
+  SizeAndLayout();
   expected_layout.host_size = {38, 20};
   expected_layout.child_layouts[2].bounds = {23, 5, 10, 10};
   EnsureLayout(expected_layout);
 
   // Advance the animation 60%.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(600));
-  view()->Layout();
+  SizeAndLayout();
   expected_layout.host_size = {47, 20};
   expected_layout.child_layouts[2].bounds = {32, 5, 10, 10};
   EnsureLayout(expected_layout);
 
   // Advance the animation to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
-  view()->Layout();
+  SizeAndLayout();
   EnsureLayout(final_layout);
 }
 
@@ -804,32 +823,32 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   ProposedLayout expected_layout = initial_layout;
   expected_layout.child_layouts[2] = {child(2), true, {5, 20, 10, 10}};
   EnsureLayout(expected_layout);
 
   // Advance the animation 20%.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
-  view()->Layout();
+  SizeAndLayout();
   expected_layout.host_size = {20, 38};
   expected_layout.child_layouts[2].bounds = {5, 23, 10, 10};
   EnsureLayout(expected_layout);
 
   // Advance the animation 60%.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(600));
-  view()->Layout();
+  SizeAndLayout();
   expected_layout.host_size = {20, 47};
   expected_layout.child_layouts[2].bounds = {5, 32, 10, 10};
   EnsureLayout(expected_layout);
 
   // Advance the animation to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
-  view()->Layout();
+  SizeAndLayout();
   EnsureLayout(final_layout);
 }
 
@@ -853,17 +872,17 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   ProposedLayout expected_layout = initial_layout;
   EnsureLayout(expected_layout);
 
   // Advance the animation 20%.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
-  view()->Layout();
+  SizeAndLayout();
   expected_layout.host_size = {47, 20};
   expected_layout.child_layouts[1].bounds = {18, 5, 5, 10};
   expected_layout.child_layouts[2].bounds = {32, 5, 10, 10};
@@ -871,7 +890,7 @@
 
   // Advance the animation 60%.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(600));
-  view()->Layout();
+  SizeAndLayout();
   expected_layout.host_size = {38, 20};
   expected_layout.child_layouts[1].bounds = {12, 5, 5, 10};
   expected_layout.child_layouts[2].bounds = {23, 5, 10, 10};
@@ -879,7 +898,7 @@
 
   // Advance the animation to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
-  view()->Layout();
+  SizeAndLayout();
   EnsureLayout(final_layout);
 }
 
@@ -903,17 +922,17 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   ProposedLayout expected_layout = initial_layout;
   EnsureLayout(expected_layout);
 
   // Advance the animation 20%.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
-  view()->Layout();
+  SizeAndLayout();
   expected_layout.host_size = {38, 20};
   expected_layout.child_layouts[1].bounds = {17, 5, 5, 10};
   expected_layout.child_layouts[2].bounds = {32, 5, 10, 10};
@@ -921,7 +940,7 @@
 
   // Advance the animation 60%.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(600));
-  view()->Layout();
+  SizeAndLayout();
   expected_layout.host_size = {47, 20};
   expected_layout.child_layouts[1].bounds = {8, 5, 5, 10};
   expected_layout.child_layouts[2].bounds = {23, 5, 10, 10};
@@ -929,7 +948,7 @@
 
   // Advance the animation to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
-  view()->Layout();
+  SizeAndLayout();
   EnsureLayout(final_layout);
 }
 
@@ -953,17 +972,17 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(initial_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   test_layout->SetLayout(final_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   ProposedLayout expected_layout = initial_layout;
   EnsureLayout(expected_layout);
 
   // Advance the animation 20%.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
-  view()->Layout();
+  SizeAndLayout();
   expected_layout.host_size = {47, 20};
   expected_layout.child_layouts[1].bounds = {20, 5, 5, 10};
   expected_layout.child_layouts[2].bounds = {32, 5, 10, 10};
@@ -971,7 +990,7 @@
 
   // Advance the animation 60%.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(600));
-  view()->Layout();
+  SizeAndLayout();
   expected_layout.host_size = {38, 20};
   expected_layout.child_layouts[1].bounds = {20, 5, 5, 10};
   expected_layout.child_layouts[2].bounds = {23, 5, 10, 10};
@@ -979,7 +998,7 @@
 
   // Advance the animation to completion.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
-  view()->Layout();
+  SizeAndLayout();
   EnsureLayout(final_layout);
 }
 
@@ -1012,7 +1031,7 @@
        {child(2), true, {{35, 5}, kChildViewSize}}}};
 
   // Set up the initial state of the host view and children.
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EnsureLayout(expected_start);
 
@@ -1242,7 +1261,7 @@
        {child(2), true, {{35, 5}, kChildViewSize}}}};
 
   // Set up the initial state of the host view and children.
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EnsureLayout(expected_start);
 
@@ -1302,7 +1321,7 @@
        {child(2), true, {{35, 5}, kChildViewSize}}}};
 
   // Set up the initial state of the host view and children.
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EnsureLayout(expected_start);
 
@@ -1356,7 +1375,7 @@
        {child(2), true, {{35, 5}, kChildViewSize}}}};
 
   // Set up the initial state of the host view and children.
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EnsureLayout(expected_start);
 
@@ -1407,7 +1426,7 @@
        {child(2), true, {{35, 5}, kChildViewSize}}}};
 
   // Set up the initial state of the host view and children.
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EnsureLayout(expected_start);
 
@@ -1458,7 +1477,7 @@
        {child(2), true, {{35, 5}, kChildViewSize}}}};
 
   // Set up the initial state of the host view and children.
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EnsureLayout(expected_start);
 
@@ -1515,7 +1534,7 @@
       {{child(0), false}, {child(1), true, {5, 5, 40, 10}}, {child(2), false}}};
 
   // Set up the initial state of the host view and children.
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EnsureLayout(expected_start);
 
@@ -1574,7 +1593,7 @@
       {{child(0), false}, {child(1), true, {5, 5, 40, 10}}, {child(2), false}}};
 
   // Set up the initial state of the host view and children.
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EnsureLayout(expected_start);
 
@@ -1631,7 +1650,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   EXPECT_FALSE(layout()->is_animating());
   EventWatcher watcher(layout());
@@ -1651,7 +1670,7 @@
   // Final layout clears the |is_animating| state because the views are now in
   // their final configuration.
   const std::vector<bool> expected2{true, false};
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_EQ(expected2, watcher.events());
 }
@@ -1669,7 +1688,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   EXPECT_FALSE(layout()->is_animating());
   test_layout->SetLayout(layout2());
@@ -1684,7 +1703,7 @@
 
   // Advance partially.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   EXPECT_FALSE(action1_called);
   EXPECT_FALSE(action2_called);
@@ -1697,7 +1716,7 @@
 
   // Final layout clears the |is_animating| state because the views are now in
   // their final configuration.
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_TRUE(action1_called);
   EXPECT_TRUE(action2_called);
@@ -1717,7 +1736,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   EXPECT_FALSE(layout()->is_animating());
   test_layout->SetLayout(layout2());
@@ -1732,7 +1751,7 @@
 
   // Advance partially.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(850));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   EXPECT_FALSE(action1_called);
   EXPECT_FALSE(action2_called);
@@ -1743,7 +1762,7 @@
 
   // Advance partially.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   EXPECT_FALSE(action1_called);
   EXPECT_FALSE(action2_called);
@@ -1756,7 +1775,7 @@
 
   // Final layout clears the |is_animating| state because the views are now in
   // their final configuration.
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_TRUE(action1_called);
   EXPECT_TRUE(action2_called);
@@ -1775,7 +1794,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   EXPECT_FALSE(layout()->is_animating());
   test_layout->SetLayout(layout2());
@@ -1790,7 +1809,7 @@
 
   // Advance partially.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   EXPECT_FALSE(action1_called);
   EXPECT_FALSE(action2_called);
@@ -1815,7 +1834,7 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   // Since the layout is not animating yet, this action runs immediately.
   EXPECT_FALSE(layout()->is_animating());
@@ -1835,7 +1854,7 @@
 
   // Advance partially.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_TRUE(layout()->is_animating());
   EXPECT_TRUE(action1_called);
   EXPECT_FALSE(action2_called);
@@ -1848,7 +1867,7 @@
 
   // Final layout clears the |is_animating| state because the views are now in
   // their final configuration.
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EXPECT_TRUE(action1_called);
   EXPECT_TRUE(action2_called);
@@ -1864,12 +1883,12 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(layout1());
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   // Start the animation.
   test_layout->SetLayout(layout2());
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
 
   // At this point animation should have started, but not proceeded.
   EXPECT_TRUE(layout()->is_animating());
@@ -1877,12 +1896,12 @@
 
   // Advance partially.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_EQ(view()->children(), view()->GetChildrenInZOrder());
 
   // Advance to end.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_EQ(view()->children(), view()->GetChildrenInZOrder());
 }
 
@@ -1906,22 +1925,22 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(starting_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   // Start the animation.
   test_layout->SetLayout(ending_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_EQ(expected_order, view()->GetChildrenInZOrder());
 
   // Advance partially.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_EQ(expected_order, view()->GetChildrenInZOrder());
 
   // Advance to end (restores Z order).
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_EQ(view()->children(), view()->GetChildrenInZOrder());
 }
 
@@ -1945,25 +1964,99 @@
       layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
   test_layout->SetLayout(starting_layout);
   layout()->ResetLayout();
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
 
   // Start the animation.
   test_layout->SetLayout(ending_layout);
   view()->InvalidateLayout();
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_EQ(expected_order, view()->GetChildrenInZOrder());
 
   // Advance partially.
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_EQ(expected_order, view()->GetChildrenInZOrder());
 
   // Advance to end (restores Z order).
   animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
-  view()->Layout();
+  SizeAndLayout();
   EXPECT_EQ(view()->children(), view()->GetChildrenInZOrder());
 }
 
+TEST_F(AnimatingLayoutManagerSteppingTest, ConstrainedSpace_StopsAnimation) {
+  layout()->SetShouldAnimateBounds(true);
+  auto* const test_layout =
+      layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
+  test_layout->SetLayout(layout1());
+  layout()->ResetLayout();
+  SizeAndLayout();
+
+  test_layout->SetLayout(layout2());
+  view()->InvalidateLayout();
+  SizeAndLayout();
+
+  // Advance the animation.
+  animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
+  // Layout 2 is 200 across. Halfway is 150. Getting less should halt the
+  // animation. Note that calling SetSize() should result in a Layout() call.
+  view()->SetSize({140, 200});
+  EXPECT_FALSE(layout()->is_animating());
+}
+
+TEST_F(AnimatingLayoutManagerSteppingTest,
+       ConstrainedSpace_TriggersDelayedAction) {
+  layout()->SetShouldAnimateBounds(true);
+  auto* const test_layout =
+      layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
+  test_layout->SetLayout(layout1());
+  layout()->ResetLayout();
+  SizeAndLayout();
+
+  test_layout->SetLayout(layout2());
+  view()->InvalidateLayout();
+  SizeAndLayout();
+
+  bool action_called = false;
+  auto action = base::BindOnce([](bool* var) { *var = true; }, &action_called);
+  layout()->QueueDelayedAction(std::move(action));
+
+  // Advance the animation.
+  animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
+  // Layout 2 is 200 across. Halfway is 150. Getting less should halt the
+  // animation. Note that calling SetSize() should result in a Layout() call.
+  view()->SetSize({140, 200});
+  EXPECT_TRUE(action_called);
+}
+
+TEST_F(AnimatingLayoutManagerSteppingTest,
+       ConstrainedSpace_SubsequentAnimation) {
+  layout()->SetShouldAnimateBounds(true);
+  auto* const test_layout =
+      layout()->SetTargetLayoutManager(std::make_unique<TestLayoutManager>());
+  test_layout->SetLayout(layout1());
+  layout()->ResetLayout();
+  SizeAndLayout();
+
+  test_layout->SetLayout(layout2());
+  view()->InvalidateLayout();
+  SizeAndLayout();
+
+  // Advance the animation.
+  animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(500));
+  // Layout 2 is 200 across. Halfway is 150. Getting less should halt the
+  // animation. Note that calling SetSize() should result in a Layout() call.
+  view()->SetSize({140, 200});
+
+  // This should attempt to restart the animation.
+  view()->InvalidateLayout();
+  EXPECT_TRUE(layout()->is_animating());
+
+  // And this should halt it again.
+  animation_api()->IncrementTime(base::TimeDelta::FromMilliseconds(200));
+  view()->Layout();
+  EXPECT_FALSE(layout()->is_animating());
+}
+
 namespace {
 
 constexpr base::TimeDelta kMinimumAnimationTime =
@@ -1973,8 +2066,11 @@
 // invalidated.
 class ImmediateLayoutManager : public LayoutManager {
  public:
-  explicit ImmediateLayoutManager(bool use_preferred_size)
-      : use_preferred_size_(use_preferred_size) {}
+  ImmediateLayoutManager(bool use_preferred_size,
+                         const SizeBounds& size_bounds = SizeBounds())
+      : use_preferred_size_(use_preferred_size), size_bounds_(size_bounds) {
+    DCHECK(use_preferred_size_ || size_bounds == SizeBounds());
+  }
 
   // LayoutManager:
 
@@ -1988,7 +2084,8 @@
     EXPECT_EQ(host_, view);
     for (View* child : host_->children()) {
       if (use_preferred_size_) {
-        const gfx::Size preferred = child->GetPreferredSize();
+        const gfx::Size preferred =
+            ConstrainSizeToBounds(child->GetPreferredSize(), size_bounds_);
         if (preferred != child->size()) {
           // This implicityly lays out the child view.
           child->SetSize(preferred);
@@ -2006,6 +2103,7 @@
 
  private:
   const bool use_preferred_size_;
+  const SizeBounds size_bounds_;
   View* host_ = nullptr;
 };
 
@@ -2067,9 +2165,9 @@
 
   bool UseContainerTestApi() const override { return false; }
 
-  void InitRootView() {
+  void InitRootView(const SizeBounds& bounds = SizeBounds()) {
     root_view_->SetLayoutManager(std::make_unique<ImmediateLayoutManager>(
-        layout()->should_animate_bounds()));
+        layout()->should_animate_bounds(), bounds));
     layout()->EnableAnimationForTesting();
   }
 
@@ -2145,7 +2243,7 @@
        {child(2), true, {{35, 5}, kChildViewSize}}}};
 
   // Set up the initial state of the host view and children.
-  view()->SetSize(view()->GetPreferredSize());
+  SizeAndLayout();
   EXPECT_FALSE(layout()->is_animating());
   EnsureLayout(expected_start);
 
@@ -2157,6 +2255,148 @@
   EnsureLayout(expected_end);
 }
 
+TEST_F(AnimatingLayoutManagerRealtimeTest, TestConstrainedSpaceStopsAnimation) {
+  constexpr gfx::Insets kChildMargins(5);
+  static const SizeBounds kSizeBounds(45, base::nullopt);
+  layout()->SetShouldAnimateBounds(true);
+  layout()->SetAnimationDuration(kMinimumAnimationTime);
+  auto* const flex_layout =
+      layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>());
+  flex_layout->SetOrientation(LayoutOrientation::kHorizontal);
+  flex_layout->SetCollapseMargins(true);
+  flex_layout->SetCrossAxisAlignment(LayoutAlignment::kStart);
+  flex_layout->SetDefault(kMarginsKey, kChildMargins);
+  InitRootView(kSizeBounds);
+  child(0)->SetProperty(kFlexBehaviorKey, FlexSpecification::ForSizeRule(
+                                              MinimumFlexSizeRule::kScaleToZero,
+                                              MaximumFlexSizeRule::kPreferred));
+  child(0)->SetVisible(false);
+  view()->InvalidateLayout();
+
+  const ProposedLayout starting_layout{
+      {35, 20},
+      {{child(1), true, {{5, 5}, kChildViewSize}},
+       {child(2), true, {{20, 5}, kChildViewSize}}}};
+
+  const ProposedLayout ending_layout{
+      {45, 20},
+      {{child(0), true, {{5, 5}, {5, 10}}},
+       {child(1), true, {{15, 5}, kChildViewSize}},
+       {child(2), true, {{30, 5}, kChildViewSize}}}};
+
+  // Set up the initial state of the host view and children.
+  EXPECT_FALSE(layout()->is_animating());
+  EXPECT_EQ(starting_layout.host_size, view()->size());
+  EnsureLayout(starting_layout);
+
+  child(0)->SetVisible(true);
+  EXPECT_TRUE(layout()->is_animating());
+
+  animation_watcher()->WaitForAnimationToComplete();
+  EXPECT_FALSE(layout()->is_animating());
+  EXPECT_EQ(ending_layout.host_size, view()->size());
+  EnsureLayout(ending_layout);
+}
+
+TEST_F(AnimatingLayoutManagerRealtimeTest,
+       TestConstrainedSpaceRestartedAnimationStops) {
+  constexpr gfx::Insets kChildMargins(5);
+  static const SizeBounds kSizeBounds(45, base::nullopt);
+  layout()->SetShouldAnimateBounds(true);
+  layout()->SetAnimationDuration(kMinimumAnimationTime);
+  auto* const flex_layout =
+      layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>());
+  flex_layout->SetOrientation(LayoutOrientation::kHorizontal);
+  flex_layout->SetCollapseMargins(true);
+  flex_layout->SetCrossAxisAlignment(LayoutAlignment::kStart);
+  flex_layout->SetDefault(kMarginsKey, kChildMargins);
+  InitRootView(kSizeBounds);
+  child(0)->SetProperty(kFlexBehaviorKey, FlexSpecification::ForSizeRule(
+                                              MinimumFlexSizeRule::kScaleToZero,
+                                              MaximumFlexSizeRule::kPreferred));
+  child(0)->SetVisible(false);
+  view()->InvalidateLayout();
+
+  const ProposedLayout starting_layout{
+      {35, 20},
+      {{child(1), true, {{5, 5}, kChildViewSize}},
+       {child(2), true, {{20, 5}, kChildViewSize}}}};
+
+  const ProposedLayout ending_layout{
+      {45, 20},
+      {{child(0), true, {{5, 5}, {5, 10}}},
+       {child(1), true, {{15, 5}, kChildViewSize}},
+       {child(2), true, {{30, 5}, kChildViewSize}}}};
+
+  // Set up the initial state of the host view and children.
+  EXPECT_FALSE(layout()->is_animating());
+  EXPECT_EQ(starting_layout.host_size, view()->size());
+  EnsureLayout(starting_layout);
+
+  // This should cause an animation that aborts when it hits the size bound.
+  child(0)->SetVisible(true);
+  animation_watcher()->WaitForAnimationToComplete();
+
+  // Invalidating the host causes an additional layout, but animation will stop
+  // immediately.
+  view()->InvalidateLayout();
+  EXPECT_TRUE(layout()->is_animating());
+  animation_watcher()->WaitForAnimationToComplete();
+  EXPECT_FALSE(layout()->is_animating());
+  EXPECT_EQ(ending_layout.host_size, view()->size());
+  EnsureLayout(ending_layout);
+}
+
+TEST_F(AnimatingLayoutManagerRealtimeTest,
+       TestConstrainedSpaceRestartedAnimationSucceeds) {
+  constexpr gfx::Insets kChildMargins(5);
+  static const SizeBounds kSizeBounds(45, base::nullopt);
+  layout()->SetShouldAnimateBounds(true);
+  layout()->SetAnimationDuration(kMinimumAnimationTime);
+  auto* const flex_layout =
+      layout()->SetTargetLayoutManager(std::make_unique<FlexLayout>());
+  flex_layout->SetOrientation(LayoutOrientation::kHorizontal);
+  flex_layout->SetCollapseMargins(true);
+  flex_layout->SetCrossAxisAlignment(LayoutAlignment::kStart);
+  flex_layout->SetDefault(kMarginsKey, kChildMargins);
+  InitRootView(kSizeBounds);
+  child(0)->SetProperty(kFlexBehaviorKey, FlexSpecification::ForSizeRule(
+                                              MinimumFlexSizeRule::kScaleToZero,
+                                              MaximumFlexSizeRule::kPreferred));
+  child(0)->SetVisible(false);
+  view()->InvalidateLayout();
+
+  const ProposedLayout starting_layout{
+      {35, 20},
+      {{child(1), true, {{5, 5}, kChildViewSize}},
+       {child(2), true, {{20, 5}, kChildViewSize}}}};
+
+  const ProposedLayout ending_layout{
+      {45, 20},
+      {{child(0), true, {{5, 5}, {5, 10}}},
+       {child(1), true, {{15, 5}, kChildViewSize}},
+       {child(2), true, {{30, 5}, kChildViewSize}}}};
+
+  // Set up the initial state of the host view and children.
+  EXPECT_FALSE(layout()->is_animating());
+  EXPECT_EQ(starting_layout.host_size, view()->size());
+  EnsureLayout(starting_layout);
+
+  // This should cause an animation that aborts when it hits the size bound.
+  child(0)->SetVisible(true);
+  animation_watcher()->WaitForAnimationToComplete();
+
+  // This will restart the animation, but since the target is smaller than the
+  // available space, the animation will proceed.
+  child(0)->SetVisible(false);
+  view()->InvalidateLayout();
+  EXPECT_TRUE(layout()->is_animating());
+  animation_watcher()->WaitForAnimationToComplete();
+  EXPECT_FALSE(layout()->is_animating());
+  EXPECT_EQ(starting_layout.host_size, view()->size());
+  EnsureLayout(starting_layout);
+}
+
 // TODO(dfried): figure out why these tests absolutely do not animate properly
 // on Mac. Whatever magic makes the compositor animation runner go doesn't seem
 // to want to work on Mac in non-browsertests :(
diff --git a/ui/views/linux_ui/linux_ui.cc b/ui/views/linux_ui/linux_ui.cc
index 0c4ab3a0..27ae4e29 100644
--- a/ui/views/linux_ui/linux_ui.cc
+++ b/ui/views/linux_ui/linux_ui.cc
@@ -28,10 +28,15 @@
   SkiaFontDelegate::SetInstance(instance);
   ShellDialogLinux::SetInstance(instance);
   ui::SetTextEditKeyBindingsDelegate(instance);
+  ui::CursorThemeManagerLinux::SetInstance(instance);
 }
 
 LinuxUI* LinuxUI::instance() {
   return g_linux_ui;
 }
 
+LinuxUI::LinuxUI() = default;
+
+LinuxUI::~LinuxUI() = default;
+
 }  // namespace views
diff --git a/ui/views/linux_ui/linux_ui.h b/ui/views/linux_ui/linux_ui.h
index 0e7b7ea..4b82de8b 100644
--- a/ui/views/linux_ui/linux_ui.h
+++ b/ui/views/linux_ui/linux_ui.h
@@ -8,8 +8,10 @@
 #include <string>
 
 #include "base/callback.h"
+#include "base/macros.h"
 #include "build/buildflag.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/cursor/cursor_theme_manager_linux.h"
 #include "ui/base/ime/linux/linux_input_method_context_factory.h"
 #include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h"
 #include "ui/gfx/skia_font_delegate.h"
@@ -59,8 +61,12 @@
 class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory,
                              public gfx::SkiaFontDelegate,
                              public ui::ShellDialogLinux,
-                             public ui::TextEditKeyBindingsDelegateAuraLinux {
+                             public ui::TextEditKeyBindingsDelegateAuraLinux,
+                             public ui::CursorThemeManagerLinux {
  public:
+  using NativeThemeGetter =
+      base::RepeatingCallback<ui::NativeTheme*(aura::Window* window)>;
+
   // Describes the window management actions that could be taken in response to
   // a middle click in the non client area.
   enum class WindowFrameAction {
@@ -78,10 +84,7 @@
     kRightClick,
   };
 
-  using NativeThemeGetter =
-      base::RepeatingCallback<ui::NativeTheme*(aura::Window* window)>;
-
-  ~LinuxUI() override {}
+  ~LinuxUI() override;
 
   // Sets the dynamically loaded singleton that draws the desktop native UI.
   static void SetInstance(LinuxUI* instance);
@@ -100,7 +103,7 @@
                         PrefService* pref_service) const = 0;
   virtual bool GetDisplayProperty(int id, int* result) const = 0;
 
-  // Returns the preferences that we pass to WebKit.
+  // Returns the preferences that we pass to Blink.
   virtual SkColor GetFocusRingColor() const = 0;
   virtual SkColor GetActiveSelectionBgColor() const = 0;
   virtual SkColor GetActiveSelectionFgColor() const = 0;
@@ -122,8 +125,8 @@
   // Returns the icon for a given content type from the icon theme.
   // TODO(davidben): Add an observer for the theme changing, so we can drop the
   // caches.
-  virtual gfx::Image GetIconForContentType(
-      const std::string& content_type, int size) const = 0;
+  virtual gfx::Image GetIconForContentType(const std::string& content_type,
+                                           int size) const = 0;
 
   // Builds a Border which paints the native button style.
   virtual std::unique_ptr<Border> CreateNativeBorder(
@@ -180,6 +183,12 @@
 
   // Returns a map of KeyboardEvent code to KeyboardEvent key values.
   virtual base::flat_map<std::string, std::string> GetKeyboardLayoutMap() = 0;
+
+ protected:
+  LinuxUI();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LinuxUI);
 };
 
 }  // namespace views
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 988b1d47..65d14b8 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -1623,9 +1623,14 @@
 }
 
 void View::PreferredSizeChanged() {
-  InvalidateLayout();
   if (parent_)
     parent_->ChildPreferredSizeChanged(this);
+  // Since some layout managers (specifically AnimatingLayoutManager) can react
+  // to InvalidateLayout() by doing calculations and since the parent can
+  // potentially change preferred size, etc. as a result of calling
+  // ChildPreferredSizeChanged(), postpone invalidation until the events have
+  // run all the way up the hierarchy.
+  InvalidateLayout();
   for (ViewObserver& observer : observers_)
     observer.OnViewPreferredSizeChanged(this);
 }
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.js b/ui/webui/resources/cr_components/chromeos/network/network_config.js
index e9cd084..7f3ac3a 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.js
@@ -93,8 +93,11 @@
       notify: true,
     },
 
-    /** @private {!chromeos.networkConfig.mojom.ManagedProperties|undefined} */
-    managedProperties_: Object,
+    /** @private {?chromeos.networkConfig.mojom.ManagedProperties} */
+    managedProperties_: {
+      type: Object,
+      value: null,
+    },
 
     /**
      * Managed EAP properties used for determination of managed EAP fields.
@@ -346,7 +349,7 @@
   init: function() {
     this.mojoType_ = undefined;
     this.vpnType_ = undefined;
-    this.managedProperties_ = undefined;
+    this.managedProperties_ = null;
     this.configProperties_ = undefined;
     this.propertiesSent_ = false;
     this.selectedServerCaHash_ = undefined;
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
index b203cb4..f4705ff 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -44,12 +44,14 @@
 
     private static class FileProviderHelper implements ContentUriUtils.FileProviderUtil {
         // Keep this variable in sync with the value defined in AndroidManifest.xml.
-        private static final String API_AUTHORITY = "org.chromium.weblayer.client.FileProvider";
+        private static final String API_AUTHORITY_SUFFIX =
+                ".org.chromium.weblayer.client.FileProvider";
 
         @Override
         public Uri getContentUriFromFile(File file) {
             Context appContext = ContextUtils.getApplicationContext();
-            return FileProvider.getUriForFile(appContext, API_AUTHORITY, file);
+            return FileProvider.getUriForFile(
+                    appContext, appContext.getPackageName() + API_AUTHORITY_SUFFIX, file);
         }
     }
 
diff --git a/weblayer/public/java/AndroidManifest.xml b/weblayer/public/java/AndroidManifest.xml
index ae3d86d..054f0d32 100644
--- a/weblayer/public/java/AndroidManifest.xml
+++ b/weblayer/public/java/AndroidManifest.xml
@@ -46,7 +46,7 @@
         {% endfor %}
 
         <provider android:name="android.support.v4.content.FileProvider"
-            android:authorities="org.chromium.weblayer.client.FileProvider"
+            android:authorities="${applicationId}.org.chromium.weblayer.client.FileProvider"
             android:exported="false"
             android:grantUriPermissions="true">
             <meta-data android:name="android.support.FILE_PROVIDER_PATHS"